diff --git a/.gitignore b/.gitignore index ae7fa75384..a381014599 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,7 @@ compile_commands.json # ISOs *.iso + +*.ini +*.controller +pipeline_cache.bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e2be043ee..d7308b0ba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,13 @@ 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) +add_custom_command(TARGET dusk POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/res" + "$/res" + COMMENT "Copying resources" +) + include(extern/aurora/cmake/AuroraCopyRuntimeDLLs.cmake) aurora_copy_runtime_dlls(dusk game) diff --git a/extern/aurora b/extern/aurora index 33ec54347d..b17da31593 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 33ec54347df497c3f8c237e5c2b65590fb1807df +Subproject commit b17da315932b85d7d708eaf3e44914f70045a44e diff --git a/files.cmake b/files.cmake index 8a9e7be4d5..7d07408613 100644 --- a/files.cmake +++ b/files.cmake @@ -1333,7 +1333,6 @@ set(DOLPHIN_FILES set(DUSK_FILES include/dusk/endian_gx.hpp include/dusk/config.hpp - include/dusk/settings.hpp src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp src/dusk/config.cpp @@ -1346,10 +1345,13 @@ set(DUSK_FILES src/dusk/extras.cpp src/dusk/io.cpp src/dusk/globals.cpp + src/dusk/settings.cpp #src/dusk/m_Do_ext_dusk.cpp src/dusk/imgui/ImGuiConfig.hpp src/dusk/imgui/ImGuiConsole.hpp src/dusk/imgui/ImGuiConsole.cpp + src/dusk/imgui/ImGuiEngine.cpp + src/dusk/imgui/ImGuiEngine.hpp src/dusk/imgui/ImGuiMenuGame.cpp src/dusk/imgui/ImGuiMenuGame.hpp src/dusk/imgui/ImGuiMenuTools.cpp diff --git a/include/d/d_s_name.h b/include/d/d_s_name.h index 73d3d703be..72a44c02de 100644 --- a/include/d/d_s_name.h +++ b/include/d/d_s_name.h @@ -48,6 +48,7 @@ public: void FileSelectClose(); void brightCheckOpen(); void brightCheck(); + void doPreLoadSetup(); void changeGameScene(); #if VERSION == VERSION_GCN_PAL @@ -70,6 +71,9 @@ private: /* 0x41E */ u8 mWaitTimer; /* 0x41F */ u8 field_0x41f; /* 0x420 */ u8 field_0x420; +#if TARGET_PC + bool mShowTvSettingsScreen; +#endif }; #endif /* D_S_D_S_NAME_H */ diff --git a/include/dusk/hotkeys.h b/include/dusk/hotkeys.h new file mode 100644 index 0000000000..ba187a544c --- /dev/null +++ b/include/dusk/hotkeys.h @@ -0,0 +1,19 @@ +#ifndef DUSK_HOTKEYS_H +#define DUSK_HOTKEYS_H + +namespace dusk::hotkeys { + +constexpr const char* DO_RESET = "Ctrl+R"; + +constexpr const char* TOGGLE_FULLSCREEN = "F11"; + +constexpr const char* SHOW_PROCESS_MANAGEMENT = "F2"; +constexpr const char* SHOW_DEBUG_OVERLAY = "F3"; +constexpr const char* SHOW_HEAP_VIEWER = "F4"; +constexpr const char* SHOW_STUB_LOG = "F5"; +constexpr const char* SHOW_CAMERA_DEBUG = "F6"; +constexpr const char* SHOW_AUDIO_DEBUG = "F7"; + +} + +#endif // DUSK_HOTKEYS_H diff --git a/include/dusk/settings.h b/include/dusk/settings.h new file mode 100644 index 0000000000..d6a860939d --- /dev/null +++ b/include/dusk/settings.h @@ -0,0 +1,78 @@ +#ifndef DUSK_CONFIG_H +#define DUSK_CONFIG_H + +#include "config_var.hpp" + +namespace dusk::settings { +using namespace config; + +// Persistent user settings + +namespace video { +extern ConfigVar enableFullscreen; +} + +namespace audio { +extern ConfigVar masterVolume; +extern ConfigVar mainMusicVolume; +extern ConfigVar subMusicVolume; +extern ConfigVar soundEffectsVolume; +extern ConfigVar fanfareVolume; +} + +namespace game { +// QoL +extern ConfigVar enableQuickTransform; +extern ConfigVar hideTvSettingsScreen; +extern ConfigVar biggerWallets; +extern ConfigVar noReturnRupees; +extern ConfigVar disableRupeeCutscenes; +extern ConfigVar noSwordRecoil; +extern ConfigVar damageMultiplier; +extern ConfigVar instantDeath; +extern ConfigVar fastClimbing; +extern ConfigVar fastTears; + +// Preferences +extern ConfigVar enableMirrorMode; +extern ConfigVar invertCameraXAxis; + +// Graphics +extern ConfigVar enableBloom; +extern ConfigVar useWaterProjectionOffset; + +// Cheats +extern ConfigVar enableFastIronBoots; + +// Technical +extern ConfigVar restoreWiiGlitches; +} + +void Register(); + +} + +namespace dusk { +// Transient settings + +struct CollisionViewSettings { + bool enableTerrainView; + bool enableWireframe; + bool enableAtView; + bool enableTgView; + bool enableCoView; + float terrainViewOpacity; + float colliderViewOpacity; + float drawRange; +}; + +struct TransientSettings { + CollisionViewSettings collisionView; +}; + +TransientSettings& getTransientSettings(); + +} + +#endif // DUSK_CONFIG_H + diff --git a/include/dusk/settings.hpp b/include/dusk/settings.hpp deleted file mode 100644 index 79c9981647..0000000000 --- a/include/dusk/settings.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DUSK_SETTINGS_HPP -#define DUSK_SETTINGS_HPP - -#include "config_var.hpp" - -namespace dusk::settings { - using namespace dusk::config; - - namespace enhancements { - extern ConfigVar FastIronBoots; - extern ConfigVar InvertCameraXAxis; - extern ConfigVar QuickTransform; - extern ConfigVar RestoreWiiGlitches; - extern ConfigVar EnableBloom; - extern ConfigVar UseWaterProjectionOffset; - extern ConfigVar MirrorMode; - - void Register(); - } - - void Register(); -} - -#endif // DUSK_SETTINGS_HPP diff --git a/include/m_Do/m_Do_controller_pad.h b/include/m_Do/m_Do_controller_pad.h index 57de651bc0..f76a38cac8 100644 --- a/include/m_Do/m_Do_controller_pad.h +++ b/include/m_Do/m_Do_controller_pad.h @@ -3,9 +3,7 @@ #include "JSystem/JUtility/JUTGamePad.h" #include "SSystem/SComponent/c_API_controller_pad.h" -#include "dusk/settings.hpp" - -#include "dusk/imgui/ImGuiMenuEnhancements.hpp" +#include "dusk/settings.h" // Controller Ports 1 - 4 enum { PAD_1, PAD_2, PAD_3, PAD_4 }; @@ -58,7 +56,7 @@ public: static s16 getStickAngle3D(u32 pad) { #if TARGET_PC - if (dusk::settings::enhancements::MirrorMode.getValue()) { + if (dusk::settings::game::enableMirrorMode) { return -getCpadInfo(pad).mMainStickAngle; } else { return getCpadInfo(pad).mMainStickAngle; @@ -70,7 +68,7 @@ public: static f32 getSubStickX3D(u32 pad) { #if TARGET_PC - if (dusk::settings::enhancements::MirrorMode.getValue()) { + if (dusk::settings::game::enableMirrorMode) { return -getCpadInfo(pad).mCStickPosX; } else { return getCpadInfo(pad).mCStickPosX; diff --git a/libs/JSystem/include/JSystem/J3DGraphLoader/J3DMaterialFactory_v21.h b/libs/JSystem/include/JSystem/J3DGraphLoader/J3DMaterialFactory_v21.h index 6b70201532..93d4fb9b7d 100644 --- a/libs/JSystem/include/JSystem/J3DGraphLoader/J3DMaterialFactory_v21.h +++ b/libs/JSystem/include/JSystem/J3DGraphLoader/J3DMaterialFactory_v21.h @@ -82,7 +82,7 @@ public: /* 0x00 */ u16 mMaterialNum; /* 0x04 */ J3DMaterialInitData_v21* mpMaterialInitData; - /* 0x08 */ u16* mpMaterialID; + /* 0x08 */ BE(u16)* mpMaterialID; /* 0x0C */ GXColor* mpMatColor; /* 0x10 */ u8* mpColorChanNum; /* 0x14 */ J3DColorChanInfo* mpColorChanInfo; @@ -91,10 +91,10 @@ public: /* 0x20 */ J3DTexCoord2Info* mpTexCoord2Info; /* 0x24 */ J3DTexMtxInfo* mpTexMtxInfo; /* 0x28 */ J3DTexMtxInfo* field_0x28; - /* 0x2C */ u16* mpTexNo; - /* 0x30 */ GXCullMode* mpCullMode; + /* 0x2C */ BE(u16)* mpTexNo; + /* 0x30 */ BE(GXCullMode)* mpCullMode; /* 0x34 */ J3DTevOrderInfo* mpTevOrderInfo; - /* 0x38 */ GXColorS10* mpTevColor; + /* 0x38 */ BE(GXColorS10)* mpTevColor; /* 0x3C */ GXColor* mpTevKColor; /* 0x40 */ u8* mpTevStageNum; /* 0x44 */ J3DTevStageInfo* mpTevStageInfo; diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h b/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h index 50f5b45f09..44d4137d9b 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h @@ -10,7 +10,7 @@ */ struct JAUAudibleParam { f32 getDopplerPower() const { - return (u32)((*(u8*)&field_0x0.raw >> 4) & 0xf) * (1.0f / 15.0f); + return field_0x0.bytes.b0_0 * (1.0f / 15.0f); } union { diff --git a/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp b/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp index 229309545a..f6cca25525 100644 --- a/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp +++ b/libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp @@ -10,8 +10,8 @@ J3DMaterialFactory_v21::J3DMaterialFactory_v21(J3DMaterialBlock_v21 const& i_block) { mMaterialNum = i_block.mMaterialNum; mpMaterialInitData = JSUConvertOffsetToPtr(&i_block, i_block.mpMaterialInitData); - mpMaterialID = JSUConvertOffsetToPtr(&i_block, i_block.mpMaterialID); - mpCullMode = JSUConvertOffsetToPtr(&i_block, i_block.mpCullMode); + mpMaterialID = JSUConvertOffsetToPtr(&i_block, i_block.mpMaterialID); + mpCullMode = JSUConvertOffsetToPtr(&i_block, i_block.mpCullMode); mpMatColor = JSUConvertOffsetToPtr(&i_block, i_block.mpMatColor); mpColorChanNum = JSUConvertOffsetToPtr(&i_block, i_block.mpColorChanNum); mpColorChanInfo = JSUConvertOffsetToPtr(&i_block, i_block.mpColorChanInfo); @@ -20,9 +20,9 @@ J3DMaterialFactory_v21::J3DMaterialFactory_v21(J3DMaterialBlock_v21 const& i_blo mpTexCoord2Info = JSUConvertOffsetToPtr(&i_block, i_block.mpTexCoord2Info); mpTexMtxInfo = JSUConvertOffsetToPtr(&i_block, i_block.mpTexMtxInfo); field_0x28 = JSUConvertOffsetToPtr(&i_block, i_block.field_0x38); - mpTexNo = JSUConvertOffsetToPtr(&i_block, i_block.mpTexNo); + mpTexNo = JSUConvertOffsetToPtr(&i_block, i_block.mpTexNo); mpTevOrderInfo = JSUConvertOffsetToPtr(&i_block, i_block.mpTevOrderInfo); - mpTevColor = JSUConvertOffsetToPtr(&i_block, i_block.mpTevColor); + mpTevColor = JSUConvertOffsetToPtr(&i_block, i_block.mpTevColor); mpTevKColor = JSUConvertOffsetToPtr(&i_block, i_block.mpTevKColor); mpTevStageNum = JSUConvertOffsetToPtr(&i_block, i_block.mpTevStageNum); mpTevStageInfo = JSUConvertOffsetToPtr(&i_block, i_block.mpTevStageInfo); @@ -260,7 +260,7 @@ J3DGXColorS10 J3DMaterialFactory_v21::newTevColor(int i_idx, int i_no) const { J3DGXColorS10 dflt = defaultTevColor; J3DMaterialInitData_v21* mtl_init_data = &mpMaterialInitData[mpMaterialID[i_idx]]; if (mtl_init_data->mTevColorIdx[i_no] != 0xffff) { - return mpTevColor[mtl_init_data->mTevColorIdx[i_no]]; + return (GXColorS10)mpTevColor[mtl_init_data->mTevColorIdx[i_no]]; } else { return dflt; } diff --git a/res/NotoMono-Regular.ttf b/res/NotoMono-Regular.ttf new file mode 100644 index 0000000000..3560a3a0c8 Binary files /dev/null and b/res/NotoMono-Regular.ttf differ diff --git a/src/Z2AudioLib/Z2Audience.cpp b/src/Z2AudioLib/Z2Audience.cpp index 9adab4084f..5bb91eef31 100644 --- a/src/Z2AudioLib/Z2Audience.cpp +++ b/src/Z2AudioLib/Z2Audience.cpp @@ -802,7 +802,7 @@ f32 Z2Audience::calcFxMix_(f32 param_0, int distVolBit) const { f32 Z2Audience::calcPitch_(Z2AudibleChannel* channel, const Z2Audible* audible, const Z2AudioCamera* camera) const { JAUAudibleParam audParam = *audible->getAudibleParam(); - if ((*(u8*)&audParam.field_0x0.raw >> 4) & 0xf) { + if (audParam.field_0x0.bytes.b0_0) { JGeometry::TVec3 aTStack_4c; aTStack_4c.normalize(channel->field_0x14.field_0x00); JAUAudibleParam audParam = *audible->getAudibleParam(); diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index f847fde1cb..993f78037b 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -54,8 +54,6 @@ #include "res/Object/Alink.h" #include -#include "dusk/settings.hpp" - static int daAlink_Create(fopAc_ac_c* i_this); static int daAlink_Delete(daAlink_c* i_this); static int daAlink_Execute(daAlink_c* i_this); @@ -7512,7 +7510,7 @@ void daAlink_c::setBlendMoveAnime(f32 i_morf) { BOOL sp24 = checkEventRun(); BOOL sp20 = checkBootsMoveAnime(1); #if TARGET_PC - if (dusk::settings::enhancements::FastIronBoots.getValue()) { + if (dusk::settings::game::enableFastIronBoots) { sp20 = FALSE; } #endif @@ -9477,7 +9475,7 @@ void daAlink_c::setStickData() { mHeavySpeedMultiplier = mpHIO->mItem.mIronBoots.m.mInputFactor; } #if TARGET_PC - if (dusk::settings::enhancements::FastIronBoots.getValue()) { + if (dusk::settings::game::enableFastIronBoots) { mHeavySpeedMultiplier = 1.0f; } #endif @@ -9489,7 +9487,7 @@ void daAlink_c::setStickData() { mHeavySpeedMultiplier = mpHIO->mItem.mIronBoots.m.mWaterInputFactor; } #if TARGET_PC - if (dusk::settings::enhancements::FastIronBoots.getValue()) { + if (dusk::settings::game::enableFastIronBoots) { mHeavySpeedMultiplier = 1.0f; } #endif diff --git a/src/d/actor/d_a_alink_cut.inc b/src/d/actor/d_a_alink_cut.inc index 4e9dd18aee..5928f7e891 100644 --- a/src/d/actor/d_a_alink_cut.inc +++ b/src/d/actor/d_a_alink_cut.inc @@ -7,6 +7,8 @@ #include "d/actor/d_a_b_gnd.h" #include "SSystem/SComponent/c_math.h" +#include "dusk/settings.h" + enum daAlink_CutNmParamType { CUT_NM_PARAM_VERTICAL, CUT_NM_PARAM_LEFT, @@ -369,6 +371,12 @@ BOOL daAlink_c::changeCutReverseProc(daAlink_c::daAlink_ANM i_anmID) { return procCutReverseInit(i_anmID); } + #if TARGET_PC + if (dusk::settings::game::noSwordRecoil) { + return FALSE; + } + #endif + if (checkNoResetFlg0(FLG0_CUT_AT_FLG) || mEquipItem == dItemNo_COPY_ROD_e) { cXyz sp28; Vec sp1C; diff --git a/src/d/actor/d_a_alink_damage.inc b/src/d/actor/d_a_alink_damage.inc index 122bf8f248..9bbe04206b 100644 --- a/src/d/actor/d_a_alink_damage.inc +++ b/src/d/actor/d_a_alink_damage.inc @@ -152,6 +152,13 @@ f32 daAlink_c::damageMagnification(BOOL i_checkZoraMag, int param_1) { base_mag = 1.0f; } + #if TARGET_PC + base_mag *= dusk::settings::game::damageMultiplier; + if (dusk::settings::game::instantDeath) { + base_mag = 9999.0f; + } + #endif + if (checkWolf() && !checkCargoCarry() && param_1 == 0) { return 2.0f * base_mag; } diff --git a/src/d/actor/d_a_alink_demo.inc b/src/d/actor/d_a_alink_demo.inc index 936b0128d5..c1c8c53bd2 100644 --- a/src/d/actor/d_a_alink_demo.inc +++ b/src/d/actor/d_a_alink_demo.inc @@ -23,7 +23,7 @@ #include "d/actor/d_a_npc_tkc.h" #include -#include "dusk/imgui/ImGuiMenuEnhancements.hpp" +#include "dusk/settings.h" BOOL daAlink_c::checkEventRun() const { return dComIfGp_event_runCheck() || checkPlayerDemoMode(); @@ -2232,6 +2232,12 @@ void daAlink_c::setGetSubBgm(int i_itemNo) { } BOOL daAlink_c::checkTreasureRupeeReturn(int i_itemNo) const { + #if TARGET_PC + if (dusk::settings::game::noReturnRupees) { + return FALSE; + } + #endif + static const int dummy = 0; if (i_itemNo == dItemNo_LINKS_SAVINGS_e) { @@ -4301,7 +4307,7 @@ bool daAlink_c::checkAcceptWarp() { */ if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) #if TARGET_PC - && (dusk::settings::enhancements::RestoreWiiGlitches.getValue() || !checkNoResetFlg0(FLG0_WATER_IN_MOVE)) + && (dusk::settings::game::restoreWiiGlitches || !checkNoResetFlg0(FLG0_WATER_IN_MOVE)) #elif VERSION != VERSION_WII_USA_R0 && !checkNoResetFlg0(FLG0_WATER_IN_MOVE) #endif @@ -4312,7 +4318,7 @@ bool daAlink_c::checkAcceptWarp() { */ if ( #if TARGET_PC - (dusk::settings::enhancements::RestoreWiiGlitches.getValue() || !getSlidePolygon(&plane)) && + (dusk::settings::game::restoreWiiGlitches || !getSlidePolygon(&plane)) && #elif VERSION != VERSION_WII_USA_R0 !getSlidePolygon(&plane) && #endif diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index f49443a0d0..4a9b2b273a 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -3,10 +3,9 @@ #include "d/d_meter2.h" #include "d/d_meter2_draw.h" #include "d/d_meter2_info.h" -#include "dusk/imgui/ImGuiMenuEnhancements.hpp" void daAlink_c::handleQuickTransform() { - if (!dusk::settings::enhancements::QuickTransform.getValue()) { + if (!dusk::settings::game::enableQuickTransform) { return; } @@ -70,4 +69,4 @@ void daAlink_c::handleQuickTransform() { OSReport("Running quick transform!"); procCoMetamorphoseInit(); -} \ No newline at end of file +} diff --git a/src/d/actor/d_a_alink_hang.inc b/src/d/actor/d_a_alink_hang.inc index b99fe71c85..eb5e1392e0 100644 --- a/src/d/actor/d_a_alink_hang.inc +++ b/src/d/actor/d_a_alink_hang.inc @@ -13,8 +13,15 @@ #include "d/actor/d_a_obj_swhang.h" f32 daAlink_c::getHangMoveAnmSpeed() { + #if TARGET_PC + return getAnmSpeedStickRate( + dusk::settings::game::fastClimbing ? 2.0f : mpHIO->mWallHang.mWallMove.m.mMinAnmSpeed, + dusk::settings::game::fastClimbing ? 2.0f : mpHIO->mWallHang.mWallMove.m.mMaxAnmSpeed + ); + #else return getAnmSpeedStickRate(mpHIO->mWallHang.mWallMove.m.mMinAnmSpeed, mpHIO->mWallHang.mWallMove.m.mMaxAnmSpeed); + #endif } int daAlink_c::getHangDirectionFromAngle() { @@ -1209,7 +1216,12 @@ void daAlink_c::setLadderPos(int param_0) { f32 daAlink_c::getLadderMoveAnmSpeed() { return getAnmSpeedStickRate(mpHIO->mLadder.m.mMoveMinASpeed, - mpHIO->mLadder.m.mMoveMaxSpeed); + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.575f : mpHIO->mLadder.m.mMoveMaxSpeed + #else + mpHIO->mLadder.m.mMoveMaxSpeed + #endif + ); } int daAlink_c::changeLadderMoveProc(int param_0) { @@ -1304,8 +1316,14 @@ int daAlink_c::procLadderUpStartInit() { mNormalSpeed = 0.0f; speedF = 0.0f; - setSingleAnimeBaseSpeed(ANM_LADDER_UP_START, mpHIO->mLadder.m.mClimbUpStartASpeed, - mpHIO->mLadder.m.mClimbUpStartInterp); + setSingleAnimeBaseSpeed(ANM_LADDER_UP_START, + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.8f : mpHIO->mLadder.m.mClimbUpStartASpeed, + #else + mpHIO->mLadder.m.mClimbUpStartASpeed, + #endif + mpHIO->mLadder.m.mClimbUpStartInterp); + field_0x2f99 = 0x10; field_0x3588 = l_waitBaseAnime; dComIfGp_setPlayerStatus0(0, 0x2000000); @@ -1354,8 +1372,14 @@ int daAlink_c::procLadderUpEndInit(BOOL param_0) { anm_id = ANM_LADDER_UP_END_RIGHT; } - setSingleAnimeBaseSpeed(anm_id, mpHIO->mLadder.m.mClimbUpEndASpeed, - mpHIO->mLadder.m.mClimbUpEndInterp); + setSingleAnimeBaseSpeed(anm_id, + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.765f : mpHIO->mLadder.m.mClimbUpEndASpeed, + #else + mpHIO->mLadder.m.mClimbUpEndASpeed, + #endif + mpHIO->mLadder.m.mClimbUpEndInterp); + field_0x2f99 = 14; setSpecialGravity(0.0f, maxFallSpeed, 0); @@ -1413,8 +1437,14 @@ int daAlink_c::procLadderDownStartInit() { shape_angle.y = field_0x306e + 0x8000; current.angle.y = field_0x306e; - setSingleAnimeBaseSpeed(ANM_LADDER_DOWN_START, mpHIO->mLadder.m.mClimbDownStartASpeed, - mpHIO->mLadder.m.mClimbDownStartInterp); + setSingleAnimeBaseSpeed(ANM_LADDER_DOWN_START, + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.8f : mpHIO->mLadder.m.mClimbDownStartASpeed, + #else + mpHIO->mLadder.m.mClimbDownStartASpeed, + #endif + mpHIO->mLadder.m.mClimbDownStartInterp); + field_0x2f99 = 0x10; field_0x3588 = l_waitBaseAnime; dComIfGp_setPlayerStatus0(0, 0x2000000); @@ -1469,8 +1499,14 @@ int daAlink_c::procLadderDownEndInit(BOOL param_0) { anm_id = ANM_LADDER_DOWN_END_RIGHT; } - setSingleAnimeBaseSpeed(anm_id, mpHIO->mLadder.m.mClimbDownEndASpeed, - mpHIO->mLadder.m.mClimbDownEndInterp); + setSingleAnimeBaseSpeed(anm_id, + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.8f : mpHIO->mLadder.m.mClimbDownEndASpeed, + #else + mpHIO->mLadder.m.mClimbDownEndASpeed, + #endif + mpHIO->mLadder.m.mClimbDownEndInterp); + field_0x2f99 = 14; setSpecialGravity(0.0f, maxFallSpeed, 0); @@ -1602,12 +1638,22 @@ int daAlink_c::procLadderMove() { f32 daAlink_c::getClimbMoveUpDownAnmSpeed() { return getAnmSpeedStickRate(mpHIO->mLadder.m.mWallVerticalMinAnmSpeed, - mpHIO->mLadder.m.mWallVerticalMaxAnmSpeed); + #if TARGET_PC + dusk::settings::game::fastClimbing ? 1.875f : mpHIO->mLadder.m.mWallVerticalMaxAnmSpeed + #else + mpHIO->mLadder.m.mWallVerticalMaxAnmSpeed + #endif + ); } f32 daAlink_c::getClimbMoveSideAnmSpeed() { return getAnmSpeedStickRate(mpHIO->mLadder.m.mWallHorizontalMinAnmSpeed, - mpHIO->mLadder.m.mWallHorizontalMaxAnmSpeed); + #if TARGET_PC + dusk::settings::game::fastClimbing ? 2.0f : mpHIO->mLadder.m.mWallHorizontalMaxAnmSpeed + #else + mpHIO->mLadder.m.mWallHorizontalMaxAnmSpeed + #endif + ); } BOOL daAlink_c::checkClimbCode(cBgS_PolyInfo& i_polyinfo) { diff --git a/src/d/actor/d_a_alink_hvyboots.inc b/src/d/actor/d_a_alink_hvyboots.inc index af00c28b1b..fd9848d85c 100644 --- a/src/d/actor/d_a_alink_hvyboots.inc +++ b/src/d/actor/d_a_alink_hvyboots.inc @@ -6,8 +6,6 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_tag_magne.h" -#include "dusk/imgui/ImGuiMenuEnhancements.hpp" - void daAlink_c::concatMagneBootMtx() { if (checkMagneBootsOn()) { mDoMtx_stack_c::concat(mMagneBootMtx); @@ -351,7 +349,7 @@ int daAlink_c::procMagneBootsFly() { */ if (dComIfG_Bgsp().ChkPolySafe(mPolyInfo2) #if TARGET_PC - && (dusk::settings::enhancements::RestoreWiiGlitches.getValue() || checkEquipHeavyBoots()) + && (dusk::settings::game::restoreWiiGlitches || checkEquipHeavyBoots()) #elif PLATFORM_GCN || VERSION == VERSION_WII_KOR && checkEquipHeavyBoots() #endif diff --git a/src/d/actor/d_a_e_ym.cpp b/src/d/actor/d_a_e_ym.cpp index a5544b4041..0b87d419e3 100644 --- a/src/d/actor/d_a_e_ym.cpp +++ b/src/d/actor/d_a_e_ym.cpp @@ -16,6 +16,8 @@ #include "f_op/f_op_camera_mng.h" #include +#include "dusk/settings.h" + class daE_YM_HIO_c: public JORReflexible { public: daE_YM_HIO_c(); @@ -1066,7 +1068,11 @@ void daE_YM_c::executeDown() { } if (mAcch.ChkGroundHit()) { if (mFlyType != 1) { + #if TARGET_PC + bckSet(6, 0, 0.0f, dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f); + #else bckSet(6, 0, 0.0f, 1.0f); + #endif } if (mMode == 1) { mSound.startCreatureSound(Z2SE_EN_YM_LAND, 0, -1); @@ -1086,7 +1092,11 @@ void daE_YM_c::executeDown() { if (current.pos.y < gnd_cross) { mSound.startCreatureSound(Z2SE_CM_BODYFALL_WATER_M, 0, -1); mSound.startCreatureSound(Z2SE_EN_YM_MOGAKU, 0, -1); + #if TARGET_PC + bckSet(6, 0, 0.0f, dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f); + #else bckSet(6, 0, 0.0f, 1.0f); + #endif speedF = 0.0f; speed.y = gravity = 0.0f; current.pos.y = gnd_cross; @@ -1102,8 +1112,13 @@ void daE_YM_c::executeDown() { f32 gnd_cross_2 = dComIfG_Bgsp().GroundCross(&gnd_chk); if (gnd_cross_2 == -G_CM3D_F_INF || std::abs(gnd_cross_2 - current.pos.y) > 1000.0f || dComIfG_Bgsp().GetGroundCode(gnd_chk) == 4 || dComIfG_Bgsp().GetGroundCode(gnd_chk) == 10 - || dComIfG_Bgsp().GetGroundCode(gnd_chk) == 5) { + || dComIfG_Bgsp().GetGroundCode(gnd_chk) == 5) + { + #if TARGET_PC + bckSet(6, 0, 0.0f, dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0 ? 2.0f : 1.0f); + #else bckSet(6, 0, 0.0f, 1.0f); + #endif mMode = 3; speedF = 0.0f; shape_angle.x = -0x8000; diff --git a/src/d/actor/d_a_npc_henna.cpp b/src/d/actor/d_a_npc_henna.cpp index 59f13ceabe..339d5ee82c 100644 --- a/src/d/actor/d_a_npc_henna.cpp +++ b/src/d/actor/d_a_npc_henna.cpp @@ -2709,6 +2709,14 @@ static int daNpc_Henna_Create(fopAc_ac_c* i_this) { } i_this->attention_info.flags = (fopAc_AttnFlag_SPEAK_e | fopAc_AttnFlag_TALK_e); a_this->action = 0; + +#if AVOID_UB + a_this->field_0x654 = 0; + a_this->field_0x658 = 0; + a_this->field_0x662 = 0; + a_this->field_0x664 = 0; +#endif + fopAcM_SetMtx(i_this, a_this->mpMorf->getModel()->getBaseTRMtx()); lbl_82_bss_90 = 0; if (a_this->arg0 == 1) { diff --git a/src/d/actor/d_a_obj_drop.cpp b/src/d/actor/d_a_obj_drop.cpp index 64fc1f537d..1fb93a4270 100644 --- a/src/d/actor/d_a_obj_drop.cpp +++ b/src/d/actor/d_a_obj_drop.cpp @@ -20,6 +20,8 @@ #include "d/actor/d_a_e_ymb.h" #include "f_op/f_op_camera_mng.h" +#include "dusk/settings.h" + #if DEBUG daObjDrop_HIO_c l_HIO; #endif @@ -261,8 +263,18 @@ int daObjDrop_c::modeParentWait() { } mModeAction = 1; + +#if TARGET_PC + mModeTimer = dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0 ? 20 : 40; + if (dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0) { + current.pos.y += 100.0f; + } else { + current.pos.y += 300.0f; + } +#else mModeTimer = 40; current.pos.y += 300.0f; +#endif mSound.startSound(Z2SE_OBJ_LIGHTDROP_APPEAR, 0, -1); break; case 1: @@ -272,7 +284,11 @@ int daObjDrop_c::modeParentWait() { break; case 2: createBodyEffect(); +#if TARGET_PC + mModeTimer = dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0 ? 5 : 45; +#else mModeTimer = 45; +#endif mModeAction = 0; setMode(MODE_WAIT_e); break; @@ -281,6 +297,18 @@ int daObjDrop_c::modeParentWait() { return 1; } +#if TARGET_PC +static inline BOOL checkGetCargoRide() { + if ((daPy_getPlayerActorClass()->checkCargoCarry() && strcmp(dComIfGp_getStartStageName(), "F_SP112") == 0) || + dComIfGs_isLightDropGetFlag(dComIfGp_getStartStageDarkArea())) + { + return true; + } + + return false; +} +#endif + int daObjDrop_c::modeWait() { daPy_py_c* pplayer = daPy_getPlayerActorClass(); @@ -302,7 +330,32 @@ int daObjDrop_c::modeWait() { case 1: case 2: case 50: + #if TARGET_PC + if (dusk::settings::game::fastTears && dComIfGp_event_getMode() == 0) { + f32 player_dist = current.pos.abs(daPy_getPlayerActorClass()->current.pos); + f32 home_dist = current.pos.abs(home.pos); + + if (checkGetCargoRide() && player_dist < 1000.0f) { + mTargetPos = pplayer->current.pos; + mTargetPos.y += 100.0f; + + f32 temp = 3000.0f - home_dist; + if (temp < 0.0f) { + temp = 0.0f; + } + + cLib_chaseF(&speedF, (temp / 3000.0f) * 10.0f * (temp / 3000.0f), 1.0f); + } else { + mTargetPos = home.pos; + cLib_chaseF(&speedF, 2.0f, 0.5f); + } + } else { + cLib_chaseF(&speedF, 7.5f, 0.4f); + } + #else cLib_chaseF(&speedF, 7.5f, 0.4f); + #endif + if (mModeAction == 1) { cLib_chasePos(¤t.pos, mTargetPos, speedF); } diff --git a/src/d/d_bg_s.cpp b/src/d/d_bg_s.cpp index f218424d30..f48c58fead 100644 --- a/src/d/d_bg_s.cpp +++ b/src/d/d_bg_s.cpp @@ -10,12 +10,14 @@ #include "d/d_bg_w.h" #include "d/d_com_inf_game.h" #include "f_op/f_op_actor_mng.h" -#include "dusk/offset_ptr.h" #include "d/d_debug_viewer.h" #include "d/d_bg_s_capt_poly.h" -#include "dusk/imgui/ImGuiConsole.hpp" +#if TARGET_PC +#include "dusk/offset_ptr.h" +#include "dusk/settings.h" +#endif #if DEBUG int g_ground_counter; @@ -619,8 +621,8 @@ static int poly_draw(dBgS_CaptPoly* capt, cBgD_Vtx_t* vtxList, int v0, int v1, i GXColor wall_color = {0, 0xFF, 0, 0xFF}; #if TARGET_PC - dusk::ImGuiMenuTools::CollisionViewSettings collisionViewSettings = dusk::g_imguiConsole.getCollisionViewSettings(); - f32 view_opacity = 255 * (collisionViewSettings.m_terrainViewOpacity / 100.0f); + const auto& collisionViewSettings = dusk::getTransientSettings().collisionView; + f32 view_opacity = 255 * (collisionViewSettings.terrainViewOpacity / 100.0f); ground_color.a = view_opacity; roof_color.a = view_opacity; wall_color.a = view_opacity; @@ -658,16 +660,16 @@ void dBgS::Draw() { cBgS::Draw(); #if TARGET_PC - #define IMGUI_TOGGLE_HIO_FLAG(status, flag) \ + #define DUSK_TOGGLE_HIO_FLAG(status, flag) \ if (status) { \ s_InsideHio.m_flags |= flag; \ } else { \ s_InsideHio.m_flags &= ~flag; \ } - dusk::ImGuiMenuTools::CollisionViewSettings collisionViewSettings = dusk::g_imguiConsole.getCollisionViewSettings(); - IMGUI_TOGGLE_HIO_FLAG(collisionViewSettings.m_enableTerrainView, dBgS_InsideHIO::FLAG_DISP_POLY_e); - IMGUI_TOGGLE_HIO_FLAG(collisionViewSettings.m_enableWireframe, dBgS_InsideHIO::FLAG_WHITE_WIRE_e); + const auto& collisionViewSettings = dusk::getTransientSettings().collisionView; + DUSK_TOGGLE_HIO_FLAG(collisionViewSettings.enableTerrainView, dBgS_InsideHIO::FLAG_DISP_POLY_e); + DUSK_TOGGLE_HIO_FLAG(collisionViewSettings.enableWireframe, dBgS_InsideHIO::FLAG_WHITE_WIRE_e); #endif if (s_InsideHio.ChkDispPoly()) { @@ -680,8 +682,7 @@ void dBgS::Draw() { f32 var_f31 = fabsf(s_InsideHio.m_p0.x); #if TARGET_PC - dusk::ImGuiMenuTools::CollisionViewSettings collisionViewSettings = dusk::g_imguiConsole.getCollisionViewSettings(); - var_f31 = collisionViewSettings.m_drawRange; + var_f31 = collisionViewSettings.drawRange; #endif min.x = player->current.pos.x - var_f31; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 6e63d8b940..6e40fede18 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -28,8 +28,6 @@ #include "d/d_debug_camera.h" #endif -#include "dusk/imgui/ImGuiMenuEnhancements.hpp" - namespace { static f32 limitf(f32 value, f32 min, f32 max) { @@ -766,7 +764,7 @@ void dCamera_c::updatePad() { var_f31 = mDoCPd_c::getSubStickX3D(mPadID); #if TARGET_PC - if (dusk::settings::enhancements::InvertCameraXAxis.getValue()) { + if (dusk::settings::game::invertCameraXAxis) { var_f31 *= -1.0f; } #endif diff --git a/src/d/d_cc_s.cpp b/src/d/d_cc_s.cpp index ab71f08aaa..2a19de3dda 100644 --- a/src/d/d_cc_s.cpp +++ b/src/d/d_cc_s.cpp @@ -9,8 +9,9 @@ #include "d/d_com_inf_game.h" #include "d/d_jnt_col.h" #include "f_op/f_op_actor_mng.h" - -#include "dusk/imgui/ImGuiConsole.hpp" +#if TARGET_PC +#include "dusk/settings.h" +#endif class dCcS_HIO : public JORReflexible { public: @@ -770,19 +771,19 @@ void dCcS::Draw() { #endif #if TARGET_PC -#define IMGUI_TOGGLE_HIO_FLAG(status, flag) \ +#define DUSK_TOGGLE_HIO_FLAG(status, flag) \ if (status) { \ s_Hio.m_flags |= flag; \ } else { \ s_Hio.m_flags &= ~flag; \ } - dusk::ImGuiMenuTools::CollisionViewSettings collisionViewSettings = dusk::g_imguiConsole.getCollisionViewSettings(); - IMGUI_TOGGLE_HIO_FLAG(collisionViewSettings.m_enableAtView, dCcS_HIO::FLAG_AT_ON_e); - IMGUI_TOGGLE_HIO_FLAG(collisionViewSettings.m_enableTgView, dCcS_HIO::FLAG_TG_ON_e); - IMGUI_TOGGLE_HIO_FLAG(collisionViewSettings.m_enableCoView, dCcS_HIO::FLAG_CO_ON_e); + const auto& collisionViewSettings = dusk::getTransientSettings().collisionView; + DUSK_TOGGLE_HIO_FLAG(collisionViewSettings.enableAtView, dCcS_HIO::FLAG_AT_ON_e); + DUSK_TOGGLE_HIO_FLAG(collisionViewSettings.enableTgView, dCcS_HIO::FLAG_TG_ON_e); + DUSK_TOGGLE_HIO_FLAG(collisionViewSettings.enableCoView, dCcS_HIO::FLAG_CO_ON_e); - f32 view_opacity = 255 * (collisionViewSettings.m_colliderViewOpacity / 100.0f); + f32 view_opacity = 255 * (collisionViewSettings.colliderViewOpacity / 100.0f); #endif if (s_Hio.CheckAtOn()) { diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index e772098eae..3cb4811ab1 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -32,7 +32,7 @@ #include #include #if TARGET_PC -#include "dusk/imgui/ImGuiConsole.hpp" +#include "dusk/settings.h" #endif static void GxXFog_set(); @@ -11381,7 +11381,7 @@ void dKy_bg_MAxx_proc(void* bg_model_p) { C_MTXLightPerspective(sp1D8, dComIfGd_getView()->fovy, camera_p->view.aspect, 1.0f, 1.0f, #if TARGET_PC - dusk::settings::enhancements::UseWaterProjectionOffset.getValue() ? -0.01f : 0.0f, 0.0f); + dusk::settings::game::useWaterProjectionOffset ? -0.01f : 0.0f, 0.0f); #else -0.01f, 0.0f); #endif diff --git a/src/d/d_s_name.cpp b/src/d/d_s_name.cpp index 5f22177654..df512d8cb2 100644 --- a/src/d/d_s_name.cpp +++ b/src/d/d_s_name.cpp @@ -17,6 +17,13 @@ #include "m_Do/m_Do_main.h" #include "f_op/f_op_overlap_mng.h" #include "dusk/memory.h" +#include "dusk/settings.h" + +#if TARGET_PC +#define SHOW_TV_SETTINGS_SCREEN (this->mShowTvSettingsScreen) +#else +#define SHOW_TV_SETTINGS_SCREEN (1) +#endif static dSn_HIO_c g_snHIO; @@ -293,13 +300,27 @@ void dScnName_c::FileSelectMain() { } void dScnName_c::FileSelectMainNormal() { +#if TARGET_PC + mShowTvSettingsScreen = !dusk::settings::game::hideTvSettingsScreen; +#endif + switch(dFs_c->isSelectEnd()) { case 1: - mWaitTimer = 15; - mDoGph_gInf_c::setFadeColor(*(JUtility::TColor*)&g_blackColor); - mDoGph_gInf_c::startFadeOut(15); + if (SHOW_TV_SETTINGS_SCREEN) { + mWaitTimer = 15; + mDoGph_gInf_c::setFadeColor(*(JUtility::TColor*)&g_blackColor); + mDoGph_gInf_c::startFadeOut(15); + } else { + mWaitTimer = 1; + } + mProc = dScnName_PROC_FileSelectClose; field_0x420 = 1; + + if (!SHOW_TV_SETTINGS_SCREEN) { + mDoAud_seStart(Z2SE_ENTER_GAME, NULL, 0, 0); + } + break; } } @@ -308,12 +329,17 @@ void dScnName_c::FileSelectClose() { mWaitTimer--; if (mWaitTimer == 0) { - mProc = dScnName_PROC_BrightCheckOpen; - mWaitTimer = 15; - mDrawProc = 1; - mDoGph_gInf_c::setFadeColor(*(JUtility::TColor*)&g_blackColor); - mDoGph_gInf_c::startFadeIn(15); - field_0x420 = 0; + if (SHOW_TV_SETTINGS_SCREEN) { + mProc = dScnName_PROC_BrightCheckOpen; + mWaitTimer = 15; + mDrawProc = 1; + mDoGph_gInf_c::setFadeColor(*(JUtility::TColor*)&g_blackColor); + mDoGph_gInf_c::startFadeIn(15); + field_0x420 = 0; + } else { + doPreLoadSetup(); + field_0x420 = 0; + } } } @@ -330,24 +356,34 @@ void dScnName_c::brightCheck() { mBrightCheck->_move(); if (mBrightCheck->isEnd()) { - dComIfGs_setSaveTotalTime(dComIfGs_getTotalTime()); - dComIfGs_setSaveStartTime(OSGetTime()); - mDoAud_bgmStop(45); - - field_0x41f = 0; - mProc = dScnName_PROC_ChangeGameScene; - - // Reset rupee "first-time collection" flags so the collection cutscene will play again - dComIfGs_offItemFirstBit(dItemNo_GREEN_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_BLUE_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_YELLOW_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_RED_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_PURPLE_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_ORANGE_RUPEE_e); - dComIfGs_offItemFirstBit(dItemNo_SILVER_RUPEE_e); + doPreLoadSetup(); } } +void dScnName_c::doPreLoadSetup() { + dComIfGs_setSaveTotalTime(dComIfGs_getTotalTime()); + dComIfGs_setSaveStartTime(OSGetTime()); + mDoAud_bgmStop(45); + + field_0x41f = 0; + mProc = dScnName_PROC_ChangeGameScene; + + #if TARGET_PC + if (dusk::settings::game::disableRupeeCutscenes) { + return; + } + #endif + + // Reset rupee "first-time collection" flags so the collection cutscene will play again + dComIfGs_offItemFirstBit(dItemNo_GREEN_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_BLUE_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_YELLOW_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_RED_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_PURPLE_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_ORANGE_RUPEE_e); + dComIfGs_offItemFirstBit(dItemNo_SILVER_RUPEE_e); +} + void dScnName_c::changeGameScene() { if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) { dComIfGs_gameStart(); diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index 06b92994db..e9977bc534 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -25,6 +25,8 @@ #include "lingcod/lingcod.h" #endif +#include "dusk/settings.h" + static u8 dSv_item_rename(u8 i_itemNo) { switch (i_itemNo) { case dItemNo_OIL_BOTTLE_2_e: @@ -111,11 +113,23 @@ u16 dSv_player_status_a_c::getRupeeMax() const { if (mWalletSize < 3) { // if you make this a default, it wont match. Compiler, pls. switch (mWalletSize) { case WALLET: + #if TARGET_PC + return dusk::settings::game::biggerWallets ? 500 : 300; + #else return 300; + #endif case BIG_WALLET: + #if TARGET_PC + return dusk::settings::game::biggerWallets ? 1000 : 600; + #else return 600; + #endif case GIANT_WALLET: + #if TARGET_PC + return dusk::settings::game::biggerWallets ? 2000 : 1000; + #else return 1000; + #endif } } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 7b9401d394..7cf1c44cd8 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -74,19 +74,6 @@ constexpr static int PitchToSampleRate(u16 value) { return static_cast(static_cast(SampleRate) * value / 4096); } -static void UpdateSampleRate(const JASDsp::TChannel& channel, ChannelAuxData& aux) { - auto sampleRate = PitchToSampleRate(channel.mPitch); - - const SDL_AudioSpec spec = { - SDL_AUDIO_S16, - 1, - sampleRate - }; - - SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr); - aux.prevPitch = channel.mPitch; -} - /** * Reset state for a DSP channel between independent playbacks. */ @@ -98,8 +85,9 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { aux.hist0 = 0; aux.hist1 = 0; - SDL_ClearAudioStream(aux.resampleStream); - UpdateSampleRate(channel, aux); + aux.decodeBufCount = 0; + aux.resamplePos = 0.0; + aux.resamplePrev = 0; for (auto& volume : aux.prevVolume) { volume = NAN; @@ -196,15 +184,16 @@ static void ReadSampleData( } /** - * Read a single *contiguous* chunk of sample data from a channel, - * writes the samples to the channel's resampler stream. + * Read a single *contiguous* chunk of sample data from a channel into outBuf * - * @returns Amount of samples actually read. Can be greater than the amount requested. + * @returns Amount of samples written to outBuf. May be less than desiredSamples */ static int ReadChannelSamplesChunk( JASDsp::TChannel& channel, ChannelAuxData& aux, - int desiredSamples) { + int desiredSamples, + s16* outBuf, + int outBufSize) { assert(desiredSamples >= 0); @@ -249,61 +238,49 @@ static int ReadChannelSamplesChunk( channel.mSamplesLeft -= renderSamples; channel.mSamplePosition += renderSamples; - SDL_PutAudioStreamData( - aux.resampleStream, - renderData + skipSamples, - static_cast(renderSize - skipSamples * sizeof(u16))); + int outputCount = static_cast(renderSamples - skipSamples); + + // this should never be hit with the limits on pitch shift (i think) but just in case!! + outputCount = std::min(outputCount, outBufSize); + if (outputCount > 0) { + memcpy(outBuf, renderData + skipSamples, outputCount * sizeof(s16)); + } assert(curSamplePosition % channel.mSamplesPerBlock == 0 || channel.mSamplesLeft == 0); - return static_cast(renderSamples - skipSamples); + return outputCount; } /** - * Reads new audio channels from a DSP channel and writes them to the resampler stream. + * Fill decodeBuf with at least `needed` samples, fewer may be written if the channel has no loop and its data ends */ -static void SDLCALL ReadChannelSamples( - void *userdata, - SDL_AudioStream*, - int additional_amount, - int) { - - if (additional_amount == 0) { - return; - } - - const auto index = static_cast(reinterpret_cast(userdata)); - auto& channel = JASDsp::CH_BUF[index]; - auto& aux = ChannelAux[index]; - - if (channel.mSamplesLeft == 0 && !channel.mLoopFlag) { - // May get called when we're out of data to read. - // This is expected, as we need to drain the resampler channel before we mark the channel as finished. - return; - } - - auto samplesRead = ReadChannelSamplesChunk(channel, aux, additional_amount); - additional_amount -= samplesRead; - - if (channel.mSamplesLeft == 0) { - // Reached end of buffer. - if (!channel.mLoopFlag) { - return; +static void FillDecodeBuf(JASDsp::TChannel& channel, ChannelAuxData& aux, int needed) { + while (aux.decodeBufCount < needed) { + if (channel.mSamplesLeft == 0) { + if (!channel.mLoopFlag) { + // we aren't a looping channel and there's no samples left, we out of this fuckin loop + break; + } else { + // we are looping, handle loop logic + channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; + channel.mSamplePosition = channel.mLoopStartSample; + aux.hist1 = channel.mpPenult; + aux.hist0 = channel.mpLast; + } } - channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; - channel.mSamplePosition = channel.mLoopStartSample; + int remainingDecodeSpace = ChannelAuxData::DECODE_BUF_SIZE - aux.decodeBufCount; + if (remainingDecodeSpace == 0) { + break; + } - aux.hist1 = channel.mpPenult; - aux.hist0 = channel.mpLast; + aux.decodeBufCount += ReadChannelSamplesChunk( + channel, aux, std::min(remainingDecodeSpace, needed - aux.decodeBufCount), + aux.decodeBuf + aux.decodeBufCount, remainingDecodeSpace + ); } - if (additional_amount >= 0) { - ReadChannelSamplesChunk(channel, aux, additional_amount); - } - - channel.mAramStreamPosition = channel.mWaveAramAddress - + ConvertSamplesToDataLength(channel, channel.mSamplePosition); + channel.mAramStreamPosition = channel.mWaveAramAddress + ConvertSamplesToDataLength(channel, channel.mSamplePosition); } /** @@ -422,57 +399,70 @@ static void RenderOutputChannel( prevVolume = targetVolume; } +/** + * Fetch, decode, resample, output + */ static void RenderChannel( JASDsp::TChannel& channel, ChannelAuxData& channelAux, OutputSubframe& subframe) { + if (channel.mResetFlag) { ResetChannel(channel, channelAux); - } else if (channelAux.prevPitch != channel.mPitch) { - UpdateSampleRate(channel, channelAux); } - DspSubframe audioLoadBuffer = {}; + // how many input samples we step per output sample, aka the resampling ratio + f32 step = (f32)PitchToSampleRate(channel.mPitch) / SampleRate; - int wantRead = sizeof(audioLoadBuffer); - auto read = SDL_GetAudioStreamData( - channelAux.resampleStream, - &audioLoadBuffer, - wantRead); + // how many input samples to resample to DSP_SUBFRAME_SIZE output samples + int needed = static_cast(channelAux.resamplePos + DSP_SUBFRAME_SIZE * step) + 2; - if (read < wantRead) { + FillDecodeBuf(channel, channelAux, needed); + + // source ran dry, channel is finished + if(channelAux.decodeBufCount < needed) { channel.mIsFinished = true; } - auto hasReadSamples = std::span(audioLoadBuffer).subspan(0, wantRead / sizeof(f32)); + DspSubframe audioLoadBuffer = {}; + f64 pos = channelAux.resamplePos; + s16 prev = channelAux.resamplePrev; + s16 next = channelAux.decodeBufCount > 0 ? channelAux.decodeBuf[0] : prev; + int srcIdx = 0; + + // linear resampling and f32 conversion + for (int i = 0; i < DSP_SUBFRAME_SIZE; i++) { + audioLoadBuffer[i] = static_cast(prev + pos * (next - prev)) / 32768.0f; + pos += step; + while (pos >= 1.0) { + pos -= 1.0; + prev = next; + srcIdx++; + next = srcIdx < channelAux.decodeBufCount ? channelAux.decodeBuf[srcIdx] : prev; + } + } + + // save resampler state for the next subframe, prevents popping on pitch change + channelAux.resamplePos = pos; + channelAux.resamplePrev = prev; + + // move any remaining samples in the decode buf to the beginning + int remainingDecodeBuf = channelAux.decodeBufCount - srcIdx; + if (remainingDecodeBuf > 0) { + memmove(channelAux.decodeBuf, channelAux.decodeBuf + srcIdx, remainingDecodeBuf * sizeof(s16)); + } + + channelAux.decodeBufCount = std::max(0, remainingDecodeBuf); + + auto hasReadSamples = std::span(audioLoadBuffer).subspan(0, DSP_SUBFRAME_SIZE); static_assert(OutputSubframe::NUM_CHANNELS == 2, "Keep RenderChannel in sync!"); RenderOutputChannel(channel, channelAux, OutputChannel::LEFT, hasReadSamples, subframe); - RenderOutputChannel(channel, channelAux,OutputChannel::RIGHT, hasReadSamples, subframe); + RenderOutputChannel(channel, channelAux, OutputChannel::RIGHT, hasReadSamples, subframe); } void dusk::audio::DspInit() { - constexpr SDL_AudioSpec srcSpec = { - SDL_AUDIO_S16, - 1, - SampleRate - }; - constexpr SDL_AudioSpec dstSpec = { - SDL_AUDIO_F32, - 1, - SampleRate - }; - - for (u32 i = 0; i < DSP_CHANNELS; i++) { - auto& aux = ChannelAux[i]; - aux.resampleStream = SDL_CreateAudioStream(&srcSpec, &dstSpec); - - SDL_SetAudioStreamGetCallback( - aux.resampleStream, - ReadChannelSamples, - reinterpret_cast(static_cast(i))); - } } void dusk::audio::ApplyVolume( diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index c84bb338db..35b6ccabc9 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -26,8 +26,6 @@ namespace dusk::audio { struct ChannelAuxData { s16 hist1; s16 hist0; - SDL_AudioStream* resampleStream; - u16 prevPitch; // Used for debugging tools. u32 resetCount; @@ -43,6 +41,17 @@ namespace dusk::audio { assert(channel < OutputChannel::OutputChannel_MAX); return prevVolume[static_cast(channel)]; } + + // buffer for decoding before resampling, size is chosen based on how many input samples we would need to fetch for the highest possible pitch + // to fill one subframe of output samples after resampling + static constexpr int DECODE_BUF_SIZE = 2048; + s16 decodeBuf[DECODE_BUF_SIZE]; + int decodeBufCount; + + // 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; }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 707091b158..ae01f119f4 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -50,9 +50,10 @@ static void DisplayDspChannel(int i) { auto dolby = (channel.mAutoMixerPanDolby & 0xFF) / 127.5f; auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f; auto volume = VolumeFromU16(channel.mAutoMixerVolume); + auto pitch = channel.mPitch / 4096.0f; ImGui::Text( - "Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f)", - pan, dolby, fxMix, volume); + "Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f, pitch %f)", + pan, dolby, fxMix, volume, pitch); } else { ImGui::Text( "Bus connect: %04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f)", @@ -249,4 +250,4 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() { } ImGui::End(); -} \ No newline at end of file +} diff --git a/src/dusk/imgui/ImGuiCameraOverlay.cpp b/src/dusk/imgui/ImGuiCameraOverlay.cpp index 58b31fe00f..9d3ef60142 100644 --- a/src/dusk/imgui/ImGuiCameraOverlay.cpp +++ b/src/dusk/imgui/ImGuiCameraOverlay.cpp @@ -26,7 +26,8 @@ namespace dusk { } ImGui::SetNextWindowBgAlpha(0.65f); - ImGui::SetNextWindowSizeConstraints(ImVec2(300, 0), ImVec2(FLT_MAX, FLT_MAX)); + ImGui::SetNextWindowSizeConstraints(ImVec2(300.f * ImGuiScale(), 0), + ImVec2(FLT_MAX, FLT_MAX)); if (!ImGui::Begin("Camera Debug", nullptr, windowFlags)) { ImGui::End(); diff --git a/src/dusk/imgui/ImGuiConfig.hpp b/src/dusk/imgui/ImGuiConfig.hpp index c4ecb61d77..ee7b34ae68 100644 --- a/src/dusk/imgui/ImGuiConfig.hpp +++ b/src/dusk/imgui/ImGuiConfig.hpp @@ -5,13 +5,29 @@ #include "imgui.h" namespace dusk::config { - inline void ImguiCheckbox(const char* title, ConfigVar& var) { + inline void ImGuiCheckbox(const char* title, ConfigVar& var) { bool copy = var.getValue(); if (ImGui::Checkbox(title, ©)) { var.setValue(copy); Save(); } } + + static void ImGuiSliderFloat(const char* label, ConfigVar& var, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0) { + float val = var; + if (ImGui::SliderFloat(label, &val, v_min, v_max, format, flags)) { + var.setValue(val); + Save(); + } + } + + static void ImGuiSliderInt(const char* label, ConfigVar& var, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0) { + int val = var; + if (ImGui::SliderInt(label, &val, v_min, v_max, format, flags)) { + var.setValue(val); + Save(); + } + } } #endif // DUSK_IMGUICONFIG_HPP diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 917b00b237..6a06fce882 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,6 +15,7 @@ #include "JSystem/JUtility/JUTGamePad.h" #if _WIN32 +#define NOMINMAX #include "Windows.h" #endif @@ -21,6 +23,8 @@ using namespace std::string_literals; using namespace std::string_view_literals; namespace dusk { + float ImGuiScale() { return ImGui::GetIO().DisplayFramebufferScale.x; } + void ImGuiStringViewText(std::string_view text) { // begin()/end() do not work on MSVC ImGui::TextUnformatted(text.data(), text.data() + text.size()); @@ -47,7 +51,7 @@ namespace dusk { ImVec2 workSize = viewport->WorkSize; ImVec2 windowPos; ImVec2 windowPosPivot; - constexpr float padding = 10.0f; + const float padding = 10.0f * ImGuiScale(); windowPos.x = (corner & 1) != 0 ? (workPos.x + workSize.x - padding) : (workPos.x + padding); windowPos.y = (corner & 2) != 0 ? (workPos.y + workSize.y - padding) : (workPos.y + padding); windowPosPivot.x = (corner & 1) != 0 ? 1.0f : 0.0f; @@ -172,25 +176,38 @@ namespace dusk { ImGuiConsole::ImGuiConsole() {} - void ImGuiConsole::draw() { + void ImGuiConsole::PreDraw() { + if (!m_isLaunchInitialized) { + m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); + m_isLaunchInitialized = true; + } + if (CheckMenuViewToggle(ImGuiKey_F1, m_isHidden)) { - m_menuTools.afterDraw(); + ShowToasts(); return; } + // TODO: we need to be able to render the menu bar & any overlays separately + // The code currently ties them all together, so hiding the menu hides all windows + if (ImGui::BeginMainMenuBar()) { m_menuGame.draw(); m_menuTools.draw(); m_menuEnhancements.draw(); - ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f); + ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f * ImGuiScale()); ImGuiIO& io = ImGui::GetIO(); ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate)); ImGui::EndMainMenuBar(); } + ShowToasts(); + } + + void ImGuiConsole::PostDraw() { m_menuTools.afterDraw(); + ShowPipelineProgress(); } bool ImGuiConsole::CheckMenuViewToggle(ImGuiKey key, bool& active) { @@ -223,4 +240,72 @@ namespace dusk { return "Null"sv; } } + + void ImGuiConsole::ShowToasts() { + if (m_toasts.empty()) { + return; + } + auto& toast = m_toasts.front(); + const float dt = ImGui::GetIO().DeltaTime; + toast.remain -= dt; + toast.current += dt; + + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + const ImVec2 workPos = viewport->WorkPos; + const ImVec2 workSize = viewport->WorkSize; + constexpr float padding = 10.0f; + const ImVec2 windowPos{workPos.x + workSize.x / 2, workPos.y + workSize.y - padding}; + ImGui::SetNextWindowPos(windowPos, ImGuiCond_Always, ImVec2{0.5f, 1.f}); + + const float alpha = std::min({toast.remain, toast.current, 1.f}); + ImGui::SetNextWindowBgAlpha(alpha * 0.65f); + ImVec4 textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); + textColor.w *= alpha; + ImVec4 borderColor = ImGui::GetStyleColorVec4(ImGuiCol_Border); + borderColor.w *= alpha; + ImGui::PushStyleColor(ImGuiCol_Text, textColor); + ImGui::PushStyleColor(ImGuiCol_Border, borderColor); + if (ImGui::Begin("Toast", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_NoMove)) + { + ImGuiStringViewText(toast.message); + } + ImGui::End(); + ImGui::PopStyleColor(2); + + if (toast.remain <= 0.f) { + m_toasts.pop_front(); + } + } + + void ImGuiConsole::ShowPipelineProgress() { + const auto* stats = aurora_get_stats(); + const u32 queuedPipelines = stats->queuedPipelines; + if (queuedPipelines == 0) { + return; + } + const u32 createdPipelines = stats->createdPipelines; + const u32 totalPipelines = queuedPipelines + createdPipelines; + + const auto* viewport = ImGui::GetMainViewport(); + const auto padding = viewport->WorkPos.y + 10.f; + const auto halfWidth = viewport->GetWorkCenter().x; + ImGui::SetNextWindowPos(ImVec2{halfWidth, padding}, ImGuiCond_Always, ImVec2{0.5f, 0.f}); + ImGui::SetNextWindowSize(ImVec2{halfWidth, 0.f}, ImGuiCond_Always); + ImGui::SetNextWindowBgAlpha(0.65f); + ImGui::Begin("Pipelines", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing); + const auto percent = static_cast(createdPipelines) / static_cast(totalPipelines); + const auto progressStr = fmt::format("Processing pipelines: {} / {}", createdPipelines, totalPipelines); + const auto textSize = ImGui::CalcTextSize(progressStr.data(), progressStr.data() + progressStr.size()); + ImGui::NewLine(); + ImGui::SameLine(ImGui::GetWindowWidth() / 2.f - textSize.x + textSize.x / 2.f); + ImGuiStringViewText(progressStr); + ImGui::ProgressBar(percent); + ImGui::End(); + } } diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 2cb887f904..a7c433be4d 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -2,41 +2,54 @@ #define DUSK_IMGUI_HPP #include +#include #include -#include "imgui.h" +#include "ImGuiMenuEnhancements.hpp" #include "ImGuiMenuGame.hpp" #include "ImGuiMenuTools.hpp" -#include "ImGuiMenuEnhancements.hpp" +#include "imgui.h" namespace dusk { - class ImGuiConsole { - public: - ImGuiConsole(); - void draw(); +class ImGuiConsole { +public: + ImGuiConsole(); + void PreDraw(); + void PostDraw(); - ImGuiMenuTools::CollisionViewSettings& getCollisionViewSettings() { return m_menuTools.getCollisionViewSettings(); } + static bool CheckMenuViewToggle(ImGuiKey key, bool& active); - static bool CheckMenuViewToggle(ImGuiKey key, bool& active); +private: + struct Toast { + std::string message; + float remain; + float current = 0.f; + Toast(std::string message, float duration) noexcept : message(std::move(message)), + remain(duration) {} + }; - private: - bool m_isHidden = false; + bool m_isHidden = true; + bool m_isLaunchInitialized = false; + std::deque m_toasts; + ImGuiMenuGame m_menuGame; + ImGuiMenuTools m_menuTools; + ImGuiMenuEnhancements m_menuEnhancements; - ImGuiMenuGame m_menuGame; - ImGuiMenuTools m_menuTools; - ImGuiMenuEnhancements m_menuEnhancements; - }; + void ShowToasts(); + void ShowPipelineProgress(); +}; - extern ImGuiConsole g_imguiConsole; +extern ImGuiConsole g_imguiConsole; - std::string_view backend_name(AuroraBackend backend); - std::string BytesToString(size_t bytes); - void SetOverlayWindowLocation(int corner); - bool ShowCornerContextMenu(int& corner, int avoidCorner); - void ImGuiStringViewText(std::string_view text); - void ImGuiBeginGroupPanel(const char* name, const ImVec2& size); - void ImGuiEndGroupPanel(); -} +std::string_view backend_name(AuroraBackend backend); +std::string BytesToString(size_t bytes); +void SetOverlayWindowLocation(int corner); +bool ShowCornerContextMenu(int& corner, int avoidCorner); +void ImGuiStringViewText(std::string_view text); +void ImGuiBeginGroupPanel(const char* name, const ImVec2& size); +void ImGuiEndGroupPanel(); +float ImGuiScale(); +} // namespace dusk void DuskDebugPad(); diff --git a/src/dusk/imgui/ImGuiControllerOverlay.cpp b/src/dusk/imgui/ImGuiControllerOverlay.cpp index 0da82ac878..306bace22a 100644 --- a/src/dusk/imgui/ImGuiControllerOverlay.cpp +++ b/src/dusk/imgui/ImGuiControllerOverlay.cpp @@ -13,8 +13,6 @@ namespace dusk { ImGui::TextUnformatted(text.c_str()); } - static inline float GetScale() { return ImGui::GetCurrentContext()->CurrentDpiScale; } - void ImGuiMenuGame::windowInputViewer() { if (!m_showInputViewer) { return; @@ -35,7 +33,7 @@ namespace dusk { ImGui::SetNextWindowBgAlpha(0.65f); if (ImGui::Begin("Input Viewer", nullptr, windowFlags)) { - float scale = GetScale(); + float scale = ImGuiScale(); if (!m_controllerName.empty()) { TextCenter(m_controllerName); ImGui::Separator(); diff --git a/src/dusk/imgui/ImGuiEngine.cpp b/src/dusk/imgui/ImGuiEngine.cpp new file mode 100644 index 0000000000..4e2097cf3d --- /dev/null +++ b/src/dusk/imgui/ImGuiEngine.cpp @@ -0,0 +1,201 @@ +#include "ImGuiEngine.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "dusk/logging.h" + +#ifdef IMGUI_ENABLE_FREETYPE +#include "misc/freetype/imgui_freetype.h" +#endif + +namespace dusk { +namespace { +std::string GetAssetPath(const char* assetName) { + const char* basePath = SDL_GetBasePath(); + if (basePath != nullptr && basePath[0] != '\0') { + return std::string(basePath) + "res/" + assetName; + } + return std::string("res/") + assetName; +} + +bool AssetExists(const std::string& path) { + SDL_PathInfo pathInfo{}; + return SDL_GetPathInfo(path.c_str(), &pathInfo) && pathInfo.type == SDL_PATHTYPE_FILE; +} +} // namespace + +ImFont* ImGuiEngine::fontNormal; +ImFont* ImGuiEngine::fontLarge; +ImTextureID ImGuiEngine::duskIcon; + +void ImGuiEngine_Initialize(float scale) { + ImGui::GetCurrentContext(); + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->Clear(); + + const std::string fontPath = GetAssetPath("NotoMono-Regular.ttf"); + const bool hasFontFile = AssetExists(fontPath); + + ImFontConfig fontConfig{}; + fontConfig.SizePixels = std::floor(15.f * scale); + snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), + "Noto Mono Regular, %dpx", static_cast(fontConfig.SizePixels)); + ImGuiEngine::fontNormal = + hasFontFile ? + io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) : + nullptr; + if (ImGuiEngine::fontNormal == nullptr) { + if (hasFontFile) { + DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError()); + } + ImGuiEngine::fontNormal = io.Fonts->AddFontDefault(&fontConfig); + } + + fontConfig.SizePixels = std::floor(26.f * scale); +#ifdef IMGUI_ENABLE_FREETYPE + fontConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; + snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), "Noto Mono Bold, %dpx", + static_cast(fontConfig.SizePixels)); +#else + snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), + "Noto Mono Regular, %dpx", static_cast(fontConfig.SizePixels)); +#endif + ImGuiEngine::fontLarge = + hasFontFile ? + io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) : + nullptr; + if (ImGuiEngine::fontLarge == nullptr) { + if (hasFontFile) { + DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError()); + } + ImGuiEngine::fontLarge = io.Fonts->AddFontDefault(&fontConfig); + } + + auto& style = ImGui::GetStyle(); + style = {}; // Reset sizes + style.WindowPadding = ImVec2(15, 15); + style.WindowRounding = 5.0f; + style.FrameBorderSize = 1.f; + style.FramePadding = ImVec2(5, 5); + style.FrameRounding = 4.0f; + style.ItemSpacing = ImVec2(12, 8); + style.ItemInnerSpacing = ImVec2(8, 6); + style.IndentSpacing = 25.0f; + style.ScrollbarSize = 15.0f; + style.ScrollbarRounding = 9.0f; + style.GrabMinSize = 5.0f; + style.GrabRounding = 3.0f; + style.PopupBorderSize = 1.f; + style.PopupRounding = 7.0; + style.TabBorderSize = 1.f; + style.TabRounding = 3.f; + + auto* colors = style.Colors; + colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); + colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f); + colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f); + colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f); + colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.29f, 0.55f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f); + colors[ImGuiCol_TabHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); + colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f); + colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f); + colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f); + colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); + colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); + colors[ImGuiCol_PlotHistogram] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); + colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); + colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); + + style.ScaleAllSizes(scale); +} + +Icon GetIcon() { + const std::string iconPath = GetAssetPath("icon.png"); + if (!AssetExists(iconPath)) { + return {}; + } + + SDL_Surface* loadedSurface = SDL_LoadPNG(iconPath.c_str()); + if (loadedSurface == nullptr) { + DuskLog.warn("Failed to load icon '{}': {}", iconPath, SDL_GetError()); + return {}; + } + + SDL_Surface* rgbaSurface = SDL_ConvertSurface(loadedSurface, SDL_PIXELFORMAT_RGBA32); + SDL_DestroySurface(loadedSurface); + if (rgbaSurface == nullptr) { + DuskLog.warn("Failed to convert icon '{}': {}", iconPath, SDL_GetError()); + return {}; + } + + const auto iconWidth = static_cast(rgbaSurface->w); + const auto iconHeight = static_cast(rgbaSurface->h); + const size_t rowSize = static_cast(iconWidth) * 4; + const size_t size = rowSize * static_cast(iconHeight); + auto ptr = std::make_unique(size); + for (uint32_t row = 0; row < iconHeight; ++row) { + const auto* src = static_cast(rgbaSurface->pixels) + + static_cast(row) * static_cast(rgbaSurface->pitch); + auto* dst = ptr.get() + static_cast(row) * rowSize; + std::memcpy(dst, src, rowSize); + } + + SDL_DestroySurface(rgbaSurface); + return Icon{ + std::move(ptr), + size, + iconWidth, + iconHeight, + }; +} + +void ImGuiEngine_AddTextures() { + auto icon = GetIcon(); + if (icon.data == nullptr || icon.width == 0 || icon.height == 0) { + ImGuiEngine::duskIcon = 0; + return; + } + + ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get()); +} +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiEngine.hpp b/src/dusk/imgui/ImGuiEngine.hpp new file mode 100644 index 0000000000..18e49c2a47 --- /dev/null +++ b/src/dusk/imgui/ImGuiEngine.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "imgui.h" +#include "misc/cpp/imgui_stdlib.h" + +namespace dusk { +class ImGuiEngine { +public: + static ImFont* fontNormal; + static ImFont* fontLarge; + static ImTextureID duskIcon; +}; + +void ImGuiEngine_Initialize(float scale); +void ImGuiEngine_AddTextures(); + +struct Icon { + std::unique_ptr data; + size_t size; + uint32_t width; + uint32_t height; +}; +Icon GetIcon(); +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiMapLoader.cpp b/src/dusk/imgui/ImGuiMapLoader.cpp index 8d11c0673b..4baf48bdab 100644 --- a/src/dusk/imgui/ImGuiMapLoader.cpp +++ b/src/dusk/imgui/ImGuiMapLoader.cpp @@ -17,7 +17,8 @@ namespace dusk { ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; ImGui::SetNextWindowBgAlpha(0.65f); - ImGui::SetNextWindowSizeConstraints(ImVec2(300, 0), ImVec2(FLT_MAX, FLT_MAX)); + ImGui::SetNextWindowSizeConstraints(ImVec2(300.f * ImGuiScale(), 0), + ImVec2(FLT_MAX, FLT_MAX)); if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) { ImGui::End(); diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index c582bc2335..347d634dc4 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -1,12 +1,8 @@ -#include "fmt/format.h" #include "imgui.h" -#include "aurora/gfx.h" -#include "ImGuiConsole.hpp" #include "ImGuiMenuEnhancements.hpp" #include "ImGuiConfig.hpp" -#include "dusk/settings.hpp" -#include +#include "dusk/settings.h" namespace dusk { ImGuiMenuEnhancements::ImGuiMenuEnhancements() {} @@ -14,26 +10,81 @@ namespace dusk { void ImGuiMenuEnhancements::draw() { if (ImGui::BeginMenu("Enhancements")) { if (ImGui::BeginMenu("Quality of Life")) { - config::ImguiCheckbox("Fast Iron Boots", settings::enhancements::FastIronBoots); - config::ImguiCheckbox("Invert Camera X Axis", settings::enhancements::InvertCameraXAxis); - config::ImguiCheckbox("Quick Transform (R+Y)", settings::enhancements::QuickTransform); + config::ImGuiCheckbox("Quick Transform (R+Y)", settings::game::enableQuickTransform); + + config::ImGuiCheckbox("Bigger Wallets", settings::game::biggerWallets); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Wallet sizes are like in the HD version (500, 1000, 2000)"); + } + + config::ImGuiCheckbox("No Rupee Returns", settings::game::noReturnRupees); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full"); + } + + config::ImGuiCheckbox("Disable Rupee Cutscenes", settings::game::disableRupeeCutscenes); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time"); + } + + config::ImGuiCheckbox("No Sword Recoil", settings::game::noSwordRecoil); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Link won't recoil when his sword hits walls"); + } + + config::ImGuiCheckbox("Faster Climbing", settings::game::fastClimbing); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version"); + } + + 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"); + } + + config::ImGuiCheckbox("Hide TV Settings Screen", settings::game::hideTvSettingsScreen); + 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); + config::ImGuiCheckbox("Invert Camera X Axis", settings::game::invertCameraXAxis); + ImGui::EndMenu(); } if (ImGui::BeginMenu("Graphics")) { - config::ImguiCheckbox("Native Bloom", settings::enhancements::EnableBloom); - config::ImguiCheckbox("Water Projection Offset", settings::enhancements::UseWaterProjectionOffset); + 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"); } - config::ImguiCheckbox("Mirror Mode", settings::enhancements::MirrorMode); ImGui::EndMenu(); } - if (ImGui::BeginMenu("Restorations")) { - config::ImguiCheckbox("Restore Wii 1.0 Glitches", settings::enhancements::RestoreWiiGlitches); + if (ImGui::BeginMenu("Cheats")) { + config::ImGuiCheckbox("Fast Iron Boots", settings::game::enableFastIronBoots); + + 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"); + } + + ImGui::EndMenu(); + } + + 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"); } @@ -41,10 +92,6 @@ namespace dusk { ImGui::EndMenu(); } - if (ImGui::BeginMenu("Cheats")) { - ImGui::EndMenu(); - } - ImGui::EndMenu(); } } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 00b287cc41..00d7b578bc 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -1,32 +1,36 @@ #include "fmt/format.h" #include "imgui.h" -#include "aurora/gfx.h" #include "ImGuiConsole.hpp" #include "ImGuiMenuGame.hpp" +#include "ImGuiConfig.hpp" #include #include "JSystem/JUtility/JUTGamePad.h" -#include "d/actor/d_a_alink.h" #include "dusk/audio/DuskAudioSystem.h" -#include "m_Do/m_Do_audio.h" +#include "dusk/hotkeys.h" +#include "dusk/settings.h" #include "m_Do/m_Do_controller_pad.h" namespace dusk { + static void ToggleFullscreen() { + settings::video::enableFullscreen.setValue(!settings::video::enableFullscreen); + VISetWindowFullscreen(settings::video::enableFullscreen); + } + ImGuiMenuGame::ImGuiMenuGame() {} void ImGuiMenuGame::draw() { if (ImGui::BeginMenu("Game")) { - if (ImGui::MenuItem("Reset", "Ctrl+R")) { + if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) { JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; } ImGui::Separator(); if (ImGui::BeginMenu("Graphics")) { - if (ImGui::MenuItem("Toggle Fullscreen", "F11")) { - m_fullscreen = !m_fullscreen; - VISetWindowFullscreen(m_fullscreen); + if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) { + ToggleFullscreen(); } ImGui::EndMenu(); @@ -34,28 +38,28 @@ namespace dusk { if (ImGui::BeginMenu("Audio")) { ImGui::Text("Master Volume"); - ImGui::SliderFloat("##m_masterVolume", &m_audioSettings.m_masterVolume, 0.0f, 1.0f, ""); + config::ImGuiSliderFloat("##masterVolume", settings::audio::masterVolume, 0.0f, 1.0f, ""); /* // TODO: implement additional settings ImGui::Text("Main Music Volume"); - ImGui::SliderFloat("##m_mainMusicVolume", &m_audioSettings.m_mainMusicVolume, 0.0f, 1.0f, ""); + ImGui::SliderFloat("##mainMusicVolume", &getSettings().audio.mainMusicVolume, 0.0f, 1.0f, ""); ImGui::Text("Sub Music Volume"); - ImGui::SliderFloat("##m_subMusicVolume", &m_audioSettings.m_subMusicVolume, 0.0f, 1.0f, ""); + ImGui::SliderFloat("##subMusicVolume", &getSettings().audio.subMusicVolume, 0.0f, 1.0f, ""); ImGui::Text("Sound Effects Volume"); - ImGui::SliderFloat("##m_soundEffectsVolume", &m_audioSettings.m_soundEffectsVolume, 0.0f, 1.0f, ""); + ImGui::SliderFloat("##soundEffectsVolume", &getSettings().audio.soundEffectsVolume, 0.0f, 1.0f, ""); ImGui::Text("Fanfare Volume"); - ImGui::SliderFloat("##m_fanfareVolume", &m_audioSettings.m_fanfareVolume, 0.0f, 1.0f, ""); + ImGui::SliderFloat("##fanfareVolume", &getSettings().audio.fanfareVolume, 0.0f, 1.0f, ""); Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface(); if (audioMgr != nullptr) { } */ - audio::SetMasterVolume(m_audioSettings.m_masterVolume); + audio::SetMasterVolume(settings::audio::masterVolume); ImGui::EndMenu(); } @@ -78,19 +82,19 @@ namespace dusk { } if (ImGui::IsKeyPressed(ImGuiKey_F11)) { - m_fullscreen = !m_fullscreen; - VISetWindowFullscreen(m_fullscreen); + ToggleFullscreen(); } } static void drawVirtualStick(const char* id, const ImVec2& stick) { - ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5, ImGui::GetCursorPos().y)); + float scale = ImGuiScale(); + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5 * scale, ImGui::GetCursorPos().y)); - ImGui::BeginChild(id, ImVec2(80, 80)); + ImGui::BeginChild(id, ImVec2(80 * scale, 80 * scale)); ImDrawList* dl = ImGui::GetWindowDrawList(); ImVec2 p = ImGui::GetCursorScreenPos(); - float radius = ImGui::GetCurrentContext()->CurrentDpiScale * 30.0f; + float radius = 30.0f * scale; ImVec2 pos = ImVec2(p.x + radius, p.y + radius); constexpr ImU32 stickGray = IM_COL32(150, 150, 150, 255); @@ -98,7 +102,7 @@ namespace dusk { constexpr ImU32 red = IM_COL32(230, 0, 0, 255); dl->AddCircleFilled(pos, radius, stickGray, 8); - dl->AddCircleFilled(ImVec2(pos.x + stick.x * (radius), pos.y + -stick.y * (radius)), 3, red); + dl->AddCircleFilled(ImVec2(pos.x + stick.x * (radius), pos.y + -stick.y * (radius)), 3 * scale, red); ImGui::EndChild(); } @@ -139,12 +143,14 @@ namespace dusk { } } + float scale = ImGuiScale(); ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize; ImGui::SetNextWindowBgAlpha(0.65f); - ImGui::SetNextWindowSizeConstraints(ImVec2(850, 400), ImVec2(850, 400)); + ImGui::SetNextWindowSizeConstraints(ImVec2(850 * scale, 400 * scale), + ImVec2(850 * scale, 400 * scale)); if (!ImGui::Begin("Controller Config", &m_showControllerConfig, windowFlags)) { ImGui::End(); @@ -225,9 +231,9 @@ namespace dusk { } // buttons panel - constexpr float uiButtonSize = 40; + const float uiButtonSize = 40 * scale; - ImGuiBeginGroupPanel("Buttons", ImVec2(150, 20)); + ImGuiBeginGroupPanel("Buttons", ImVec2(150 * scale, 20 * scale)); u32 buttonCount; PADButtonMapping* btnMappingList = PADGetButtonMappings(m_controllerConfig.m_selectedPort, &buttonCount); @@ -251,7 +257,7 @@ namespace dusk { dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(btnMappingList[i].nativeButton), i); } bool pressed = ImGui::Button(dispName.c_str(), - ImVec2(100.0f, 20.0f)); + ImVec2(100.0f * scale, 20.0f * scale)); if (pressed) { m_controllerConfig.m_isReading = true; @@ -268,7 +274,7 @@ namespace dusk { uint32_t axisCount; PADAxisMapping* axisMappingList = PADGetAxisMappings(m_controllerConfig.m_selectedPort, &axisCount); - ImGuiBeginGroupPanel("Triggers", ImVec2(150, 20)); + ImGuiBeginGroupPanel("Triggers", ImVec2(150 * scale, 20 * scale)); PADAxis triggers[] = {PAD_AXIS_TRIGGER_L, PAD_AXIS_TRIGGER_R}; if (axisMappingList != nullptr) { @@ -291,7 +297,7 @@ namespace dusk { dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger); } bool pressed = ImGui::Button(dispName.c_str(), - ImVec2(100.0f, 20.0f)); + ImVec2(100.0f * scale, 20.0f * scale)); if (pressed) { m_controllerConfig.m_isReading = true; @@ -308,7 +314,7 @@ namespace dusk { int port = m_controllerConfig.m_selectedPort; // main stick panel - ImGuiBeginGroupPanel("Control Stick", ImVec2(150, 20)); + ImGuiBeginGroupPanel("Control Stick", ImVec2(150 * scale, 20 * scale)); drawVirtualStick("##mainStick", ImVec2{ mDoCPd_c::getStickX(port), mDoCPd_c::getStickY(port) }); @@ -345,7 +351,7 @@ namespace dusk { dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis); } } - bool pressed = ImGui::Button(dispName.c_str(), ImVec2(100.0f, 20.0f)); + bool pressed = ImGui::Button(dispName.c_str(), ImVec2(100.0f * scale, 20.0f * scale)); if (pressed) { m_controllerConfig.m_isReading = true; @@ -372,7 +378,7 @@ namespace dusk { ImGui::SameLine(); // sub stick panel - ImGuiBeginGroupPanel("C Stick", ImVec2(150, 20)); + ImGuiBeginGroupPanel("C Stick", ImVec2(150 * scale, 20 * scale)); drawVirtualStick("##subStick", ImVec2{ mDoCPd_c::getSubStickX(port), mDoCPd_c::getSubStickY(port) }); @@ -409,7 +415,7 @@ namespace dusk { dispName = fmt::format("{0}##-{1}", PADGetNativeButtonName(axisMappingList[axis].nativeButton), axis); } } - bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), ImVec2(100.0f, 20.0f)); + bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), ImVec2(100.0f * scale, 20.0f * scale)); if (pressed) { m_controllerConfig.m_isReading = true; @@ -434,7 +440,7 @@ namespace dusk { ImGui::SameLine(); // Triggers Panel - ImGuiBeginGroupPanel("Triggers", ImVec2(150, 20)); + ImGuiBeginGroupPanel("Triggers", ImVec2(150 * scale, 20 * scale)); if (deadZones != nullptr) { ImGui::Text("L Threshold"); @@ -460,7 +466,7 @@ namespace dusk { ImGui::SameLine(); // Options panel - ImGuiBeginGroupPanel("Options", ImVec2(150, 20)); + ImGuiBeginGroupPanel("Options", ImVec2(150 * scale, 20 * scale)); if (deadZones != nullptr) { ImGui::Checkbox("Enable Dead Zones", &deadZones->useDeadzones); diff --git a/src/dusk/imgui/ImGuiMenuGame.hpp b/src/dusk/imgui/ImGuiMenuGame.hpp index 7df0a00973..6d7ed614aa 100644 --- a/src/dusk/imgui/ImGuiMenuGame.hpp +++ b/src/dusk/imgui/ImGuiMenuGame.hpp @@ -17,14 +17,6 @@ namespace dusk { void windowControllerConfig(); private: - struct { - float m_masterVolume = 1.0f; - float m_mainMusicVolume = 1.0f; - float m_subMusicVolume = 1.0f; - float m_soundEffectsVolume = 1.0f; - float m_fanfareVolume = 1.0f; - } m_audioSettings; - struct { int m_selectedPort = 0; bool m_isReading = false; @@ -33,8 +25,6 @@ namespace dusk { int m_pendingPort = -1; } m_controllerConfig; - bool m_fullscreen = false; - bool m_showControllerConfig = false; bool m_showInputViewer = false; diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 40bb3fa1ec..20e92768a6 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -2,6 +2,7 @@ #include "imgui.h" #include "aurora/gfx.h" +#include "dusk/hotkeys.h" #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" @@ -23,28 +24,29 @@ namespace dusk { ImGui::Separator(); + auto& collisionView = getTransientSettings().collisionView; if (ImGui::BeginMenu("Collision View")) { - ImGui::Checkbox("Enable Terrain view", &m_collisionViewSettings.m_enableTerrainView); - ImGui::Checkbox("Enable wireframe view", &m_collisionViewSettings.m_enableWireframe); - ImGui::SliderFloat("Opacity##terrain", &m_collisionViewSettings.m_terrainViewOpacity, 0.0f, 100.0f); - ImGui::SliderFloat("Draw Range", &m_collisionViewSettings.m_drawRange, 0.0f, 1000.0f); + ImGui::Checkbox("Enable Terrain view", &collisionView.enableTerrainView); + ImGui::Checkbox("Enable wireframe view", &collisionView.enableWireframe); + ImGui::SliderFloat("Opacity##terrain", &collisionView.terrainViewOpacity, 0.0f, 100.0f); + ImGui::SliderFloat("Draw Range", &collisionView.drawRange, 0.0f, 1000.0f); ImGui::Separator(); - ImGui::Checkbox("Enable Attack Collider view", &m_collisionViewSettings.m_enableAtView); - ImGui::Checkbox("Enable Target Collider view", &m_collisionViewSettings.m_enableTgView); - ImGui::Checkbox("Enable Push Collider view", &m_collisionViewSettings.m_enableCoView); - ImGui::SliderFloat("Opacity##colliders", &m_collisionViewSettings.m_colliderViewOpacity, 0.0f, 100.0f); + ImGui::Checkbox("Enable Attack Collider view", &collisionView.enableAtView); + ImGui::Checkbox("Enable Target Collider view", &collisionView.enableTgView); + ImGui::Checkbox("Enable Push Collider view", &collisionView.enableCoView); + ImGui::SliderFloat("Opacity##colliders", &collisionView.colliderViewOpacity, 0.0f, 100.0f); ImGui::EndMenu(); } - ImGui::MenuItem("Process Management", "F2", &m_showProcessManagement); - ImGui::MenuItem("Debug Overlay", "F3", &m_showDebugOverlay); - ImGui::MenuItem("Heap Viewer", "F4", &m_showHeapOverlay); - ImGui::MenuItem("Stub Log", "F5", &m_showStubLog); - ImGui::MenuItem("Debug Camera", "F6", &m_showCameraOverlay); + ImGui::MenuItem("Process Management", hotkeys::SHOW_PROCESS_MANAGEMENT, &m_showProcessManagement); + ImGui::MenuItem("Debug Overlay", hotkeys::SHOW_DEBUG_OVERLAY, &m_showDebugOverlay); + ImGui::MenuItem("Heap Viewer", hotkeys::SHOW_HEAP_VIEWER, &m_showHeapOverlay); + ImGui::MenuItem("Stub Log", hotkeys::SHOW_STUB_LOG, &m_showStubLog); + ImGui::MenuItem("Debug Camera", hotkeys::SHOW_CAMERA_DEBUG, &m_showCameraOverlay); ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader); ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo); ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor); - ImGui::MenuItem("Audio Debug", "F7", &m_showAudioDebug); + ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug); ImGui::MenuItem("OSReport Force", nullptr, &OSReportReallyForceEnable); ImGui::EndMenu(); } @@ -131,6 +133,9 @@ namespace dusk { BytesToString(stats->lastVertSize + stats->lastUniformSize + stats->lastIndexSize + stats->lastStorageSize + stats->lastTextureUploadSize))); + + // TODO: persist to config + ShowCornerContextMenu(m_debugOverlayCorner, m_cameraOverlayCorner); } ImGui::End(); } diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 66c820dd78..35604e0c93 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -10,17 +10,6 @@ namespace dusk { class ImGuiMenuTools { public: - struct CollisionViewSettings { - bool m_enableTerrainView = false; - bool m_enableWireframe = false; - bool m_enableAtView = false; - bool m_enableTgView = false; - bool m_enableCoView = false; - float m_terrainViewOpacity = 50.0f; - float m_colliderViewOpacity = 50.0f; - float m_drawRange = 100.0f; - }; - ImGuiMenuTools(); void draw(); void afterDraw(); @@ -34,11 +23,9 @@ namespace dusk { void ShowPlayerInfo(); void ShowAudioDebug(); - CollisionViewSettings& getCollisionViewSettings() { return m_collisionViewSettings; } - private: bool m_showDebugOverlay = false; - int m_debugOverlayCorner = 0; // top-left + int m_debugOverlayCorner = 2; // bottom-left bool m_showCameraOverlay = false; int m_cameraOverlayCorner = 3; @@ -67,8 +54,6 @@ namespace dusk { bool m_isDevelopmentMode = false; bool m_showPlayerInfo = false; - CollisionViewSettings m_collisionViewSettings; - bool m_showSaveEditor = false; ImGuiSaveEditor m_saveEditor; }; diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index ff04b1f1a2..a9cca636f8 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -823,7 +823,7 @@ namespace dusk { for (int e = 0; e < 255; e++) { ImGui::Text("%03d ", e); ImGui::SameLine(); - for (int i = 8; i >= 0; i--) { + for (int i = 7; i >= 0; i--) { bool flag = event.mEvent[e] & (1 << i); if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) { if (flag) diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 2be800b6a5..34bde294de 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -1,28 +1,97 @@ -#include "dusk/settings.hpp" +#include "dusk/settings.h" #include "dusk/config.hpp" -namespace dusk::settings::enhancements { - ConfigVar FastIronBoots("enhancements.fast_iron_boots", false); - ConfigVar InvertCameraXAxis("enhancements.invert_camera_x_axis", false); - ConfigVar QuickTransform("enhancements.quick_transform", false); - ConfigVar RestoreWiiGlitches("enhancements.restore_wii_glitches", false); - ConfigVar EnableBloom("enhancements.enable_bloom", true); - ConfigVar UseWaterProjectionOffset("enhancements.use_water_projection_offset", false); - ConfigVar MirrorMode("enhancements.mirror_mode", false); - - void Register() { - Register(FastIronBoots); - Register(InvertCameraXAxis); - Register(QuickTransform); - Register(RestoreWiiGlitches); - Register(EnableBloom); - Register(UseWaterProjectionOffset); - Register(MirrorMode); - } -} - namespace dusk::settings { - void Register() { - enhancements::Register(); - } +namespace video { +ConfigVar enableFullscreen("video.enableFullscreen", false); +} + +namespace audio { +ConfigVar masterVolume("audio.masterVolume", 1.0f); +ConfigVar mainMusicVolume("audio.mainMusicVolume", 1.0f); +ConfigVar subMusicVolume("audio.subMusicVolume", 1.0f); +ConfigVar soundEffectsVolume("audio.soundEffectsVolume", 1.0f); +ConfigVar fanfareVolume("audio.fanfareVolume", 1.0f); +} + +namespace game { +// Quality of Life +ConfigVar enableQuickTransform("game.enableQuickTransform", false); +ConfigVar hideTvSettingsScreen("game.hideTvSettingsScreen", false); +ConfigVar biggerWallets("game.biggerWallets", false); +ConfigVar noReturnRupees("game.noReturnRupees", false); +ConfigVar disableRupeeCutscenes("game.disableRupeeCutscenes", false); +ConfigVar noSwordRecoil("game.noSwordRecoil", false); +ConfigVar damageMultiplier("game.damageMultiplier", 1); +ConfigVar instantDeath("game.instantDeath", false); +ConfigVar fastClimbing("game.fastClimbing", false); +ConfigVar fastTears("game.fastTears", false); + +// Preferences +ConfigVar enableMirrorMode("game.enableMirrorMode", false); +ConfigVar invertCameraXAxis("game.invertCameraXAxis", false); + +// Graphics +ConfigVar enableBloom("game.enableBloom", true); +ConfigVar useWaterProjectionOffset("game.useWaterProjectionOffset", false); + +// Cheats +ConfigVar enableFastIronBoots("game.enableFastIronBoots", false); + +// Technical +ConfigVar restoreWiiGlitches("game.restoreWiiGlitches", false); +} + +void Register() { + // Video + Register(video::enableFullscreen); + + // Audio + Register(audio::masterVolume); + Register(audio::mainMusicVolume); + Register(audio::subMusicVolume); + Register(audio::soundEffectsVolume); + Register(audio::fanfareVolume); + + // Game + Register(game::enableQuickTransform); + Register(game::hideTvSettingsScreen); + Register(game::biggerWallets); + Register(game::noReturnRupees); + Register(game::disableRupeeCutscenes); + Register(game::noSwordRecoil); + Register(game::damageMultiplier); + Register(game::instantDeath); + Register(game::fastClimbing); + Register(game::fastTears); + Register(game::enableMirrorMode); + Register(game::invertCameraXAxis); + Register(game::enableBloom); + Register(game::useWaterProjectionOffset); + Register(game::enableFastIronBoots); + Register(game::restoreWiiGlitches); +} +} + +namespace dusk { + +// Transient settings + +static TransientSettings g_transientSettings = { + .collisionView = { + .enableTerrainView = false, + .enableWireframe = false, + .enableAtView = false, + .enableTgView = false, + .enableCoView = false, + .terrainViewOpacity = 50.0f, + .colliderViewOpacity = 50.0f, + .drawRange = 100.0f, + }, +}; + +TransientSettings& getTransientSettings() { + return g_transientSettings; +} + } diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 8ecc4bbb8f..7ee50fc0ea 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1163,7 +1163,7 @@ void mDoGph_gInf_c::bloom_c::remove() { void mDoGph_gInf_c::bloom_c::draw() { #if TARGET_PC - if (!dusk::settings::enhancements::EnableBloom.getValue()) { + if (!dusk::settings::game::enableBloom) { return; } #endif @@ -1634,6 +1634,10 @@ int mDoGph_Painter() { if (wn != 0) sDiagLoggedWindow = true; } +#if TARGET_PC + dusk::g_imguiConsole.PreDraw(); +#endif + #if DEBUG drawHeapMap(); #endif @@ -2113,7 +2117,7 @@ int mDoGph_Painter() { #endif #if TARGET_PC - if (dusk::settings::enhancements::MirrorMode.getValue()) + if (dusk::settings::game::enableMirrorMode) #elif PLATFORM_WII if (data_8053a730) #endif @@ -2244,7 +2248,7 @@ int mDoGph_Painter() { #endif #if TARGET_PC - dusk::g_imguiConsole.draw(); + dusk::g_imguiConsole.PostDraw(); #endif mDoGph_gInf_c::endRender(); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index b8d6fe9728..ba86f17d79 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -48,6 +48,7 @@ #include "dusk/logging.h" #include "dusk/time.h" #include "dusk/main.h" +#include "dusk/imgui/ImGuiEngine.hpp" #include #include @@ -57,7 +58,6 @@ #include "cxxopts.hpp" #include "dusk/config.hpp" -#include "dusk/settings.hpp" // --- GLOBALS --- s8 mDoMain::developmentMode = -1; @@ -158,6 +158,9 @@ void main01(void) { case AURORA_WINDOW_RESIZED: mDoGph_gInf_c::setWindowSize(event->windowSize); break; + case AURORA_DISPLAY_SCALE_CHANGED: + dusk::ImGuiEngine_Initialize(event->windowSize.scale); + break; case AURORA_EXIT: goto exit; } @@ -227,6 +230,10 @@ static AuroraBackend ParseAuroraBackend(const std::string& value) { exit(1); } +static void aurora_imgui_init_callback(const AuroraWindowSize* size) { + dusk::ImGuiEngine_Initialize(size->scale); +} + static void ApplyCVarOverrides(const cxxopts::OptionValue& option) { if (option.count() == 0) { return; @@ -261,6 +268,7 @@ static void ApplyCVarOverrides(const cxxopts::OptionValue& option) { // ========================================================================= int game_main(int argc, char* argv[]) { dusk::settings::Register(); + dusk::config::FinishRegistration(); cxxopts::ParseResult parsed_arg_options; @@ -301,12 +309,12 @@ int game_main(int argc, char* argv[]) { config.windowWidth = 608 * 2; config.windowHeight = 448 * 2; config.desiredBackend = ParseAuroraBackend(parsed_arg_options["backend"].as()); - config.configPath = "."; config.logCallback = &aurora_log_callback; config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as(); config.mem1Size = 256 * 1024 * 1024; config.mem2Size = 24 * 1024 * 1024; config.allowJoystickBackgroundEvents = true; + config.imGuiInitCallback = &aurora_imgui_init_callback; auroraInfo = aurora_initialize(argc, argv, &config);