Added initial sound effect support.
- Added SoundController module, which uses Java Sound since OpenAL has a slight delay on playing sounds. - Uploaded all effect WAVs. (credits: WWWskin, AL's IA, Fantasy's Skin, Minimalist Miku) - Added a missing entry in credits. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
parent
60eaa42997
commit
0604a25822
|
@ -23,4 +23,5 @@ Projects
|
||||||
The following projects were referenced in creating opsu!:
|
The following projects were referenced in creating opsu!:
|
||||||
|
|
||||||
* "Wojtkosu" - Wojtek Kowaluk (https://osu.ppy.sh/forum/t/97260)
|
* "Wojtkosu" - Wojtek Kowaluk (https://osu.ppy.sh/forum/t/97260)
|
||||||
|
* "osu-parser" - nojhamster (https://github.com/nojhamster/osu-parser)
|
||||||
* "osu! web" - pictuga (https://github.com/pictuga/osu-web)
|
* "osu! web" - pictuga (https://github.com/pictuga/osu-web)
|
BIN
res/applause.wav
Normal file
BIN
res/applause.wav
Normal file
Binary file not shown.
BIN
res/combobreak.wav
Normal file
BIN
res/combobreak.wav
Normal file
Binary file not shown.
BIN
res/count1s.wav
Normal file
BIN
res/count1s.wav
Normal file
Binary file not shown.
BIN
res/count2s.wav
Normal file
BIN
res/count2s.wav
Normal file
Binary file not shown.
BIN
res/count3s.wav
Normal file
BIN
res/count3s.wav
Normal file
Binary file not shown.
BIN
res/drum-hitclap.wav
Normal file
BIN
res/drum-hitclap.wav
Normal file
Binary file not shown.
BIN
res/drum-hitfinish.wav
Normal file
BIN
res/drum-hitfinish.wav
Normal file
Binary file not shown.
BIN
res/drum-hitnormal.wav
Normal file
BIN
res/drum-hitnormal.wav
Normal file
Binary file not shown.
BIN
res/drum-hitwhistle.wav
Normal file
BIN
res/drum-hitwhistle.wav
Normal file
Binary file not shown.
BIN
res/drum-sliderslide.wav
Normal file
BIN
res/drum-sliderslide.wav
Normal file
Binary file not shown.
BIN
res/drum-slidertick.wav
Normal file
BIN
res/drum-slidertick.wav
Normal file
Binary file not shown.
BIN
res/drum-sliderwhistle.wav
Normal file
BIN
res/drum-sliderwhistle.wav
Normal file
Binary file not shown.
BIN
res/failsound.wav
Normal file
BIN
res/failsound.wav
Normal file
Binary file not shown.
BIN
res/gos.wav
Normal file
BIN
res/gos.wav
Normal file
Binary file not shown.
BIN
res/menuback.wav
Normal file
BIN
res/menuback.wav
Normal file
Binary file not shown.
BIN
res/menuclick.wav
Normal file
BIN
res/menuclick.wav
Normal file
Binary file not shown.
BIN
res/menuhit.wav
Normal file
BIN
res/menuhit.wav
Normal file
Binary file not shown.
BIN
res/normal-hitclap.wav
Normal file
BIN
res/normal-hitclap.wav
Normal file
Binary file not shown.
BIN
res/normal-hitfinish.wav
Normal file
BIN
res/normal-hitfinish.wav
Normal file
Binary file not shown.
BIN
res/normal-hitnormal.wav
Normal file
BIN
res/normal-hitnormal.wav
Normal file
Binary file not shown.
BIN
res/normal-hitwhistle.wav
Normal file
BIN
res/normal-hitwhistle.wav
Normal file
Binary file not shown.
BIN
res/normal-sliderslide.wav
Normal file
BIN
res/normal-sliderslide.wav
Normal file
Binary file not shown.
BIN
res/normal-slidertick.wav
Normal file
BIN
res/normal-slidertick.wav
Normal file
Binary file not shown.
BIN
res/normal-sliderwhistle.wav
Normal file
BIN
res/normal-sliderwhistle.wav
Normal file
Binary file not shown.
BIN
res/readys.wav
Normal file
BIN
res/readys.wav
Normal file
Binary file not shown.
BIN
res/sectionfail.wav
Normal file
BIN
res/sectionfail.wav
Normal file
Binary file not shown.
BIN
res/sectionpass.wav
Normal file
BIN
res/sectionpass.wav
Normal file
Binary file not shown.
BIN
res/shutter.wav
Normal file
BIN
res/shutter.wav
Normal file
Binary file not shown.
BIN
res/soft-hitclap.wav
Normal file
BIN
res/soft-hitclap.wav
Normal file
Binary file not shown.
BIN
res/soft-hitfinish.wav
Normal file
BIN
res/soft-hitfinish.wav
Normal file
Binary file not shown.
BIN
res/soft-hitnormal.wav
Normal file
BIN
res/soft-hitnormal.wav
Normal file
Binary file not shown.
BIN
res/soft-hitwhistle.wav
Normal file
BIN
res/soft-hitwhistle.wav
Normal file
Binary file not shown.
BIN
res/soft-sliderslide.wav
Normal file
BIN
res/soft-sliderslide.wav
Normal file
Binary file not shown.
BIN
res/soft-slidertick.wav
Normal file
BIN
res/soft-slidertick.wav
Normal file
Binary file not shown.
BIN
res/soft-sliderwhistle.wav
Normal file
BIN
res/soft-sliderwhistle.wav
Normal file
Binary file not shown.
BIN
res/spinner-osu.wav
Normal file
BIN
res/spinner-osu.wav
Normal file
Binary file not shown.
BIN
res/spinnerbonus.wav
Normal file
BIN
res/spinnerbonus.wav
Normal file
Binary file not shown.
BIN
res/spinnerspin.wav
Normal file
BIN
res/spinnerspin.wav
Normal file
Binary file not shown.
|
@ -717,29 +717,41 @@ public class GameScore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the combo streak to zero.
|
||||||
|
*/
|
||||||
|
private void resetComboStreak() {
|
||||||
|
if (combo >= 20)
|
||||||
|
SoundController.playSound(SoundController.SOUND_COMBOBREAK);
|
||||||
|
combo = 0;
|
||||||
|
if (Options.isModActive(Options.MOD_SUDDEN_DEATH))
|
||||||
|
health = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a slider tick result.
|
* Handles a slider tick result.
|
||||||
* @param time the tick start time
|
* @param time the tick start time
|
||||||
* @param result the hit result (HIT_* constants)
|
* @param result the hit result (HIT_* constants)
|
||||||
* @param x the x coordinate
|
* @param x the x coordinate
|
||||||
* @param y the y coordinate
|
* @param y the y coordinate
|
||||||
|
* @param hitSound the object's hit sound
|
||||||
*/
|
*/
|
||||||
public void sliderTickResult(int time, int result, float x, float y) {
|
public void sliderTickResult(int time, int result, float x, float y, byte hitSound) {
|
||||||
int hitValue = 0;
|
int hitValue = 0;
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case HIT_SLIDER30:
|
case HIT_SLIDER30:
|
||||||
hitValue = 30;
|
hitValue = 30;
|
||||||
incrementComboStreak();
|
incrementComboStreak();
|
||||||
changeHealth(1f);
|
changeHealth(1f);
|
||||||
|
SoundController.playHitSound(hitSound);
|
||||||
break;
|
break;
|
||||||
case HIT_SLIDER10:
|
case HIT_SLIDER10:
|
||||||
hitValue = 10;
|
hitValue = 10;
|
||||||
incrementComboStreak();
|
incrementComboStreak();
|
||||||
|
SoundController.playHitSound(SoundController.HIT_SLIDERTICK);
|
||||||
break;
|
break;
|
||||||
case HIT_MISS:
|
case HIT_MISS:
|
||||||
combo = 0;
|
resetComboStreak();
|
||||||
if (Options.isModActive(Options.MOD_SUDDEN_DEATH))
|
|
||||||
health = 0f;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
|
@ -760,8 +772,10 @@ public class GameScore {
|
||||||
* @param y the y coordinate
|
* @param y the y coordinate
|
||||||
* @param color the combo color
|
* @param color the combo color
|
||||||
* @param end true if this is the last hit object in the combo
|
* @param end true if this is the last hit object in the combo
|
||||||
|
* @param hitSound the object's hit sound
|
||||||
*/
|
*/
|
||||||
public void hitResult(int time, int result, float x, float y, Color color, boolean end) {
|
public void hitResult(int time, int result, float x, float y, Color color,
|
||||||
|
boolean end, byte hitSound) {
|
||||||
int hitValue = 0;
|
int hitValue = 0;
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case HIT_300:
|
case HIT_300:
|
||||||
|
@ -784,15 +798,15 @@ public class GameScore {
|
||||||
hitValue = 0;
|
hitValue = 0;
|
||||||
changeHealth(-10f);
|
changeHealth(-10f);
|
||||||
comboEnd |= 2;
|
comboEnd |= 2;
|
||||||
combo = 0;
|
resetComboStreak();
|
||||||
if (Options.isModActive(Options.MOD_SUDDEN_DEATH))
|
|
||||||
health = 0f;
|
|
||||||
objectCount++;
|
objectCount++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (hitValue > 0) {
|
if (hitValue > 0) {
|
||||||
|
SoundController.playHitSound(hitSound);
|
||||||
|
|
||||||
// game mod score multipliers
|
// game mod score multipliers
|
||||||
float modMultiplier = 1f;
|
float modMultiplier = 1f;
|
||||||
if (Options.isModActive(Options.MOD_NO_FAIL))
|
if (Options.isModActive(Options.MOD_NO_FAIL))
|
||||||
|
|
|
@ -143,6 +143,9 @@ public class Opsu extends StateBasedGame {
|
||||||
}
|
}
|
||||||
Options.TMP_DIR.deleteOnExit();
|
Options.TMP_DIR.deleteOnExit();
|
||||||
|
|
||||||
|
// load sounds
|
||||||
|
SoundController.init();
|
||||||
|
|
||||||
app.start();
|
app.start();
|
||||||
} catch (SlickException e) {
|
} catch (SlickException e) {
|
||||||
Log.error("Error while creating game container.", e);
|
Log.error("Error while creating game container.", e);
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class OsuFile implements Comparable<OsuFile> {
|
||||||
// public String audioHash = ""; // audio hash (deprecated)
|
// public String audioHash = ""; // audio hash (deprecated)
|
||||||
public int previewTime = -1; // start position of music preview (in ms)
|
public int previewTime = -1; // start position of music preview (in ms)
|
||||||
public byte countdown = 0; // countdown type (0:disabled, 1:normal, 2:half, 3:double)
|
public byte countdown = 0; // countdown type (0:disabled, 1:normal, 2:half, 3:double)
|
||||||
public String sampleSet = ""; // ? ("Normal", "Soft")
|
public String sampleSet = ""; // sound samples ("None", "Normal", "Soft")
|
||||||
public float stackLeniency = 0.7f; // how often closely placed hit objects will be stacked together
|
public float stackLeniency = 0.7f; // how often closely placed hit objects will be stacked together
|
||||||
public byte mode = 0; // game mode (0:osu!, 1:taiko, 2:catch the beat, 3:osu!mania)
|
public byte mode = 0; // game mode (0:osu!, 1:taiko, 2:catch the beat, 3:osu!mania)
|
||||||
public boolean letterboxInBreaks = false; // whether the letterbox (top/bottom black bars) appears during breaks
|
public boolean letterboxInBreaks = false; // whether the letterbox (top/bottom black bars) appears during breaks
|
||||||
|
|
|
@ -38,7 +38,6 @@ public class OsuHitObject {
|
||||||
SOUND_NORMAL = 0,
|
SOUND_NORMAL = 0,
|
||||||
SOUND_WHISTLE = 2,
|
SOUND_WHISTLE = 2,
|
||||||
SOUND_FINISH = 4,
|
SOUND_FINISH = 4,
|
||||||
SOUND_WHISTLEFINISH = 6,
|
|
||||||
SOUND_CLAP = 8;
|
SOUND_CLAP = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,8 +26,8 @@ public class OsuTimingPoint {
|
||||||
public float beatLength; // (non-inherited) ms per beat
|
public float beatLength; // (non-inherited) ms per beat
|
||||||
public int velocity = 0; // (inherited) slider multiplier = -100 / value
|
public int velocity = 0; // (inherited) slider multiplier = -100 / value
|
||||||
public int meter; // beats per measure
|
public int meter; // beats per measure
|
||||||
public byte sampleType; // sound samples (0:none, 1:normal, 2:soft)
|
public byte sampleType; // sound samples (0:none, 1:normal, 2:soft, 3:drum)
|
||||||
public byte sampleTypeCustom; // custom samples (0:default, 1:custom1, 2:custom2
|
public byte sampleTypeCustom; // custom samples (0:default, 1:custom1, 2:custom2)
|
||||||
public int sampleVolume; // volume of samples (0~100)
|
public int sampleVolume; // volume of samples (0~100)
|
||||||
public boolean inherited; // is this timing point inherited?
|
public boolean inherited; // is this timing point inherited?
|
||||||
public boolean kiai; // is Kiai Mode active?
|
public boolean kiai; // is Kiai Mode active?
|
||||||
|
|
285
src/itdelatrisu/opsu/SoundController.java
Normal file
285
src/itdelatrisu/opsu/SoundController.java
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.states.Options;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.Clip;
|
||||||
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
|
||||||
|
import org.newdawn.slick.util.Log;
|
||||||
|
import org.newdawn.slick.util.ResourceLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for all sound effects.
|
||||||
|
* Note: Uses Java Sound because OpenAL lags too much for accurate hit sounds.
|
||||||
|
*/
|
||||||
|
public class SoundController {
|
||||||
|
/**
|
||||||
|
* Sound effect constants.
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
SOUND_APPLAUSE = 0,
|
||||||
|
SOUND_COMBOBREAK = 1,
|
||||||
|
// SOUND_COUNT = , // ?
|
||||||
|
SOUND_COUNT1 = 2,
|
||||||
|
SOUND_COUNT2 = 3,
|
||||||
|
SOUND_COUNT3 = 4,
|
||||||
|
SOUND_FAIL = 5,
|
||||||
|
SOUND_GO = 6,
|
||||||
|
SOUND_MENUBACK = 7,
|
||||||
|
SOUND_MENUCLICK = 8,
|
||||||
|
SOUND_MENUHIT = 9,
|
||||||
|
SOUND_READY = 10,
|
||||||
|
SOUND_SECTIONFAIL = 11,
|
||||||
|
SOUND_SECTIONPASS = 12,
|
||||||
|
SOUND_SHUTTER = 13,
|
||||||
|
SOUND_SPINNERBONUS = 14,
|
||||||
|
SOUND_SPINNEROSU = 15,
|
||||||
|
SOUND_SPINNERSPIN = 16,
|
||||||
|
SOUND_MAX = 17; // not a sound
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sound effect names (indexed by SOUND_* constants).
|
||||||
|
*/
|
||||||
|
private static final String[] soundNames = {
|
||||||
|
"applause",
|
||||||
|
"combobreak",
|
||||||
|
// "count", // ?
|
||||||
|
"count1s",
|
||||||
|
"count2s",
|
||||||
|
"count3s",
|
||||||
|
"failsound",
|
||||||
|
"gos",
|
||||||
|
"menuback",
|
||||||
|
"menuclick",
|
||||||
|
"menuhit",
|
||||||
|
"readys",
|
||||||
|
"sectionfail",
|
||||||
|
"sectionpass",
|
||||||
|
"shutter",
|
||||||
|
"spinnerbonus",
|
||||||
|
"spinner-osu",
|
||||||
|
"spinnerspin",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sound effects (indexed by SOUND_* constants).
|
||||||
|
*/
|
||||||
|
private static Clip[] sounds = new Clip[SOUND_MAX];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sound sample sets.
|
||||||
|
*/
|
||||||
|
private static final String[] sampleSets = {
|
||||||
|
"normal",
|
||||||
|
"soft",
|
||||||
|
"drum",
|
||||||
|
// "taiko"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current sample set (index in sampleSet[] array).
|
||||||
|
*/
|
||||||
|
private static int sampleSetIndex = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit sound effects.
|
||||||
|
*/
|
||||||
|
public static final int
|
||||||
|
HIT_CLAP = 0,
|
||||||
|
HIT_FINISH = 1,
|
||||||
|
HIT_NORMAL = 2,
|
||||||
|
HIT_WHISTLE = 3,
|
||||||
|
HIT_SLIDERSLIDE = 4,
|
||||||
|
HIT_SLIDERTICK = 5,
|
||||||
|
HIT_SLIDERWHISTLE = 6,
|
||||||
|
HIT_MAX = 7; // not a sound
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit sound effect names (indexed by HIT_* constants).
|
||||||
|
*/
|
||||||
|
private static final String[] hitSoundNames = {
|
||||||
|
"hitclap",
|
||||||
|
"hitfinish",
|
||||||
|
"hitnormal",
|
||||||
|
"hitwhistle",
|
||||||
|
"sliderslide",
|
||||||
|
"slidertick",
|
||||||
|
"sliderwhistle"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hit sound effects (indexed by sampleSets[], HIT_* constants).
|
||||||
|
*/
|
||||||
|
private static Clip[][] hitSounds = new Clip[sampleSets.length][HIT_MAX];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sample volume multiplier, from timing points [0, 1].
|
||||||
|
*/
|
||||||
|
private static float sampleVolumeMultiplier = 1f;
|
||||||
|
|
||||||
|
// This class should not be instantiated.
|
||||||
|
private SoundController() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and returns a Clip from a resource.
|
||||||
|
* @param ref the resource name
|
||||||
|
* @return the loaded and opened clip
|
||||||
|
*/
|
||||||
|
private static Clip loadClip(String ref) {
|
||||||
|
try {
|
||||||
|
URL url = ResourceLoader.getResource(ref);
|
||||||
|
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
||||||
|
Clip clip = AudioSystem.getClip();
|
||||||
|
clip.open(audioIn);
|
||||||
|
return clip;
|
||||||
|
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
|
||||||
|
Log.error(String.format("Failed to load file '%s'.", ref), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all sound files.
|
||||||
|
*/
|
||||||
|
public static void init() {
|
||||||
|
// TODO: support MP3 sounds?
|
||||||
|
|
||||||
|
// menu and game sounds
|
||||||
|
for (int i = 0; i < SOUND_MAX; i++)
|
||||||
|
sounds[i] = loadClip(String.format("%s.wav", soundNames[i]));
|
||||||
|
|
||||||
|
// hit sounds
|
||||||
|
for (int i = 0; i < sampleSets.length; i++) {
|
||||||
|
for (int j = 0; j < HIT_MAX; j++)
|
||||||
|
hitSounds[i][j] = loadClip(String.format("%s-%s.wav", sampleSets[i], hitSoundNames[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sample set to use when playing hit sounds.
|
||||||
|
* @param sampleSet the sample set ("None", "Normal", "Soft", "Drum")
|
||||||
|
*/
|
||||||
|
public static void setSampleSet(String sampleSet) {
|
||||||
|
sampleSetIndex = -1;
|
||||||
|
for (int i = 0; i < sampleSets.length; i++) {
|
||||||
|
if (sampleSet.equalsIgnoreCase(sampleSets[i])) {
|
||||||
|
sampleSetIndex = i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sample set to use when playing hit sounds.
|
||||||
|
* @param sampleType the sample set (0:none, 1:normal, 2:soft, 3:drum)
|
||||||
|
*/
|
||||||
|
public static void setSampleSet(byte sampleType) {
|
||||||
|
if (sampleType >= 0 && sampleType <= 3)
|
||||||
|
sampleSetIndex = sampleType - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sample volume (modifies the global sample volume).
|
||||||
|
* @param volume the sample volume [0, 100]
|
||||||
|
*/
|
||||||
|
public static void setSampleVolume(int volume) {
|
||||||
|
if (volume >= 0 && volume <= 100)
|
||||||
|
sampleVolumeMultiplier = volume / 100f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a sound clip.
|
||||||
|
* @param clip the Clip to play
|
||||||
|
* @param volume the volume [0, 1]
|
||||||
|
*/
|
||||||
|
private static void playClip(Clip clip, float volume) {
|
||||||
|
if (volume > 0f) {
|
||||||
|
// stop clip if running
|
||||||
|
if (clip.isRunning()) {
|
||||||
|
clip.stop();
|
||||||
|
clip.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set volume
|
||||||
|
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
||||||
|
float dB = (float) (Math.log(volume) / Math.log(10.0) * 20.0);
|
||||||
|
gainControl.setValue(dB * sampleVolumeMultiplier);
|
||||||
|
|
||||||
|
// play clip
|
||||||
|
clip.setFramePosition(0);
|
||||||
|
clip.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a sound.
|
||||||
|
* @param sound the sound (SOUND_* constant)
|
||||||
|
*/
|
||||||
|
public static void playSound(int sound) {
|
||||||
|
if (sound < 0 || sound >= SOUND_MAX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
playClip(sounds[sound], Options.getEffectVolume());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays hit sound(s) using an OsuHitObject bitmask.
|
||||||
|
* @param hitSound the sound (bitmask)
|
||||||
|
*/
|
||||||
|
public static void playHitSound(byte hitSound) {
|
||||||
|
if (sampleSetIndex < 0 || hitSound < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float volume = Options.getEffectVolume();
|
||||||
|
if (volume == 0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// play all sounds
|
||||||
|
if (hitSound == OsuHitObject.SOUND_NORMAL)
|
||||||
|
playClip(hitSounds[sampleSetIndex][HIT_NORMAL], volume);
|
||||||
|
else {
|
||||||
|
if ((hitSound & OsuHitObject.SOUND_WHISTLE) > 0)
|
||||||
|
playClip(hitSounds[sampleSetIndex][HIT_WHISTLE], volume);
|
||||||
|
if ((hitSound & OsuHitObject.SOUND_FINISH) > 0)
|
||||||
|
playClip(hitSounds[sampleSetIndex][HIT_FINISH], volume);
|
||||||
|
if ((hitSound & OsuHitObject.SOUND_CLAP) > 0)
|
||||||
|
playClip(hitSounds[sampleSetIndex][HIT_CLAP], volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a hit sound.
|
||||||
|
* @param sound (HIT_* constant)
|
||||||
|
*/
|
||||||
|
public static void playHitSound(int sound) {
|
||||||
|
if (sampleSetIndex < 0 || sound < 0 || sound > HIT_MAX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
playClip(hitSounds[sampleSetIndex][sound], Options.getEffectVolume());
|
||||||
|
}
|
||||||
|
}
|
|
@ -172,7 +172,9 @@ public class Circle {
|
||||||
if (distance < circleRadius) {
|
if (distance < circleRadius) {
|
||||||
int result = hitResult(hitObject.time);
|
int result = hitResult(hitObject.time);
|
||||||
if (result > -1) {
|
if (result > -1) {
|
||||||
score.hitResult(hitObject.time, result, hitObject.x, hitObject.y, color, comboEnd);
|
score.hitResult(hitObject.time, result, hitObject.x, hitObject.y,
|
||||||
|
color, comboEnd, hitObject.hitSound
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,11 +194,11 @@ public class Circle {
|
||||||
if (overlap || trackPosition > hitObject.time + hitResultOffset[GameScore.HIT_50]) {
|
if (overlap || trackPosition > hitObject.time + hitResultOffset[GameScore.HIT_50]) {
|
||||||
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
|
if (isAutoMod) // "auto" mod: catch any missed notes due to lag
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_300,
|
score.hitResult(hitObject.time, GameScore.HIT_300,
|
||||||
hitObject.x, hitObject.y, color, comboEnd);
|
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
||||||
|
|
||||||
else // no more points can be scored, so send a miss
|
else // no more points can be scored, so send a miss
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_MISS,
|
score.hitResult(hitObject.time, GameScore.HIT_MISS,
|
||||||
hitObject.x, hitObject.y, null, comboEnd);
|
hitObject.x, hitObject.y, null, comboEnd, hitObject.hitSound);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +206,7 @@ public class Circle {
|
||||||
else if (isAutoMod) {
|
else if (isAutoMod) {
|
||||||
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
||||||
score.hitResult(hitObject.time, GameScore.HIT_300,
|
score.hitResult(hitObject.time, GameScore.HIT_300,
|
||||||
hitObject.x, hitObject.y, color, comboEnd);
|
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,10 +384,11 @@ public class Slider {
|
||||||
|
|
||||||
if (currentRepeats % 2 == 0) // last circle
|
if (currentRepeats % 2 == 0) // last circle
|
||||||
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
||||||
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex], color, comboEnd);
|
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex],
|
||||||
|
color, comboEnd, hitObject.hitSound);
|
||||||
else // first circle
|
else // first circle
|
||||||
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
score.hitResult(hitObject.time + (int) sliderTimeTotal, result,
|
||||||
hitObject.x, hitObject.y, color, comboEnd);
|
hitObject.x, hitObject.y, color, comboEnd, hitObject.hitSound);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -420,7 +421,8 @@ public class Slider {
|
||||||
|
|
||||||
if (result > -1) {
|
if (result > -1) {
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
score.sliderTickResult(hitObject.time, result, hitObject.x, hitObject.y);
|
score.sliderTickResult(hitObject.time, result,
|
||||||
|
hitObject.x, hitObject.y, hitObject.hitSound);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,9 +467,11 @@ public class Slider {
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
if (isAutoMod) { // "auto" mod: catch any missed notes due to lag
|
if (isAutoMod) { // "auto" mod: catch any missed notes due to lag
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30, hitObject.x, hitObject.y);
|
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30,
|
||||||
|
hitObject.x, hitObject.y, hitObject.hitSound);
|
||||||
} else
|
} else
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_MISS, hitObject.x, hitObject.y);
|
score.sliderTickResult(hitObject.time, GameScore.HIT_MISS,
|
||||||
|
hitObject.x, hitObject.y, hitObject.hitSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "auto" mod: send a perfect hit result
|
// "auto" mod: send a perfect hit result
|
||||||
|
@ -475,7 +479,8 @@ public class Slider {
|
||||||
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
if (Math.abs(trackPosition - hitObject.time) < hitResultOffset[GameScore.HIT_300]) {
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
sliderClicked = true;
|
sliderClicked = true;
|
||||||
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30, hitObject.x, hitObject.y);
|
score.sliderTickResult(hitObject.time, GameScore.HIT_SLIDER30,
|
||||||
|
hitObject.x, hitObject.y, hitObject.hitSound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,24 +545,26 @@ public class Slider {
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
if (currentRepeats % 2 > 0) // last circle
|
if (currentRepeats % 2 > 0) // last circle
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
||||||
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex]);
|
hitObject.sliderX[lastIndex], hitObject.sliderY[lastIndex],
|
||||||
|
hitObject.hitSound);
|
||||||
else // first circle
|
else // first circle
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER30,
|
||||||
c[0], c[1]);
|
c[0], c[1], hitObject.hitSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// held during new tick
|
// held during new tick
|
||||||
if (isNewTick) {
|
if (isNewTick) {
|
||||||
ticksHit++;
|
ticksHit++;
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER10, c[0], c[1]);
|
score.sliderTickResult(trackPosition, GameScore.HIT_SLIDER10,
|
||||||
|
c[0], c[1], (byte) -1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
followCircleActive = false;
|
followCircleActive = false;
|
||||||
|
|
||||||
if (isNewRepeat)
|
if (isNewRepeat)
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0);
|
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, hitObject.hitSound);
|
||||||
if (isNewTick)
|
if (isNewTick)
|
||||||
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0);
|
score.sliderTickResult(trackPosition, GameScore.HIT_MISS, 0, 0, (byte) -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -21,6 +21,7 @@ package itdelatrisu.opsu.objects;
|
||||||
import itdelatrisu.opsu.GameScore;
|
import itdelatrisu.opsu.GameScore;
|
||||||
import itdelatrisu.opsu.MusicController;
|
import itdelatrisu.opsu.MusicController;
|
||||||
import itdelatrisu.opsu.OsuHitObject;
|
import itdelatrisu.opsu.OsuHitObject;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
import itdelatrisu.opsu.states.Game;
|
import itdelatrisu.opsu.states.Game;
|
||||||
import itdelatrisu.opsu.states.Options;
|
import itdelatrisu.opsu.states.Options;
|
||||||
|
|
||||||
|
@ -163,16 +164,19 @@ public class Spinner {
|
||||||
int result;
|
int result;
|
||||||
float ratio = rotations / rotationsNeeded;
|
float ratio = rotations / rotationsNeeded;
|
||||||
if (ratio >= 1.0f ||
|
if (ratio >= 1.0f ||
|
||||||
Options.isModActive(Options.MOD_AUTO) || Options.isModActive(Options.MOD_SPUN_OUT))
|
Options.isModActive(Options.MOD_AUTO) ||
|
||||||
|
Options.isModActive(Options.MOD_SPUN_OUT)) {
|
||||||
result = GameScore.HIT_300;
|
result = GameScore.HIT_300;
|
||||||
else if (ratio >= 0.8f)
|
SoundController.playSound(SoundController.SOUND_SPINNEROSU);
|
||||||
|
} else if (ratio >= 0.8f)
|
||||||
result = GameScore.HIT_100;
|
result = GameScore.HIT_100;
|
||||||
else if (ratio >= 0.5f)
|
else if (ratio >= 0.5f)
|
||||||
result = GameScore.HIT_50;
|
result = GameScore.HIT_50;
|
||||||
else
|
else
|
||||||
result = GameScore.HIT_MISS;
|
result = GameScore.HIT_MISS;
|
||||||
|
|
||||||
score.hitResult(hitObject.endTime, result, width / 2, height / 2, Color.transparent, true);
|
score.hitResult(hitObject.endTime, result, width / 2, height / 2,
|
||||||
|
Color.transparent, true, (byte) -1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,10 +244,13 @@ public class Spinner {
|
||||||
|
|
||||||
// added one whole rotation...
|
// added one whole rotation...
|
||||||
if (Math.floor(newRotations) > rotations) {
|
if (Math.floor(newRotations) > rotations) {
|
||||||
if (newRotations > rotationsNeeded) // extra rotations
|
if (newRotations > rotationsNeeded) { // extra rotations
|
||||||
score.changeScore(1000);
|
score.changeScore(1000);
|
||||||
else
|
SoundController.playSound(SoundController.SOUND_SPINNERBONUS);
|
||||||
|
} else {
|
||||||
score.changeScore(100);
|
score.changeScore(100);
|
||||||
|
SoundController.playSound(SoundController.SOUND_SPINNERSPIN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rotations = newRotations;
|
rotations = newRotations;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import itdelatrisu.opsu.Opsu;
|
||||||
import itdelatrisu.opsu.OsuFile;
|
import itdelatrisu.opsu.OsuFile;
|
||||||
import itdelatrisu.opsu.OsuHitObject;
|
import itdelatrisu.opsu.OsuHitObject;
|
||||||
import itdelatrisu.opsu.OsuTimingPoint;
|
import itdelatrisu.opsu.OsuTimingPoint;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
import itdelatrisu.opsu.objects.Circle;
|
import itdelatrisu.opsu.objects.Circle;
|
||||||
import itdelatrisu.opsu.objects.Slider;
|
import itdelatrisu.opsu.objects.Slider;
|
||||||
import itdelatrisu.opsu.objects.Spinner;
|
import itdelatrisu.opsu.objects.Spinner;
|
||||||
|
@ -134,6 +135,11 @@ public class Game extends BasicGameState {
|
||||||
*/
|
*/
|
||||||
private int breakTime = 0;
|
private int breakTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the break sound has been played.
|
||||||
|
*/
|
||||||
|
private boolean breakSound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip button (displayed at song start, when necessary).
|
* Skip button (displayed at song start, when necessary).
|
||||||
*/
|
*/
|
||||||
|
@ -164,6 +170,13 @@ public class Game extends BasicGameState {
|
||||||
countdown2, // "1" text
|
countdown2, // "1" text
|
||||||
countdownGo; // "GO!" text
|
countdownGo; // "GO!" text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the countdown sound has been played.
|
||||||
|
*/
|
||||||
|
private boolean
|
||||||
|
countdownReadySound, countdown3Sound, countdown1Sound,
|
||||||
|
countdown2Sound, countdownGoSound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Glowing hit circle outline which must be clicked when returning from pause menu.
|
* Glowing hit circle outline which must be clicked when returning from pause menu.
|
||||||
*/
|
*/
|
||||||
|
@ -276,10 +289,19 @@ public class Game extends BasicGameState {
|
||||||
trackPosition - breakTime > 2000 &&
|
trackPosition - breakTime > 2000 &&
|
||||||
trackPosition - breakTime < 5000) {
|
trackPosition - breakTime < 5000) {
|
||||||
// show break start
|
// show break start
|
||||||
if (score.getHealth() >= 50)
|
if (score.getHealth() >= 50) {
|
||||||
breakStartPass.drawCentered(width / 2f, height / 2f);
|
breakStartPass.drawCentered(width / 2f, height / 2f);
|
||||||
else
|
if (!breakSound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_SECTIONPASS);
|
||||||
|
breakSound = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
breakStartFail.drawCentered(width / 2f, height / 2f);
|
breakStartFail.drawCentered(width / 2f, height / 2f);
|
||||||
|
if (!breakSound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_SECTIONFAIL);
|
||||||
|
breakSound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (breakLength >= 4000) {
|
} else if (breakLength >= 4000) {
|
||||||
// show break end (flash twice for 500ms)
|
// show break end (flash twice for 500ms)
|
||||||
int endTimeDiff = endTime - trackPosition;
|
int endTimeDiff = endTime - trackPosition;
|
||||||
|
@ -326,18 +348,41 @@ public class Game extends BasicGameState {
|
||||||
if (osu.countdown > 0) { // TODO: implement half/double rate settings
|
if (osu.countdown > 0) { // TODO: implement half/double rate settings
|
||||||
int timeDiff = osu.objects[0].time - trackPosition;
|
int timeDiff = osu.objects[0].time - trackPosition;
|
||||||
if (timeDiff >= 500 && timeDiff < 3000) {
|
if (timeDiff >= 500 && timeDiff < 3000) {
|
||||||
if (timeDiff >= 1500)
|
if (timeDiff >= 1500) {
|
||||||
countdownReady.drawCentered(width / 2, height / 2);
|
countdownReady.drawCentered(width / 2, height / 2);
|
||||||
|
if (!countdownReadySound) {
|
||||||
if (timeDiff < 2000)
|
SoundController.playSound(SoundController.SOUND_READY);
|
||||||
|
countdownReadySound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeDiff < 2000) {
|
||||||
countdown3.draw(0, 0);
|
countdown3.draw(0, 0);
|
||||||
if (timeDiff < 1500)
|
if (!countdown3Sound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_COUNT3);
|
||||||
|
countdown3Sound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeDiff < 1500) {
|
||||||
countdown2.draw(width - countdown2.getWidth(), 0);
|
countdown2.draw(width - countdown2.getWidth(), 0);
|
||||||
if (timeDiff < 1000)
|
if (!countdown2Sound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_COUNT2);
|
||||||
|
countdown2Sound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (timeDiff < 1000) {
|
||||||
countdown1.drawCentered(width / 2, height / 2);
|
countdown1.drawCentered(width / 2, height / 2);
|
||||||
|
if (!countdown1Sound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_COUNT1);
|
||||||
|
countdown1Sound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (timeDiff >= -500 && timeDiff < 500) {
|
} else if (timeDiff >= -500 && timeDiff < 500) {
|
||||||
countdownGo.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
|
countdownGo.setAlpha((timeDiff < 0) ? 1 - (timeDiff / -1000f) : 1);
|
||||||
countdownGo.drawCentered(width / 2, height / 2);
|
countdownGo.drawCentered(width / 2, height / 2);
|
||||||
|
if (!countdownGoSound) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_GO);
|
||||||
|
countdownGoSound = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,6 +477,8 @@ public class Game extends BasicGameState {
|
||||||
beatLengthBase = beatLength = timingPoint.beatLength;
|
beatLengthBase = beatLength = timingPoint.beatLength;
|
||||||
else
|
else
|
||||||
beatLength = beatLengthBase * (timingPoint.velocity / -100f);
|
beatLength = beatLengthBase * (timingPoint.velocity / -100f);
|
||||||
|
SoundController.setSampleSet(timingPoint.sampleType);
|
||||||
|
SoundController.setSampleVolume(timingPoint.sampleVolume);
|
||||||
timingPointIndex++;
|
timingPointIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,6 +503,7 @@ public class Game extends BasicGameState {
|
||||||
} else if (trackPosition >= breakValue) {
|
} else if (trackPosition >= breakValue) {
|
||||||
// start a break
|
// start a break
|
||||||
breakTime = breakValue;
|
breakTime = breakValue;
|
||||||
|
breakSound = false;
|
||||||
breakIndex++;
|
breakIndex++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -638,22 +686,33 @@ public class Game extends BasicGameState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset indexes
|
// reset data
|
||||||
MusicController.setPosition(0);
|
MusicController.setPosition(0);
|
||||||
MusicController.pause();
|
MusicController.pause();
|
||||||
score.clear();
|
score.clear();
|
||||||
objectIndex = 0;
|
objectIndex = 0;
|
||||||
breakIndex = 0;
|
breakIndex = 0;
|
||||||
breakTime = 0;
|
breakTime = 0;
|
||||||
|
breakSound = false;
|
||||||
timingPointIndex = 0;
|
timingPointIndex = 0;
|
||||||
pauseTime = -1;
|
pauseTime = -1;
|
||||||
pausedMouseX = -1;
|
pausedMouseX = -1;
|
||||||
pausedMouseY = -1;
|
pausedMouseY = -1;
|
||||||
|
countdownReadySound = false;
|
||||||
|
countdown3Sound = false;
|
||||||
|
countdown1Sound = false;
|
||||||
|
countdown2Sound = false;
|
||||||
|
countdownGoSound = false;
|
||||||
|
|
||||||
// load the first timingPoint
|
// load the first timingPoint
|
||||||
if (!osu.timingPoints.isEmpty() && osu.timingPoints.get(0).velocity >= 0) {
|
if (!osu.timingPoints.isEmpty()) {
|
||||||
beatLengthBase = beatLength = osu.timingPoints.get(0).beatLength;
|
OsuTimingPoint timingPoint = osu.timingPoints.get(0);
|
||||||
timingPointIndex++;
|
if (timingPoint.velocity >= 0) {
|
||||||
|
beatLengthBase = beatLength = timingPoint.beatLength;
|
||||||
|
SoundController.setSampleSet(timingPoint.sampleType);
|
||||||
|
SoundController.setSampleVolume(timingPoint.sampleVolume);
|
||||||
|
timingPointIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
leadInTime = osu.audioLeadIn + approachTime;
|
leadInTime = osu.audioLeadIn + approachTime;
|
||||||
|
@ -675,6 +734,7 @@ public class Game extends BasicGameState {
|
||||||
MusicController.resume();
|
MusicController.resume();
|
||||||
}
|
}
|
||||||
MusicController.setPosition(osu.objects[0].time - skipOffsetTime);
|
MusicController.setPosition(osu.objects[0].time - skipOffsetTime);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -21,6 +21,7 @@ package itdelatrisu.opsu.states;
|
||||||
import itdelatrisu.opsu.GUIMenuButton;
|
import itdelatrisu.opsu.GUIMenuButton;
|
||||||
import itdelatrisu.opsu.MusicController;
|
import itdelatrisu.opsu.MusicController;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
|
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.GameContainer;
|
import org.newdawn.slick.GameContainer;
|
||||||
|
@ -142,6 +143,7 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
if (Game.getRestart() == Game.RESTART_LOSE) {
|
if (Game.getRestart() == Game.RESTART_LOSE) {
|
||||||
MusicController.stop();
|
MusicController.stop();
|
||||||
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
} else
|
} else
|
||||||
unPause(Game.RESTART_FALSE);
|
unPause(Game.RESTART_FALSE);
|
||||||
|
@ -171,6 +173,7 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
} else if (backButton.contains(x, y)) {
|
} else if (backButton.contains(x, y)) {
|
||||||
MusicController.pause(); // lose state
|
MusicController.pause(); // lose state
|
||||||
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,9 +182,10 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
public void enter(GameContainer container, StateBasedGame game)
|
public void enter(GameContainer container, StateBasedGame game)
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
pauseStartTime = System.currentTimeMillis();
|
pauseStartTime = System.currentTimeMillis();
|
||||||
if (Game.getRestart() == Game.RESTART_LOSE)
|
if (Game.getRestart() == Game.RESTART_LOSE) {
|
||||||
MusicController.fadeOut(FADEOUT_TIME);
|
MusicController.fadeOut(FADEOUT_TIME);
|
||||||
else
|
SoundController.playSound(SoundController.SOUND_FAIL);
|
||||||
|
} else
|
||||||
MusicController.pause();
|
MusicController.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +193,10 @@ public class GamePauseMenu extends BasicGameState {
|
||||||
* Unpause and return to the Game state.
|
* Unpause and return to the Game state.
|
||||||
*/
|
*/
|
||||||
private void unPause(byte restart) {
|
private void unPause(byte restart) {
|
||||||
|
if (restart == Game.RESTART_MANUAL)
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
|
else
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
Game.setRestart(restart);
|
Game.setRestart(restart);
|
||||||
game.enterState(Opsu.STATE_GAME);
|
game.enterState(Opsu.STATE_GAME);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import itdelatrisu.opsu.GameScore;
|
||||||
import itdelatrisu.opsu.MusicController;
|
import itdelatrisu.opsu.MusicController;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
import itdelatrisu.opsu.OsuFile;
|
import itdelatrisu.opsu.OsuFile;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
|
|
||||||
import org.lwjgl.opengl.Display;
|
import org.lwjgl.opengl.Display;
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
|
@ -143,7 +144,9 @@ public class GameRanking extends BasicGameState {
|
||||||
public void keyPressed(int key, char c) {
|
public void keyPressed(int key, char c) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Input.KEY_ESCAPE:
|
case Input.KEY_ESCAPE:
|
||||||
|
MusicController.pause();
|
||||||
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
break;
|
break;
|
||||||
case Input.KEY_F12:
|
case Input.KEY_F12:
|
||||||
|
@ -162,12 +165,15 @@ public class GameRanking extends BasicGameState {
|
||||||
OsuFile osu = Game.getOsuFile();
|
OsuFile osu = Game.getOsuFile();
|
||||||
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString()));
|
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString()));
|
||||||
Game.setRestart(Game.RESTART_MANUAL);
|
Game.setRestart(Game.RESTART_MANUAL);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
} else if (exitButton.contains(x, y))
|
} else if (exitButton.contains(x, y)) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
else if (Options.getBackButton().contains(x, y)) {
|
} else if (Options.getBackButton().contains(x, y)) {
|
||||||
MusicController.stop();
|
MusicController.pause();
|
||||||
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
MusicController.playAt(Game.getOsuFile().previewTime, true);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,5 +182,6 @@ public class GameRanking extends BasicGameState {
|
||||||
public void enter(GameContainer container, StateBasedGame game)
|
public void enter(GameContainer container, StateBasedGame game)
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
Display.setTitle(game.getTitle());
|
Display.setTitle(game.getTitle());
|
||||||
|
SoundController.playSound(SoundController.SOUND_APPLAUSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import itdelatrisu.opsu.GUIMenuButton;
|
||||||
import itdelatrisu.opsu.MusicController;
|
import itdelatrisu.opsu.MusicController;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
import itdelatrisu.opsu.OsuGroupNode;
|
import itdelatrisu.opsu.OsuGroupNode;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -287,17 +288,22 @@ public class MainMenu extends BasicGameState {
|
||||||
logoTimer = 0;
|
logoTimer = 0;
|
||||||
playButton.getImage().setAlpha(0f);
|
playButton.getImage().setAlpha(0f);
|
||||||
exitButton.getImage().setAlpha(0f);
|
exitButton.getImage().setAlpha(0f);
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// other button actions (if visible)
|
// other button actions (if visible)
|
||||||
else if (logoClicked) {
|
else if (logoClicked) {
|
||||||
if (logo.contains(x, y))
|
if (logo.contains(x, y)) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
logoTimer = MOVE_DELAY;
|
logoTimer = MOVE_DELAY;
|
||||||
else if (playButton.contains(x, y))
|
} else if (playButton.contains(x, y)) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_SONGMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
else if (exitButton.contains(x, y))
|
} else if (exitButton.contains(x, y)) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
game.enterState(Opsu.STATE_MAINMENUEXIT);
|
game.enterState(Opsu.STATE_MAINMENUEXIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package itdelatrisu.opsu.states;
|
||||||
|
|
||||||
import itdelatrisu.opsu.GUIMenuButton;
|
import itdelatrisu.opsu.GUIMenuButton;
|
||||||
import itdelatrisu.opsu.Opsu;
|
import itdelatrisu.opsu.Opsu;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
@ -148,12 +149,13 @@ public class Options extends BasicGameState {
|
||||||
// OPTIONS_FULLSCREEN = ,
|
// OPTIONS_FULLSCREEN = ,
|
||||||
OPTIONS_TARGET_FPS = 1,
|
OPTIONS_TARGET_FPS = 1,
|
||||||
OPTIONS_MUSIC_VOLUME = 2,
|
OPTIONS_MUSIC_VOLUME = 2,
|
||||||
OPTIONS_MUSIC_OFFSET = 3,
|
OPTIONS_EFFECT_VOLUME = 3,
|
||||||
OPTIONS_SCREENSHOT_FORMAT = 4,
|
OPTIONS_MUSIC_OFFSET = 4,
|
||||||
OPTIONS_DISPLAY_FPS = 5,
|
OPTIONS_SCREENSHOT_FORMAT = 5,
|
||||||
OPTIONS_HIT_LIGHTING = 6,
|
OPTIONS_DISPLAY_FPS = 6,
|
||||||
OPTIONS_COMBO_BURSTS = 7,
|
OPTIONS_HIT_LIGHTING = 7,
|
||||||
OPTIONS_MAX = 8; // not an option
|
OPTIONS_COMBO_BURSTS = 8,
|
||||||
|
OPTIONS_MAX = 9; // not an option
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Screen resolutions.
|
* Screen resolutions.
|
||||||
|
@ -208,7 +210,12 @@ public class Options extends BasicGameState {
|
||||||
/**
|
/**
|
||||||
* Default music volume.
|
* Default music volume.
|
||||||
*/
|
*/
|
||||||
private static int musicVolume = 20;
|
private static int musicVolume = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default sound effect volume.
|
||||||
|
*/
|
||||||
|
private static int effectVolume = 20;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset time, in milliseconds, for music position-related elements.
|
* Offset time, in milliseconds, for music position-related elements.
|
||||||
|
@ -258,8 +265,8 @@ public class Options extends BasicGameState {
|
||||||
Options.game = game;
|
Options.game = game;
|
||||||
this.input = container.getInput();
|
this.input = container.getInput();
|
||||||
|
|
||||||
// game settings;
|
// game settings
|
||||||
container.setTargetFrameRate(60);
|
container.setTargetFrameRate(targetFPS[targetFPSindex]);
|
||||||
container.setMouseCursor("cursor.png", 16, 16);
|
container.setMouseCursor("cursor.png", 16, 16);
|
||||||
container.setMusicVolume(getMusicVolume());
|
container.setMusicVolume(getMusicVolume());
|
||||||
container.setShowFPS(false);
|
container.setShowFPS(false);
|
||||||
|
@ -375,6 +382,10 @@ public class Options extends BasicGameState {
|
||||||
String.format("%d%%", musicVolume),
|
String.format("%d%%", musicVolume),
|
||||||
"Global music volume."
|
"Global music volume."
|
||||||
);
|
);
|
||||||
|
this.drawOption(g, OPTIONS_EFFECT_VOLUME, "Effect Volume",
|
||||||
|
String.format("%d%%", effectVolume),
|
||||||
|
"Sound effect volume."
|
||||||
|
);
|
||||||
this.drawOption(g, OPTIONS_MUSIC_OFFSET, "Music Offset",
|
this.drawOption(g, OPTIONS_MUSIC_OFFSET, "Music Offset",
|
||||||
String.format("%dms", musicOffset),
|
String.format("%dms", musicOffset),
|
||||||
"Adjust this value if hit objects are out of sync."
|
"Adjust this value if hit objects are out of sync."
|
||||||
|
@ -509,6 +520,14 @@ public class Options extends BasicGameState {
|
||||||
container.setMusicVolume(getMusicVolume());
|
container.setMusicVolume(getMusicVolume());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isOptionClicked(OPTIONS_EFFECT_VOLUME, oldy)) {
|
||||||
|
effectVolume += diff;
|
||||||
|
if (effectVolume < 0)
|
||||||
|
effectVolume = 0;
|
||||||
|
else if (effectVolume > 100)
|
||||||
|
effectVolume = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isOptionClicked(OPTIONS_MUSIC_OFFSET, oldy)) {
|
if (isOptionClicked(OPTIONS_MUSIC_OFFSET, oldy)) {
|
||||||
musicOffset += diff;
|
musicOffset += diff;
|
||||||
if (musicOffset < -500)
|
if (musicOffset < -500)
|
||||||
|
@ -601,6 +620,12 @@ public class Options extends BasicGameState {
|
||||||
*/
|
*/
|
||||||
public static float getMusicVolume() { return musicVolume / 100f; }
|
public static float getMusicVolume() { return musicVolume / 100f; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default sound effect volume.
|
||||||
|
* @return the sound volume [0, 1]
|
||||||
|
*/
|
||||||
|
public static float getEffectVolume() { return effectVolume / 100f; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the music offset time.
|
* Returns the music offset time.
|
||||||
* @return the offset (in milliseconds)
|
* @return the offset (in milliseconds)
|
||||||
|
@ -639,6 +664,8 @@ public class Options extends BasicGameState {
|
||||||
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
SimpleDateFormat date = new SimpleDateFormat("yyyyMMdd_HHmmss");
|
||||||
String file = date.format(new Date());
|
String file = date.format(new Date());
|
||||||
|
|
||||||
|
SoundController.playSound(SoundController.SOUND_SHUTTER);
|
||||||
|
|
||||||
// copy the screen
|
// copy the screen
|
||||||
Image screen = new Image(container.getWidth(), container.getHeight());
|
Image screen = new Image(container.getWidth(), container.getHeight());
|
||||||
container.getGraphics().copyArea(screen, 0, 0);
|
container.getGraphics().copyArea(screen, 0, 0);
|
||||||
|
@ -760,6 +787,11 @@ public class Options extends BasicGameState {
|
||||||
if (i >= 0 && i <= 100)
|
if (i >= 0 && i <= 100)
|
||||||
musicVolume = i;
|
musicVolume = i;
|
||||||
break;
|
break;
|
||||||
|
case "VolumeEffect":
|
||||||
|
i = Integer.parseInt(value);
|
||||||
|
if (i >= 0 && i <= 100)
|
||||||
|
effectVolume = i;
|
||||||
|
break;
|
||||||
case "Offset":
|
case "Offset":
|
||||||
i = Integer.parseInt(value);
|
i = Integer.parseInt(value);
|
||||||
if (i >= -500 && i <= 500)
|
if (i >= -500 && i <= 500)
|
||||||
|
@ -827,6 +859,8 @@ public class Options extends BasicGameState {
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
writer.write(String.format("VolumeMusic = %d", musicVolume));
|
writer.write(String.format("VolumeMusic = %d", musicVolume));
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
|
writer.write(String.format("VolumeEffect = %d", effectVolume));
|
||||||
|
writer.newLine();
|
||||||
writer.write(String.format("Offset = %d", musicOffset));
|
writer.write(String.format("Offset = %d", musicOffset));
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
writer.write(String.format("ScreenshotFormat = %d", screenshotFormatIndex));
|
writer.write(String.format("ScreenshotFormat = %d", screenshotFormatIndex));
|
||||||
|
|
|
@ -25,6 +25,7 @@ import itdelatrisu.opsu.OsuFile;
|
||||||
import itdelatrisu.opsu.OsuGroupList;
|
import itdelatrisu.opsu.OsuGroupList;
|
||||||
import itdelatrisu.opsu.OsuGroupNode;
|
import itdelatrisu.opsu.OsuGroupNode;
|
||||||
import itdelatrisu.opsu.OsuParser;
|
import itdelatrisu.opsu.OsuParser;
|
||||||
|
import itdelatrisu.opsu.SoundController;
|
||||||
|
|
||||||
import org.lwjgl.opengl.Display;
|
import org.lwjgl.opengl.Display;
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
|
@ -343,6 +344,7 @@ public class SongMenu extends BasicGameState {
|
||||||
|
|
||||||
// back
|
// back
|
||||||
if (Options.getBackButton().contains(x, y)) {
|
if (Options.getBackButton().contains(x, y)) {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -422,8 +424,10 @@ public class SongMenu extends BasicGameState {
|
||||||
if (!search.getText().isEmpty()) {
|
if (!search.getText().isEmpty()) {
|
||||||
search.setText("");
|
search.setText("");
|
||||||
searchTimer = SEARCH_DELAY;
|
searchTimer = SEARCH_DELAY;
|
||||||
} else
|
} else {
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUBACK);
|
||||||
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_MAINMENU, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Input.KEY_F1:
|
case Input.KEY_F1:
|
||||||
game.enterState(Opsu.STATE_OPTIONS, new EmptyTransition(), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_OPTIONS, new EmptyTransition(), new FadeInTransition(Color.black));
|
||||||
|
@ -592,8 +596,10 @@ public class SongMenu extends BasicGameState {
|
||||||
if (MusicController.isConverting())
|
if (MusicController.isConverting())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
SoundController.playSound(SoundController.SOUND_MENUHIT);
|
||||||
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString()));
|
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString()));
|
||||||
OsuParser.parseHitObjects(osu);
|
OsuParser.parseHitObjects(osu);
|
||||||
|
SoundController.setSampleSet(osu.sampleSet);
|
||||||
Game.setOsuFile(osu);
|
Game.setOsuFile(osu);
|
||||||
Game.setRestart(Game.RESTART_NEW);
|
Game.setRestart(Game.RESTART_NEW);
|
||||||
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user