diff --git a/.vscode/settings.json b/.vscode/settings.json index 4573e4a21c..bacff1eb9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,10 @@ { - "cmake.buildDirectory": "${workspaceFolder}/build/dusk/${buildType}/${variant:platform}/${variant:tp_version}", + "cmake.buildDirectory": "${workspaceFolder}/build/dusk/${buildType}/${variant:tp_version}", + "cmake.generator": "Ninja", + "cmake.configureSettings": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + }, + "cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json", "[c]": { "files.encoding": "utf8", "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd" @@ -44,8 +49,7 @@ "build.ninja": true, ".ninja_*": true, "objdiff.json": true, - ".cache/**": true, - "compile_commands.json": true, + ".cache/**": true }, "clangd.arguments": [ "--function-arg-placeholders=0", diff --git a/CMakeLists.txt b/CMakeLists.txt index d7308b0ba9..5074d121ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ set(AURORA_ENABLE_DVD ON CACHE BOOL "Enable DVD API support" FORCE) set(AURORA_ENABLE_CARD ON CACHE BOOL "Enable CARD API support" FORCE) add_subdirectory(extern/aurora EXCLUDE_FROM_ALL) +add_subdirectory(libs/freeverb) + option(DUSK_BUILD_WARNINGS "If off, compiler warnings will be suppressed") option(DUSK_SELECTED_OPT "If on, selected parts of the project will be compiled with optimizations on Debug, intending to make the game run at 30 FPS. Note for MSVC: you will need to remove '/RTC1' from your debug flags in CMake.") option(DUSK_MOVIE_SUPPORT "If on, compile against libjpeg-turbo to enable THP file decoding" ON) @@ -110,18 +112,19 @@ target_include_directories(game_debug PUBLIC src assets/${DUSK_TP_VERSION} libs/JSystem/include + libs extern/aurora/include/dolphin extern ${CMAKE_SOURCE_DIR}/build/${DUSK_TP_VERSION}/include build/${DUSK_TP_VERSION}/include) -target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card nlohmann_json::nlohmann_json) +target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card freeverb nlohmann_json::nlohmann_json) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES} src/dusk/imgui/ImGuiStubLog.cpp src/dusk/imgui/ImGuiAudio.cpp) -target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts absl::flat_hash_map) +target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts absl::flat_hash_map freeverb) if (DUSK_MOVIE_SUPPORT_REAL) if (TARGET libjpeg-turbo::turbojpeg-static) message(STATUS "dusk: Linking against libjpeg-turbo static library") @@ -138,7 +141,7 @@ target_precompile_headers(game PRIVATE "$<$:${CMAKE_SOURCE add_executable(dusk src/dusk/main.cpp) target_compile_definitions(dusk PRIVATE TARGET_PC AVOID_UB=1 VERSION=0) target_include_directories(dusk PRIVATE include) -target_link_libraries(dusk PRIVATE game aurora::main) +target_link_libraries(dusk PRIVATE game aurora::main freeverb) add_custom_command(TARGET dusk POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/cmake-variants.yaml b/cmake-variants.yaml index f9f6ad51bd..62d2642311 100644 --- a/cmake-variants.yaml +++ b/cmake-variants.yaml @@ -14,22 +14,6 @@ buildType: long: Optimized, with debug symbols buildType: RelWithDebInfo -platform: - default: win64 - description: Target platform - choices: - win32: - short: Win32 - long: Windows 32-bit (MSVC) - settings: - CMAKE_GENERATOR_PLATFORM: Win32 - - win64: - short: Win64 - long: Windows 64-bit (MSVC) - settings: - CMAKE_GENERATOR_PLATFORM: x64 - tp_version: default: GZ2E01 description: TP Version diff --git a/include/dusk/settings.h b/include/dusk/settings.h index d6a860939d..e3e3ff7e64 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -18,6 +18,7 @@ extern ConfigVar mainMusicVolume; extern ConfigVar subMusicVolume; extern ConfigVar soundEffectsVolume; extern ConfigVar fanfareVolume; +extern ConfigVar enableReverb; } namespace game { @@ -32,6 +33,7 @@ extern ConfigVar damageMultiplier; extern ConfigVar instantDeath; extern ConfigVar fastClimbing; extern ConfigVar fastTears; +extern ConfigVar noMissClimbing; // Preferences extern ConfigVar enableMirrorMode; @@ -41,8 +43,13 @@ extern ConfigVar invertCameraXAxis; extern ConfigVar enableBloom; extern ConfigVar useWaterProjectionOffset; +// Audio +extern ConfigVar noLowHpSound; +extern ConfigVar midnasLamentNonStop; + // Cheats extern ConfigVar enableFastIronBoots; +extern ConfigVar canTransformAnywhere; // Technical extern ConfigVar restoreWiiGlitches; @@ -75,4 +82,3 @@ TransientSettings& getTransientSettings(); } #endif // DUSK_CONFIG_H - diff --git a/libs/freeverb/CMakeLists.txt b/libs/freeverb/CMakeLists.txt new file mode 100644 index 0000000000..c0c12da293 --- /dev/null +++ b/libs/freeverb/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(freeverb LANGUAGES CXX) + +add_library(freeverb + comb.cpp + allpass.cpp + revmodel.cpp +) diff --git a/libs/freeverb/allpass.cpp b/libs/freeverb/allpass.cpp new file mode 100644 index 0000000000..5d80eda2bd --- /dev/null +++ b/libs/freeverb/allpass.cpp @@ -0,0 +1,36 @@ +// Allpass filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "allpass.hpp" + +allpass::allpass() +{ + bufidx = 0; +} + +void allpass::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void allpass::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif//_allpass + +//ends \ No newline at end of file diff --git a/libs/freeverb/comb.cpp b/libs/freeverb/comb.cpp new file mode 100644 index 0000000000..c05f5069c8 --- /dev/null +++ b/libs/freeverb/comb.cpp @@ -0,0 +1,48 @@ +// Comb filter implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "comb.hpp" + +comb::comb() +{ + filterstore = 0; + bufidx = 0; +} + +void comb::setbuffer(float *buf, int size) +{ + buffer = buf; + bufsize = size; +} + +void comb::mute() +{ + for (int i=0; i=bufsize) bufidx = 0; + + return output; +} + +#endif //_comb_ + +//ends diff --git a/libs/freeverb/denormals.h b/libs/freeverb/denormals.h new file mode 100644 index 0000000000..f871412714 --- /dev/null +++ b/libs/freeverb/denormals.h @@ -0,0 +1,15 @@ +// Macro for killing denormalled numbers +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// Based on IS_DENORMAL macro by Jon Watte +// This code is public domain + +#ifndef _denormals_ +#define _denormals_ + +#define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f + +#endif//_denormals_ + +//ends diff --git a/libs/freeverb/revmodel.cpp b/libs/freeverb/revmodel.cpp new file mode 100644 index 0000000000..feaa3bc04c --- /dev/null +++ b/libs/freeverb/revmodel.cpp @@ -0,0 +1,252 @@ +// Reverb model implementation +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#include "revmodel.hpp" + +revmodel::revmodel() +{ + // Tie the components to their buffers + combL[0].setbuffer(bufcombL1,combtuningL1); + combR[0].setbuffer(bufcombR1,combtuningR1); + combL[1].setbuffer(bufcombL2,combtuningL2); + combR[1].setbuffer(bufcombR2,combtuningR2); + combL[2].setbuffer(bufcombL3,combtuningL3); + combR[2].setbuffer(bufcombR3,combtuningR3); + combL[3].setbuffer(bufcombL4,combtuningL4); + combR[3].setbuffer(bufcombR4,combtuningR4); + combL[4].setbuffer(bufcombL5,combtuningL5); + combR[4].setbuffer(bufcombR5,combtuningR5); + combL[5].setbuffer(bufcombL6,combtuningL6); + combR[5].setbuffer(bufcombR6,combtuningR6); + combL[6].setbuffer(bufcombL7,combtuningL7); + combR[6].setbuffer(bufcombR7,combtuningR7); + combL[7].setbuffer(bufcombL8,combtuningL8); + combR[7].setbuffer(bufcombR8,combtuningR8); + allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); + allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); + allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); + allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); + allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); + allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); + allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); + allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); + + // Set default values + allpassL[0].setfeedback(0.5f); + allpassR[0].setfeedback(0.5f); + allpassL[1].setfeedback(0.5f); + allpassR[1].setfeedback(0.5f); + allpassL[2].setfeedback(0.5f); + allpassR[2].setfeedback(0.5f); + allpassL[3].setfeedback(0.5f); + allpassR[3].setfeedback(0.5f); + setwet(initialwet); + setroomsize(initialroom); + setdry(initialdry); + setdamp(initialdamp); + setwidth(initialwidth); + setmode(initialmode); + + // Buffer will be full of rubbish - so we MUST mute them + mute(); +} + +void revmodel::mute() +{ + if (getmode() >= freezemode) + return; + + for (int i=0;i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(int i=0; i 0) + { + outL = outR = 0; + input = (*inputL + *inputR) * gain; + + // Accumulate comb filters in parallel + for(int i=0; i= freezemode) + { + roomsize1 = 1; + damp1 = 0; + gain = muted; + } + else + { + roomsize1 = roomsize; + damp1 = damp; + gain = fixedgain; + } + + for(i=0; i= freezemode) + return 1; + else + return 0; +} + +//ends diff --git a/libs/freeverb/revmodel.hpp b/libs/freeverb/revmodel.hpp new file mode 100644 index 0000000000..10fe7c67d5 --- /dev/null +++ b/libs/freeverb/revmodel.hpp @@ -0,0 +1,87 @@ +// Reverb model declaration +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _revmodel_ +#define _revmodel_ + +#include "comb.hpp" +#include "allpass.hpp" +#include "tuning.h" + +class revmodel +{ +public: + revmodel(); + void mute(); + void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); + void setroomsize(float value); + float getroomsize(); + void setdamp(float value); + float getdamp(); + void setwet(float value); + float getwet(); + void setdry(float value); + float getdry(); + void setwidth(float value); + float getwidth(); + void setmode(float value); + float getmode(); +private: + void update(); +private: + float gain; + float roomsize,roomsize1; + float damp,damp1; + float wet,wet1,wet2; + float dry; + float width; + float mode; + + // The following are all declared inline + // to remove the need for dynamic allocation + // with its subsequent error-checking messiness + + // Comb filters + comb combL[numcombs]; + comb combR[numcombs]; + + // Allpass filters + allpass allpassL[numallpasses]; + allpass allpassR[numallpasses]; + + // Buffers for the combs + float bufcombL1[combtuningL1]; + float bufcombR1[combtuningR1]; + float bufcombL2[combtuningL2]; + float bufcombR2[combtuningR2]; + float bufcombL3[combtuningL3]; + float bufcombR3[combtuningR3]; + float bufcombL4[combtuningL4]; + float bufcombR4[combtuningR4]; + float bufcombL5[combtuningL5]; + float bufcombR5[combtuningR5]; + float bufcombL6[combtuningL6]; + float bufcombR6[combtuningR6]; + float bufcombL7[combtuningL7]; + float bufcombR7[combtuningR7]; + float bufcombL8[combtuningL8]; + float bufcombR8[combtuningR8]; + + // Buffers for the allpasses + float bufallpassL1[allpasstuningL1]; + float bufallpassR1[allpasstuningR1]; + float bufallpassL2[allpasstuningL2]; + float bufallpassR2[allpasstuningR2]; + float bufallpassL3[allpasstuningL3]; + float bufallpassR3[allpasstuningR3]; + float bufallpassL4[allpasstuningL4]; + float bufallpassR4[allpasstuningR4]; +}; + +#endif//_revmodel_ + +//ends \ No newline at end of file diff --git a/libs/freeverb/tuning.h b/libs/freeverb/tuning.h new file mode 100644 index 0000000000..baaa9ce004 --- /dev/null +++ b/libs/freeverb/tuning.h @@ -0,0 +1,60 @@ +// Reverb model tuning values +// +// Written by Jezar at Dreampoint, June 2000 +// http://www.dreampoint.co.uk +// This code is public domain + +#ifndef _tuning_ +#define _tuning_ + +const int numcombs = 8; +const int numallpasses = 4; +const float muted = 0; +const float fixedgain = 0.015f; +const float scalewet = 3; +const float scaledry = 2; +const float scaledamp = 0.4f; +const float scaleroom = 0.28f; +const float offsetroom = 0.7f; +const float initialroom = 0.5f; +const float initialdamp = 0.5f; +const float initialwet = 1/scalewet; +const float initialdry = 0; +const float initialwidth = 1; +const float initialmode = 0; +const float freezemode = 0.5f; +const int stereospread = 23; + +// These values assume 44.1KHz sample rate +// they will probably be OK for 48KHz sample rate +// but would need scaling for 96KHz (or other) sample rates. +// The values were obtained by listening tests. +const int combtuningL1 = 1116; +const int combtuningR1 = 1116+stereospread; +const int combtuningL2 = 1188; +const int combtuningR2 = 1188+stereospread; +const int combtuningL3 = 1277; +const int combtuningR3 = 1277+stereospread; +const int combtuningL4 = 1356; +const int combtuningR4 = 1356+stereospread; +const int combtuningL5 = 1422; +const int combtuningR5 = 1422+stereospread; +const int combtuningL6 = 1491; +const int combtuningR6 = 1491+stereospread; +const int combtuningL7 = 1557; +const int combtuningR7 = 1557+stereospread; +const int combtuningL8 = 1617; +const int combtuningR8 = 1617+stereospread; +const int allpasstuningL1 = 556; +const int allpasstuningR1 = 556+stereospread; +const int allpasstuningL2 = 441; +const int allpasstuningR2 = 441+stereospread; +const int allpasstuningL3 = 341; +const int allpasstuningR3 = 341+stereospread; +const int allpasstuningL4 = 225; +const int allpasstuningR4 = 225+stereospread; + +#endif//_tuning_ + +//ends + diff --git a/src/Z2AudioLib/Z2AudioMgr.cpp b/src/Z2AudioLib/Z2AudioMgr.cpp index 985c500aec..8c05678222 100644 --- a/src/Z2AudioLib/Z2AudioMgr.cpp +++ b/src/Z2AudioLib/Z2AudioMgr.cpp @@ -155,7 +155,14 @@ void Z2AudioMgr::zeldaGFrameWork() { mSpeechMgr.framework(); processSeFramework(); processBgmFramework(); + + #if TARGET_PC + if (!dusk::settings::game::noLowHpSound) { + processHeartGaugeSound(); + } + #else processHeartGaugeSound(); + #endif #if DEBUG mDebugSys.debugframework(); diff --git a/src/Z2AudioLib/Z2SoundObjMgr.cpp b/src/Z2AudioLib/Z2SoundObjMgr.cpp index 6b080134fb..463040d692 100644 --- a/src/Z2AudioLib/Z2SoundObjMgr.cpp +++ b/src/Z2AudioLib/Z2SoundObjMgr.cpp @@ -107,6 +107,15 @@ static Z2EnemyInfo mEnemyInfo[64] = { void Z2SoundObjMgr::searchEnemy() { twilightBattle_ = 0; + #if TARGET_PC + if (Z2GetSeqMgr()->checkBgmIDPlaying(Z2BGM_MIDNA_SOS) && + dusk::settings::game::midnasLamentNonStop) + { + Z2GetSeqMgr()->changeSubBgmStatus(0); + return; + } + #endif + if (!Z2GetLink()) { Z2GetSeqMgr()->stopBattleBgm(1, 1); return; diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index 4a9b2b273a..fdd309adae 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -19,28 +19,6 @@ void daAlink_c::handleQuickTransform() { return; } - // Make sure Link isn't riding anything (horse, boar, etc.) - if (checkRide()) { - return; - } - - switch (mProcID) { - // Make sure Link is not underwater or talking to someone. - case PROC_TALK: - case PROC_SWIM_UP: - case PROC_SWIM_DIVE: - return; - // If Link is targeting or pulling a chain, we don't want to remove the ability to use items in combat accidently. - case PROC_ATN_ACTOR_MOVE: - case PROC_ATN_ACTOR_WAIT: - case PROC_WOLF_ATN_AC_MOVE: - break; - default: - // Disable the input that was just pressed, as sometimes it could cause items to be used or Wolf Link to dig. - mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags = 0; - break; - } - // Ensure there is a proper pointer to the mMeterClass and mpMeterDraw structs in g_meter2_info. const auto meterClassPtr = g_meter2_info.getMeterClass(); if (!meterClassPtr) { @@ -52,18 +30,46 @@ void daAlink_c::handleQuickTransform() { return; } + mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags = 0; + // Ensure that the Z Button is not dimmed if (meterDrawPtr->getButtonZAlpha() != 1.f) { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return; } // The game will crash if trying to quick transform while holding the Ball and Chain if (mEquipItem == dItemNo_IRONBALL_e) { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return; } // Use the game's default checks for if the player can currently transform if (!m_midnaActor->checkMetamorphoseEnableBase()) { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); + return; + } + + bool canTransform = false; + + if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) { + if (!checkForestOldCentury()) { + if (checkMidnaRide()) { + if ((checkWolf() && + (checkModeFlg(MODE_UNK_1000) || dComIfGp_checkPlayerStatus0(0, 0x10))) || + (!checkWolf() && + (checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) && + (checkModeFlg(4) || dComIfGp_checkPlayerStatus0(0, 0x10)))) + { + canTransform = true; + } + } + } + } + + if (!canTransform) + { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); return; } diff --git a/src/d/actor/d_a_alink_hang.inc b/src/d/actor/d_a_alink_hang.inc index eb5e1392e0..73218f4b87 100644 --- a/src/d/actor/d_a_alink_hang.inc +++ b/src/d/actor/d_a_alink_hang.inc @@ -285,7 +285,11 @@ int daAlink_c::procHangStartInit() { offNoResetFlg2(FLG2_UNK_2000); if (checkHangFootWall()) { + #if TARGET_PC + if (!is_prev_hangReady && cM_rnd() < 0.7f && !dusk::settings::game::noMissClimbing) { + #else if (!is_prev_hangReady && cM_rnd() < 0.7f) { + #endif setSingleAnimeParam(ANM_CLIMB_HANG_MISS, &mpHIO->mLadder.m.mWallAttachMissAnm); field_0x3478 = mpHIO->mLadder.m.mWallAttachMissAnm.mCancelFrame; voiceStart(Z2SE_AL_V_FOOT_MISS); @@ -2084,7 +2088,11 @@ int daAlink_c::procClimbUpStartInit(int param_0) { speed.y = 0.0f; mNormalSpeed = 0.0f; + #if TARGET_PC + if (param_0 || var_r29 || cM_rnd() < 0.3f || dusk::settings::game::noMissClimbing) { + #else if (param_0 || var_r29 || cM_rnd() < 0.3f) { + #endif setSingleAnimeParam(ANM_CLIMB_HANG, &mpHIO->mLadder.m.mWallAttachAnm); field_0x3478 = mpHIO->mLadder.m.mWallAttachAnm.mCancelFrame; voiceStart(Z2SE_AL_V_JUMP_HANG); @@ -2161,7 +2169,11 @@ int daAlink_c::procClimbDownStartInit(s16 param_0) { deleteEquipItem(TRUE, FALSE); + #if TARGET_PC + if (cM_rnd() < 0.7f || dusk::settings::game::noMissClimbing) { + #else if (cM_rnd() < 0.7f) { + #endif setSingleAnimeParam(ANM_CLIMB_HANG, &mpHIO->mLadder.m.mWallAttachAnm); field_0x3478 = mpHIO->mLadder.m.mWallAttachAnm.mCancelFrame; mProcVar0.field_0x3008 = 0; diff --git a/src/d/actor/d_a_midna.cpp b/src/d/actor/d_a_midna.cpp index 88a5bb38bb..a50a3cb1f7 100644 --- a/src/d/actor/d_a_midna.cpp +++ b/src/d/actor/d_a_midna.cpp @@ -3046,12 +3046,17 @@ void daMidna_c::setMidnaNoDrawFlg() { BOOL daMidna_c::checkMetamorphoseEnableBase() { BOOL tmp; - if ( - !daAlink_getAlinkActorClass()->checkMidnaRide() || (g_env_light.mEvilInitialized & 0x80) || + if (!daAlink_getAlinkActorClass()->checkMidnaRide() || (g_env_light.mEvilInitialized & 0x80) || /* dSv_event_flag_c::M_077 - Main Event - Get shadow crystal (can now transform) */ !dComIfGs_isEventBit(0xD04) || +#if TARGET_PC + (fopAcIt_Judge((fopAcIt_JudgeFunc)daMidna_searchNpc, &tmp) && + !dusk::settings::game::canTransformAnywhere) +#else fopAcIt_Judge((fopAcIt_JudgeFunc)daMidna_searchNpc, &tmp) - ) { +#endif + ) + { return FALSE; } return TRUE; diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index 3cb4811ab1..8b6d197624 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -9696,7 +9696,7 @@ void dKy_ParticleColor_get_base(cXyz* param_0, dKy_tevstr_c* param_1, GXColor* p f32 var_f31; #if AVOID_UB - var_f31 = 0; + var_f31 = 100000000.0f; #endif if (dKy_SunMoon_Light_Check() == TRUE && i <= 1) { diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 93e161512f..c7313749c2 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include "JSystem/JAudio2/JASAiCtrl.h" @@ -17,8 +15,6 @@ #include "JSystem/JAudio2/JASAudioThread.h" #include "JSystem/JAudio2/JASDriverIF.h" -// #define DUSK_DUMP_AUDIO - using namespace dusk::audio; static OutputSubframe OutBuffer; @@ -91,10 +87,6 @@ void SDLCALL GetNewAudio( } } -#if defined(DUSK_DUMP_AUDIO) -static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); -#endif - int RenderNewAudioFrame() { JASCriticalSection section; const u32 countSubframes = JASDriver::getSubFrames(); @@ -107,10 +99,6 @@ int RenderNewAudioFrame() { JASAudioThread::snIntCount -= 1; } -#if defined(DUSK_DUMP_AUDIO) - outRaw.flush(); -#endif - return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } @@ -147,10 +135,6 @@ void RenderAudioSubframe() { } } -#if defined(DUSK_DUMP_AUDIO) - outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer)); -#endif - SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 7cf1c44cd8..3e32d3e7ff 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "Adpcm.hpp" @@ -17,8 +18,30 @@ using namespace dusk::audio; ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; +static bool sDumpWasActive = false; +static FILE* sChannelDumpFiles[DSP_CHANNELS] = {}; + +static void OpenChannelDumpFiles() { + char name[32]; + for (int i = 0; i < DSP_CHANNELS; i++) { + snprintf(name, sizeof(name), "channel_%02d.raw", i); + sChannelDumpFiles[i] = fopen(name, "wb"); + } +} + +static void CloseChannelDumpFiles() { + for (int i = 0; i < DSP_CHANNELS; i++) { + if (sChannelDumpFiles[i]) { + fclose(sChannelDumpFiles[i]); + sChannelDumpFiles[i] = nullptr; + } + } +} + f32 dusk::audio::MasterVolume = 1.0f; f32 dusk::audio::PrevMasterVolume = 1.0f; +bool dusk::audio::EnableReverb = true; +bool dusk::audio::DumpAudio = false; /** * Validate that a DSP channel's format is actually something we know how to play. @@ -106,38 +129,81 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { } void dusk::audio::DspRender(OutputSubframe& subframe) { + if (DumpAudio != sDumpWasActive) { + sDumpWasActive = DumpAudio; + if (DumpAudio) { + OpenChannelDumpFiles(); + } else { + CloseChannelDumpFiles(); + } + } + std::span channels(JASDsp::CH_BUF, DSP_CHANNELS); for (int i = 0; i < channels.size(); i++) { auto& channel = channels[i]; auto& channelAux = ChannelAux[i]; - if (!channel.mIsActive) { - continue; - } + bool skipRender = false; - if (channel.mPauseFlag) { + if (!channel.mIsActive) { + skipRender = true; + } + else if (channel.mPauseFlag) { // Not really sure what the practical difference between pause and // deactivation is. Either avoids clearing state or allows the DSP to avoid popping? - continue; + skipRender = true; } - - if (channel.mForcedStop) { + else if (channel.mForcedStop) { channel.mIsFinished = true; - continue; + skipRender = true; } - - if (channel.mWaveAramAddress == 0) { + else if (channel.mWaveAramAddress == 0) { // I think these are oscillator channels? Not backed by audio. // No idea how to implement these yet, so skip them. channel.mIsFinished = true; - continue; + skipRender = true; } - ValidateChannel(channel); - OutputSubframe channelSubframe = {}; - RenderChannel(channel, channelAux, channelSubframe); + + if (!skipRender) { + ValidateChannel(channel); + RenderChannel(channel, channelAux, channelSubframe); + } + + if (EnableReverb) { + // scale the input to the reverb rather than using wet/dry on the output. + // this way the reverb's internal buffers accumulate energy proportional to mAutoMixerFxMix, + // so any tail always decays at the correct level regardless of mAutoMixerFxMix changes + // prevents transients when the next sound starts playing with a different reverb level + // 600.0f was pulled out of my ass and just sounds good enough for console + f32 inputGain = (!skipRender) ? (channel.mAutoMixerFxMix >> 8) / 600.0f : 0.0f; + + OutputSubframe reverbSubframe = {}; + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + reverbSubframe.channels[0][j] = channelSubframe.channels[0][j] * inputGain; + reverbSubframe.channels[1][j] = channelSubframe.channels[1][j] * inputGain; + } + + channelAux.reverb.processreplace( + reverbSubframe.channels[0].data(), reverbSubframe.channels[1].data(), + reverbSubframe.channels[0].data(), reverbSubframe.channels[1].data(), + DSP_SUBFRAME_SIZE, 1 + ); + + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + channelSubframe.channels[0][j] += reverbSubframe.channels[0][j]; + channelSubframe.channels[1][j] += reverbSubframe.channels[1][j]; + } + } + + if (DumpAudio && sChannelDumpFiles[i]) { + for (int j = 0; j < DSP_SUBFRAME_SIZE; j++) { + fwrite(&channelSubframe.channels[0][j], sizeof(f32), 1, sChannelDumpFiles[i]); + fwrite(&channelSubframe.channels[1][j], sizeof(f32), 1, sChannelDumpFiles[i]); + } + } for (int o = 0; o < subframe.channels.size(); o++) { MixSubframe(subframe.channels[o], channelSubframe.channels[o]); @@ -463,6 +529,16 @@ static void RenderChannel( } void dusk::audio::DspInit() { + for (int i = 0; i < DSP_CHANNELS; i++) { + auto& channelAux = ChannelAux[i]; + channelAux.reverb.setwet(1.0f); + channelAux.reverb.setdry(0.0f); + channelAux.reverb.setroomsize(0.5f); + channelAux.reverb.setdamp(0.7f); + channelAux.reverb.setwidth(1.0f); + channelAux.reverb.setmode(0.0f); + channelAux.reverb.mute(); + } } void dusk::audio::ApplyVolume( diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 35b6ccabc9..728234b75c 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -8,6 +8,8 @@ #include "SDL3/SDL_audio.h" #include +#include "freeverb/revmodel.hpp" + // ReSharper disable once CppUnusedIncludeDirective #include "global.h" @@ -29,6 +31,7 @@ namespace dusk::audio { // Used for debugging tools. u32 resetCount; + revmodel reverb; /** * Previous volume values, per output channel. @@ -51,7 +54,7 @@ namespace dusk::audio { // basically stores our position between resamplePrev and decodeBuf[0] so we don't lose that fractional resampler position next subframe f32 resamplePos; // last consumed sample from decodeBuf - s16 resamplePrev; + s16 resamplePrev; }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; @@ -118,4 +121,6 @@ namespace dusk::audio { extern f32 MasterVolume; extern f32 PrevMasterVolume; + extern bool EnableReverb; + extern bool DumpAudio; } diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index ae01f119f4..3328d1b97c 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -8,6 +8,7 @@ #include "JSystem/JAudio2/JASDSPInterface.h" #include "JSystem/JAudio2/JASTrack.h" #include "dusk/audio/DuskAudioSystem.h" +#include "dusk/audio/DuskDsp.hpp" static std::array channelSortIndices = {}; static std::array lastResetCounts = {}; @@ -102,6 +103,7 @@ static void ShowAllDspChannels() { } ImGui::Text("Active channels: %d", activeChannels); + ImGui::Checkbox("Dump channels to disk", &dusk::audio::DumpAudio); ImGui::Checkbox("Sort by update count", &sortUpdateCount); if (sortUpdateCount) { diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index 347d634dc4..537539cd02 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -37,6 +37,12 @@ namespace dusk { ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version"); } + config::ImGuiCheckbox("No Climbing Miss Animation", settings::game::noMissClimbing); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Prevents Link from playing a struggle animation\n" + "when using the Clawshot on vines at a weird angle"); + } + config::ImGuiCheckbox("Faster Tears of Light", settings::game::fastTears); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version"); @@ -46,11 +52,16 @@ namespace dusk { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save"); } + ImGui::EndMenu(); } if (ImGui::BeginMenu("Preferences")) { config::ImGuiCheckbox("Mirror Mode", settings::game::enableMirrorMode); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game"); + } + config::ImGuiCheckbox("Invert Camera X Axis", settings::game::invertCameraXAxis); ImGui::EndMenu(); @@ -58,10 +69,25 @@ namespace dusk { if (ImGui::BeginMenu("Graphics")) { config::ImGuiCheckbox("Native Bloom", settings::game::enableBloom); + config::ImGuiCheckbox("Water Projection Offset", settings::game::useWaterProjectionOffset); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Adds GC-specific -0.01 transS offset\n" - "that causes ~6px ghost artifacts in water reflections"); + "that causes ~6px ghost artifacts in water reflections"); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Audio")) { + config::ImGuiCheckbox("No Low HP Sound", settings::game::noLowHpSound); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Disable the beeping sound when having low health"); + } + + config::ImGuiCheckbox("Non-Stop Midna's Lament", settings::game::midnasLamentNonStop); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing"); } ImGui::EndMenu(); @@ -70,11 +96,17 @@ namespace dusk { if (ImGui::BeginMenu("Cheats")) { config::ImGuiCheckbox("Fast Iron Boots", settings::game::enableFastIronBoots); + config::ImGuiCheckbox("Can Transform Anywhere", settings::game::canTransformAnywhere); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Allows you to transform even if NPCs are looking"); + } + ImGui::EndMenu(); } if (ImGui::BeginMenu("Difficulty")) { config::ImGuiSliderInt("Damage Multiplier", settings::game::damageMultiplier, 1, 8, "x%d"); + config::ImGuiCheckbox("Instant Death", settings::game::instantDeath); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Any hit will instantly kill you"); @@ -86,7 +118,8 @@ namespace dusk { if (ImGui::BeginMenu("Technical")) { config::ImGuiCheckbox("Restore Wii 1.0 Glitches", settings::game::restoreWiiGlitches); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0, the first released version"); + ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n" + "the first released version"); } ImGui::EndMenu(); diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 00d7b578bc..28a582e524 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -7,6 +7,7 @@ #include #include "JSystem/JUtility/JUTGamePad.h" +#include "dusk/audio/DuskDsp.hpp" #include "dusk/audio/DuskAudioSystem.h" #include "dusk/hotkeys.h" #include "dusk/settings.h" @@ -39,7 +40,7 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { ImGui::Text("Master Volume"); config::ImGuiSliderFloat("##masterVolume", settings::audio::masterVolume, 0.0f, 1.0f, ""); - + config::ImGuiCheckbox("Enable Reverb", settings::audio::enableReverb); /* // TODO: implement additional settings ImGui::Text("Main Music Volume"); @@ -60,6 +61,7 @@ namespace dusk { */ audio::SetMasterVolume(settings::audio::masterVolume); + audio::EnableReverb = settings::audio::enableReverb; ImGui::EndMenu(); } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 34bde294de..07128fa192 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -12,6 +12,7 @@ ConfigVar mainMusicVolume("audio.mainMusicVolume", 1.0f); ConfigVar subMusicVolume("audio.subMusicVolume", 1.0f); ConfigVar soundEffectsVolume("audio.soundEffectsVolume", 1.0f); ConfigVar fanfareVolume("audio.fanfareVolume", 1.0f); +ConfigVar enableReverb("audio.enableReverb", true); } namespace game { @@ -26,6 +27,7 @@ ConfigVar damageMultiplier("game.damageMultiplier", 1); ConfigVar instantDeath("game.instantDeath", false); ConfigVar fastClimbing("game.fastClimbing", false); ConfigVar fastTears("game.fastTears", false); +ConfigVar noMissClimbing("game.noMissClimbing", false); // Preferences ConfigVar enableMirrorMode("game.enableMirrorMode", false); @@ -35,8 +37,13 @@ ConfigVar invertCameraXAxis("game.invertCameraXAxis", false); ConfigVar enableBloom("game.enableBloom", true); ConfigVar useWaterProjectionOffset("game.useWaterProjectionOffset", false); +// Audio +ConfigVar noLowHpSound("game.noLowHpSound", false); +ConfigVar midnasLamentNonStop("game.midnasLamentNonStop", false); + // Cheats ConfigVar enableFastIronBoots("game.enableFastIronBoots", false); +ConfigVar canTransformAnywhere("game.canTransformAnywhere", false); // Technical ConfigVar restoreWiiGlitches("game.restoreWiiGlitches", false); @@ -52,6 +59,7 @@ void Register() { Register(audio::subMusicVolume); Register(audio::soundEffectsVolume); Register(audio::fanfareVolume); + Register(audio::enableReverb); // Game Register(game::enableQuickTransform); @@ -69,7 +77,11 @@ void Register() { Register(game::enableBloom); Register(game::useWaterProjectionOffset); Register(game::enableFastIronBoots); + Register(game::canTransformAnywhere); Register(game::restoreWiiGlitches); + Register(game::noMissClimbing); + Register(game::noLowHpSound); + Register(game::midnasLamentNonStop); } }