Browse Source

Add tooearly/toolate hitsounds

kiwec 2 months ago
parent
commit
eeb36bfc0b

BIN
resources/materials/default/tooearly.wav


BIN
resources/materials/default/toolate.wav


+ 3 - 3
src/App/Osu/Beatmap.cpp

@@ -1796,8 +1796,8 @@ void Beatmap::handlePreviewPlay() {
                 m_bWasSeekFrame = true;
             } else {
                 m_music->setPositionMS_fast(m_selectedDifficulty2->getPreviewTime() < 0
-                                           ? (unsigned long)(m_music->getLengthMS() * 0.40f)
-                                           : m_selectedDifficulty2->getPreviewTime());
+                                                ? (unsigned long)(m_music->getLengthMS() * 0.40f)
+                                                : m_selectedDifficulty2->getPreviewTime());
                 m_bWasSeekFrame = true;
             }
 
@@ -3071,7 +3071,7 @@ void Beatmap::update2() {
 
         // all remaining clicks which have not been consumed by any hitobjects can safely be deleted
         if(m_clicks.size() > 0) {
-            if(osu_play_hitsound_on_click_while_playing.getBool()) osu->getSkin()->playHitCircleSound(0);
+            if(osu_play_hitsound_on_click_while_playing.getBool()) osu->getSkin()->playHitCircleSound(0, 0.f, 0);
 
             // nightmare mod: extra clicks = sliderbreak
             if((osu->getModNightmare() || osu_mod_jigsaw1.getBool()) && !m_bIsInSkippableSection && !m_bInBreak &&

+ 3 - 0
src/App/Osu/Changelog.cpp

@@ -30,6 +30,9 @@ Changelog::Changelog() : ScreenBackable() {
     latest.title =
         UString::format("%.2f (%s, %s)", convar->getConVarByName("osu_version")->getFloat(), __DATE__, __TIME__);
     latest.changes.push_back("- Added slider instafade setting");
+    latest.changes.push_back(
+        "- Added \"tooearly.wav\" and \"toolate.wav\" hitsounds, which play when you hit too early or too late (if "
+        "your skin has them)");
     latest.changes.push_back("- Fixed Nightcore getting auto-selected instead of Double Time in some cases");
     changelogs.push_back(latest);
 

+ 2 - 1
src/App/Osu/Circle.cpp

@@ -591,7 +591,8 @@ void Circle::onHit(LiveScore::HIT result, long delta, float targetDelta, float t
 
         const Vector2 osuCoords = m_beatmap->pixels2OsuCoords(m_beatmap->osuCoords2Pixels(m_vRawPos));
 
-        m_beatmap->getSkin()->playHitCircleSound(m_iSampleType, GameRules::osuCoords2Pan(osuCoords.x));
+        const long sound_delta = result == LiveScore::HIT::HIT_300 ? 0 : delta;
+        m_beatmap->getSkin()->playHitCircleSound(m_iSampleType, GameRules::osuCoords2Pan(osuCoords.x), sound_delta);
 
         m_fHitAnimation = 0.001f;  // quickfix for 1 frame missing images
         anim->moveQuadOut(&m_fHitAnimation, 1.0f, GameRules::getFadeOutTime(m_beatmap), true);

+ 9 - 2
src/App/Osu/Osu.cpp

@@ -2028,18 +2028,25 @@ void Osu::onSkinChange(UString oldValue, UString newValue) {
         if(newValue.length() < 1) return;
     }
 
+    if(newValue == UString("default")) {
+        m_skinScheduledToLoad = new Skin(newValue, MCENGINE_DATA_DIR "materials/default/", true);
+        if(m_skin == NULL) m_skin = m_skinScheduledToLoad;
+        m_bSkinLoadScheduled = true;
+        return;
+    }
+
     std::string neosuSkinFolder = MCENGINE_DATA_DIR "skins/";
     neosuSkinFolder.append(newValue.toUtf8());
     neosuSkinFolder.append("/");
     if(env->directoryExists(neosuSkinFolder)) {
-        m_skinScheduledToLoad = new Skin(newValue, neosuSkinFolder, (newValue == UString("default")));
+        m_skinScheduledToLoad = new Skin(newValue, neosuSkinFolder, false);
     } else {
         UString ppySkinFolder = m_osu_folder_ref->getString();
         ppySkinFolder.append(m_osu_folder_sub_skins_ref->getString());
         ppySkinFolder.append(newValue);
         ppySkinFolder.append("/");
         std::string sf = ppySkinFolder.toUtf8();
-        m_skinScheduledToLoad = new Skin(newValue, sf, (newValue == UString("default")));
+        m_skinScheduledToLoad = new Skin(newValue, sf, false);
     }
 
     // initial load

+ 29 - 18
src/App/Osu/Skin.cpp

@@ -2,8 +2,6 @@
 
 #include <string.h>
 
-#include "miniz.h"
-
 #include "Beatmap.h"
 #include "ConVar.h"
 #include "Engine.h"
@@ -17,6 +15,7 @@
 #include "SkinImage.h"
 #include "SoundEngine.h"
 #include "VolumeOverlay.h"
+#include "miniz.h"
 
 using namespace std;
 
@@ -55,7 +54,6 @@ ConVar osu_sound_panning_multiplier("osu_sound_panning_multiplier", 1.0f, FCVAR_
 ConVar osu_ignore_beatmap_combo_colors("osu_ignore_beatmap_combo_colors", false, FCVAR_DEFAULT);
 ConVar osu_ignore_beatmap_sample_volume("osu_ignore_beatmap_sample_volume", false, FCVAR_DEFAULT);
 
-const char *Skin::OSUSKIN_DEFAULT_SKIN_PATH = "";  // set dynamically below in the constructor
 Image *Skin::m_missingTexture = NULL;
 
 ConVar *Skin::m_osu_skin_async = &osu_skin_async;
@@ -64,10 +62,10 @@ ConVar *Skin::m_osu_skin_hd = &osu_skin_hd;
 ConVar *Skin::m_osu_skin_ref = NULL;
 ConVar *Skin::m_osu_mod_fposu_ref = NULL;
 
-void Skin::unpack(const char* filepath) {
+void Skin::unpack(const char *filepath) {
     auto skin_name = env->getFileNameFromFilePath(filepath);
     debugLog("Extracting %s...\n", skin_name.c_str());
-    skin_name.erase(skin_name.size() - 4); // remove .osk extension
+    skin_name.erase(skin_name.size() - 4);  // remove .osk extension
 
     auto skin_root = std::string(MCENGINE_DATA_DIR "skins/");
     skin_root.append(skin_name);
@@ -138,8 +136,6 @@ Skin::Skin(UString name, std::string filepath, bool isDefaultSkin) {
 
     if(m_missingTexture == NULL) m_missingTexture = engine->getResourceManager()->getImage("MISSING_TEXTURE");
 
-    OSUSKIN_DEFAULT_SKIN_PATH = "default/";
-
     // vars
     m_hitCircle = m_missingTexture;
     m_approachCircle = m_missingTexture;
@@ -285,6 +281,9 @@ Skin::Skin(UString name, std::string filepath, bool isDefaultSkin) {
     m_spinnerBonus = NULL;
     m_spinnerSpinSound = NULL;
 
+    m_tooearly = NULL;
+    m_toolate = NULL;
+
     m_combobreak = NULL;
     m_failsound = NULL;
     m_applause = NULL;
@@ -487,15 +486,12 @@ void Skin::load() {
     // skin ini
     randomizeFilePath();
     m_sSkinIniFilePath = m_sFilePath;
-    UString defaultSkinIniFilePath = MCENGINE_DATA_DIR "/materials/";
-    defaultSkinIniFilePath.append(OSUSKIN_DEFAULT_SKIN_PATH);
-    defaultSkinIniFilePath.append("skin.ini");
     m_sSkinIniFilePath.append("skin.ini");
     bool parseSkinIni1Status = true;
     bool parseSkinIni2Status = true;
     if(!parseSkinINI(m_sSkinIniFilePath)) {
         parseSkinIni1Status = false;
-        m_sSkinIniFilePath = defaultSkinIniFilePath;
+        m_sSkinIniFilePath = MCENGINE_DATA_DIR "materials/default/skin.ini";
         parseSkinIni2Status = parseSkinINI(m_sSkinIniFilePath);
     }
 
@@ -925,6 +921,9 @@ void Skin::load() {
     checkLoadSound(&m_spinnerBonus, "spinnerbonus", "OSU_SKIN_SPINNERBONUS_SND", true, true);
     checkLoadSound(&m_spinnerSpinSound, "spinnerspin", "OSU_SKIN_SPINNERSPIN_SND", false, true, true);
 
+    checkLoadSound(&m_tooearly, "tooearly", "OSU_SKIN_TOOEARLY_SND", true, true, false, false, 0.8f);
+    checkLoadSound(&m_toolate, "toolate", "OSU_SKIN_TOOLATE_SND", true, true, false, false, 0.85f);
+
     // others
     checkLoadSound(&m_combobreak, "combobreak", "OSU_SKIN_COMBOBREAK_SND", true, true);
     checkLoadSound(&m_failsound, "failsound", "OSU_SKIN_FAILSOUND_SND");
@@ -1286,7 +1285,7 @@ Color Skin::getComboColorForCounter(int i, int offset) {
 
 void Skin::setBeatmapComboColors(std::vector<Color> colors) { m_beatmapComboColors = colors; }
 
-void Skin::playHitCircleSound(int sampleType, float pan) {
+void Skin::playHitCircleSound(int sampleType, float pan, long delta) {
     if(m_iSampleVolume <= 0) {
         return;
     }
@@ -1298,6 +1297,18 @@ void Skin::playHitCircleSound(int sampleType, float pan) {
         pan *= osu_sound_panning_multiplier.getFloat();
     }
 
+    debugLog("delta: %d\n", delta);
+    if(delta < 0 && m_tooearly != NULL) {
+        debugLog("Too early!\n");
+        engine->getSound()->play(m_tooearly, pan);
+        return;
+    }
+    if(delta > 0 && m_toolate != NULL) {
+        debugLog("Too late!\n");
+        engine->getSound()->play(m_toolate, pan);
+        return;
+    }
+
     int actualSampleSet = m_iSampleSet;
     if(osu_skin_force_hitsound_sample_set.getInt() > 0) actualSampleSet = osu_skin_force_hitsound_sample_set.getInt();
 
@@ -1437,14 +1448,12 @@ void Skin::checkLoadImage(Image **addressOfPointer, std::string skinElementName,
     // NOTE: only the default skin is loaded with a resource name (it must never be unloaded by other instances), and it
     // is NOT added to the resources vector
 
-    std::string defaultFilePath1 = MCENGINE_DATA_DIR "/materials/";
-    defaultFilePath1.append(OSUSKIN_DEFAULT_SKIN_PATH);
+    std::string defaultFilePath1 = MCENGINE_DATA_DIR "materials/default/";
     defaultFilePath1.append(skinElementName);
     defaultFilePath1.append("@2x.");
     defaultFilePath1.append(fileExtension);
 
-    std::string defaultFilePath2 = MCENGINE_DATA_DIR "/materials/";
-    defaultFilePath2.append(OSUSKIN_DEFAULT_SKIN_PATH);
+    std::string defaultFilePath2 = MCENGINE_DATA_DIR "materials/default/";
     defaultFilePath2.append(skinElementName);
     defaultFilePath2.append(".");
     defaultFilePath2.append(fileExtension);
@@ -1580,6 +1589,7 @@ void Skin::checkLoadSound(Sound **addressOfPointer, std::string skinElementName,
 
             std::string path = base_path;
             path.append(fn);
+            debugLog("Loading %s\n", path.c_str());
 
             if(env->fileExists(path)) {
                 if(osu_skin_async.getBool()) {
@@ -1589,13 +1599,14 @@ void Skin::checkLoadSound(Sound **addressOfPointer, std::string skinElementName,
             }
         }
 
+        debugLog("Failed to load %s\n", filename.c_str());
+
         return (Sound *)NULL;
     };
 
     // load default skin
     if(fallback_to_default) {
-        std::string defaultpath = MCENGINE_DATA_DIR "./materials/";
-        defaultpath.append(OSUSKIN_DEFAULT_SKIN_PATH);
+        std::string defaultpath = MCENGINE_DATA_DIR "materials/default/";
         std::string defaultResourceName = resourceName;
         defaultResourceName.append("_DEFAULT");
         *addressOfPointer = try_load_sound(defaultpath, skinElementName, defaultResourceName, loop);

+ 5 - 3
src/App/Osu/Skin.h

@@ -10,8 +10,7 @@ class SkinImage;
 
 class Skin {
    public:
-    static const char *OSUSKIN_DEFAULT_SKIN_PATH;
-    static void unpack(const char* filepath);
+    static void unpack(const char *filepath);
 
     static ConVar *m_osu_skin_async;
     static ConVar *m_osu_skin_hd;
@@ -35,7 +34,7 @@ class Skin {
     void setSampleVolume(float volume, bool force = false);
     void resetSampleVolume();
 
-    void playHitCircleSound(int sampleType, float pan = 0.0f);
+    void playHitCircleSound(int sampleType, float pan = 0.0f, long delta = 0);
     void playSliderTickSound(float pan = 0.0f);
     void playSliderSlideSound(float pan = 0.0f);
     void playSpinnerSpinSound();
@@ -584,6 +583,9 @@ class Skin {
     Sound *m_spinnerBonus;
     Sound *m_spinnerSpinSound;
 
+    Sound *m_tooearly;
+    Sound *m_toolate;
+
     // Plays when sending a message in chat
     Sound *m_messageSent = NULL;
 

+ 2 - 4
src/App/Osu/SkinImage.cpp

@@ -100,13 +100,11 @@ bool SkinImage::loadImage(std::string skinElementName, bool ignoreDefaultSkin) {
     filepath2.append(skinElementName);
     filepath2.append(".png");
 
-    std::string defaultFilePath1 = "./materials/";
-    defaultFilePath1.append(Skin::OSUSKIN_DEFAULT_SKIN_PATH);
+    std::string defaultFilePath1 = MCENGINE_DATA_DIR "materials/default/";
     defaultFilePath1.append(skinElementName);
     defaultFilePath1.append("@2x.png");
 
-    std::string defaultFilePath2 = "./materials/";
-    defaultFilePath2.append(Skin::OSUSKIN_DEFAULT_SKIN_PATH);
+    std::string defaultFilePath2 = MCENGINE_DATA_DIR "materials/default/";
     defaultFilePath2.append(skinElementName);
     defaultFilePath2.append(".png");
 

+ 3 - 2
src/App/Osu/Slider.cpp

@@ -1124,10 +1124,11 @@ void Slider::onHit(LiveScore::HIT result, long delta, bool startOrEnd, float tar
 
             const Vector2 osuCoords = m_beatmap->pixels2OsuCoords(m_beatmap->osuCoords2Pixels(m_vCurPointRaw));
 
+            const long sound_delta = result == LiveScore::HIT::HIT_300 ? 0 : delta;
             m_beatmap->getSkin()->playHitCircleSound(m_iCurRepeatCounterForHitSounds < m_hitSounds.size()
                                                          ? m_hitSounds[m_iCurRepeatCounterForHitSounds]
                                                          : m_iSampleType,
-                                                     GameRules::osuCoords2Pan(osuCoords.x));
+                                                     GameRules::osuCoords2Pan(osuCoords.x), sound_delta);
 
             if(!startOrEnd) {
                 m_fStartHitAnimation = 0.001f;  // quickfix for 1 frame missing images
@@ -1246,7 +1247,7 @@ void Slider::onRepeatHit(bool successful, bool sliderend) {
         m_beatmap->getSkin()->playHitCircleSound(m_iCurRepeatCounterForHitSounds < m_hitSounds.size()
                                                      ? m_hitSounds[m_iCurRepeatCounterForHitSounds]
                                                      : m_iSampleType,
-                                                 GameRules::osuCoords2Pan(osuCoords.x));
+                                                 GameRules::osuCoords2Pan(osuCoords.x), 0);
 
         float animation_multiplier = osu->getSpeedMultiplier() / osu->getAnimationSpeedMultiplier();
         float tick_pulse_time = GameRules::osu_slider_followcircle_tick_pulse_time.getFloat() * animation_multiplier;

+ 1 - 1
src/App/Osu/Spinner.cpp

@@ -428,7 +428,7 @@ void Spinner::onHit() {
 
         const Vector2 osuCoords = m_beatmap->pixels2OsuCoords(m_beatmap->osuCoords2Pixels(m_vRawPos));
 
-        m_beatmap->getSkin()->playHitCircleSound(m_iSampleType, GameRules::osuCoords2Pan(osuCoords.x));
+        m_beatmap->getSkin()->playHitCircleSound(m_iSampleType, GameRules::osuCoords2Pan(osuCoords.x), 0);
     }
 
     // add it, and we are finished