diff --git a/files.cmake b/files.cmake index e1f808c408..7a180c496e 100644 --- a/files.cmake +++ b/files.cmake @@ -1334,6 +1334,7 @@ set(DUSK_FILES include/dusk/endian_gx.hpp include/dusk/config.hpp include/dusk/dvd_asset.hpp + include/dusk/scope_guard.hpp src/dusk/dvd_asset.cpp src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index 53228af84b..9980ab776f 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4551,7 +4551,7 @@ public: #if TARGET_PC void handleWolfHowl(); void handleQuickTransform(); - bool checkGyroAimItemContext(); + bool checkGyroAimContext(); #endif }; // Size: 0x385C diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h index 4ee1c61737..279aeae16e 100644 --- a/include/dusk/gyro.h +++ b/include/dusk/gyro.h @@ -4,7 +4,7 @@ namespace dusk::gyro { void read(float dt); void getAimDeltas(float& out_yaw, float& out_pitch); -bool queryGyroAimItemContext(); +bool queryGyroAimContext(); void rollgoalTick(bool play_active, s16 camera_yaw); void rollgoalTableOffset(s16& out_ax, s16& out_az); diff --git a/include/dusk/scope_guard.hpp b/include/dusk/scope_guard.hpp new file mode 100644 index 0000000000..281d23434d --- /dev/null +++ b/include/dusk/scope_guard.hpp @@ -0,0 +1,20 @@ +#ifndef DUSK_SCOPE_GUARD_HPP +#define DUSK_SCOPE_GUARD_HPP + +#include + +class SimpleScopeGuard { +public: + // Store the function in the constructor + explicit SimpleScopeGuard(const std::function& func) : m_func(func) {} + + // Run the function when the object goes out of scope + ~SimpleScopeGuard() { + if (m_func) m_func(); + } + +private: + std::function m_func; +}; + +#endif //DUSK_SCOPE_GUARD_HPP diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 2676c461cd..3b3c33bb2f 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -61,6 +61,7 @@ struct UserSettings { ConfigVar noMissClimbing; ConfigVar fastTears; ConfigVar instantSaves; + ConfigVar instantText; ConfigVar sunsSong; // Preferences diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index 9b937d3678..045a9655bd 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -143,12 +143,14 @@ void daAlink_c::handleQuickTransform() { procCoMetamorphoseInit(); } -bool daAlink_c::checkGyroAimItemContext() { - if (checkWolf()) { - return false; - } - +bool daAlink_c::checkGyroAimContext() { switch (mProcID) { + case PROC_SUBJECTIVITY: + case PROC_SWIM_SUBJECTIVITY: + case PROC_HORSE_SUBJECTIVITY: + case PROC_CANOE_SUBJECTIVITY: + case PROC_BOARD_SUBJECTIVITY: + case PROC_WOLF_ROPE_SUBJECTIVITY: case PROC_BOW_SUBJECT: case PROC_BOOMERANG_SUBJECT: case PROC_COPY_ROD_SUBJECT: diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 6de02a7598..b278d866fc 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -130,7 +130,7 @@ BOOL daAlink_c::setBodyAngleToCamera() { } #if TARGET_PC - if (dusk::getSettings().game.enableGyroAim && checkGyroAimItemContext()) { + if (dusk::getSettings().game.enableGyroAim && checkGyroAimContext()) { f32 gyro_scale = 1.0f; if (checkWolfEyeUp()) { gyro_scale *= 0.6f; diff --git a/src/d/actor/d_a_npc_inko.cpp b/src/d/actor/d_a_npc_inko.cpp index 3c277085f6..4d55a31f8e 100644 --- a/src/d/actor/d_a_npc_inko.cpp +++ b/src/d/actor/d_a_npc_inko.cpp @@ -110,6 +110,10 @@ static int daNpc_Inko_Execute(npc_inko_class* i_this) { } f32 var_f31; + + #if AVOID_UB + var_f31 = 0.0f; + #endif if (i_this->field_0x598 == 0) { if (i_this->field_0x59c[1] == 0) { i_this->field_0x59c[1] = 30.0f + cM_rndF(70.0f); diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index debea084dc..37085a6bb9 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -12,6 +12,10 @@ #include "d/d_lib.h" #include "JSystem/JUtility/JUTFont.h" +#if TARGET_PC +#include "dusk/scope_guard.hpp" +#endif + #if REGION_JPN #define CHAR_CODE_MALE_ICON 0x8189 #define CHAR_CODE_FEMALE_ICON 0x818A @@ -1918,6 +1922,11 @@ void jmessage_tSequenceProcessor::do_begin(void const* pEntry, char const* pszTe pReference->resetReference(); field_0xb5 = 0; +#if TARGET_PC + if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) { + field_0xb2 = 1; + } +#endif } void jmessage_tSequenceProcessor::do_end() { @@ -2154,6 +2163,18 @@ void jmessage_tSequenceProcessor::do_character(int iCharacter) { bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_size) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); +#if TARGET_PC + // This class runs the lambda function when it goes out of scope. We want to run + // this code after the switch statement and this saves us from having to litter + // the switch statement with IF_DUSK before every return. + auto instantTextRun = SimpleScopeGuard([&]() { + if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) { + field_0xb2 = 1; + pReference->setSendTimer(0); + } + }); +#endif + switch (i_tag & 0xFF0000) { case MSGTAG_GROUP(1): { cXyz pos = pReference->getActorPos(); diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 3676639908..488af685db 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -28,6 +28,10 @@ #include "m_Do/m_Do_lib.h" #include "JSystem/JKernel/JKRExpHeap.h" +#if TARGET_PC +#include "dusk/settings.h" +#endif + static void dMsgObject_addFundRaising(s16 param_0); static void dMsgObject_addTotalPayment(s16 param_0); @@ -1566,7 +1570,8 @@ u8 dMsgObject_c::isSend() { if (pRef->getSendFlag() == 5) { if (getStatusLocal() == 21) { setButtonStatusLocal(); - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } return 0; @@ -1585,7 +1590,8 @@ u8 dMsgObject_c::isSend() { } if (pRef->getSendFlag() == 2) { setButtonStatusLocal(); - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } } @@ -1598,7 +1604,8 @@ u8 dMsgObject_c::isSend() { return 2; } } else { - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } if (mesgCancelButton) { diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index e3de48bcfc..aa62d6a98a 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -2,6 +2,7 @@ #include #include "f_op/f_op_camera_mng.h" +#include "m_Do/m_Do_graphic.h" namespace { enum class Op : uint8_t { @@ -84,6 +85,7 @@ struct CameraSnapshot { f32 aspect{}; f32 near_{}; f32 far_{}; + bool wideZoom{}; bool valid{}; }; @@ -347,6 +349,9 @@ void begin_record() { return; } else { copy_view_to_snap(&s_cam_prev, cam->view); +#if WIDESCREEN_SUPPORT + s_cam_prev.wideZoom = s_cam_curr.valid ? s_cam_curr.wideZoom : false; +#endif } } @@ -502,6 +507,9 @@ void record_camera(::camera_process_class* cam, int camera_id) { return; } copy_view_to_snap(&s_cam_curr, cam->view); +#if WIDESCREEN_SUPPORT + s_cam_curr.wideZoom = mDoGph_gInf_c::isWideZoom(); +#endif } void begin_presentation_camera() { @@ -545,6 +553,13 @@ void begin_presentation_camera() { view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; + // FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the next sim frame, but I don't care enough to right now +#if WIDESCREEN_SUPPORT + if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ? s_cam_curr.wideZoom : s_cam_prev.wideZoom) { + mDoGph_gInf_c::onWideZoom(); + } +#endif + // FRAME INTERP TODO: Largely copied from d_camera's camera_draw function from this point, got any better ideas? C_MTXPerspective(view->projMtx, view->fovy, view->aspect, view->near_, view->far_); mDoMtx_lookAt(view->viewMtx, &view->lookat.eye, &view->lookat.center, &view->lookat.up, view->bank); @@ -601,6 +616,10 @@ void begin_presentation_camera() { mDoLib_clipper::setup(view->fovy, view->aspect, view->near_, far_); +#if WIDESCREEN_SUPPORT + mDoGph_gInf_c::offWideZoom(); +#endif + s_presentation_depth = 1; } diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp index 0905bd59fd..d390c9c8b4 100644 --- a/src/dusk/gyro.cpp +++ b/src/dusk/gyro.cpp @@ -35,7 +35,7 @@ bool s_sensor_keep_alive = false; bool get_sensor_keep_alive() { return s_sensor_keep_alive; } void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } -bool queryGyroAimItemContext() { +bool queryGyroAimContext() { if (!static_cast(dusk::getSettings().game.enableGyroAim)) { return false; } @@ -45,11 +45,11 @@ bool queryGyroAimItemContext() { return false; } - return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); + return link->checkGyroAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); } void read(float dt) { - if (!s_sensor_keep_alive && !(dusk::getSettings().game.enableGyroAim && queryGyroAimItemContext())) { + if (!s_sensor_keep_alive && !queryGyroAimContext()) { if (s_sensor_enabled) { PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE); s_sensor_enabled = false; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index ee6cb88932..8b4881d797 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -371,10 +371,16 @@ namespace dusk { m_menuRandomizer.windowRandoGeneration(); DuskDebugPad(); // temporary, remove later - // Only show cursor when menu or any windows are open - if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) { - ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; - // Imgui will re-show cursor. + // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. + ImGuiIO& io = ImGui::GetIO(); + if (showMenu) { + mouseHideTimer = 0.0f; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. + } else if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { + mouseHideTimer = 0.0f; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. + } else if (mouseHideTimer <= 3.0f) { + mouseHideTimer += ImGui::GetIO().DeltaTime; } else { ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; SDL_HideCursor(); diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 39c3131fe6..ecbbf1b4c8 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -39,6 +39,8 @@ private: remain(duration) {} }; + float mouseHideTimer = 0.0f; + bool m_isHidden = true; bool m_isLaunchInitialized = false; bool m_touchTapActive = false; diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index d47b4e8d4b..a494f81771 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -65,6 +65,11 @@ namespace dusk { ImGui::SetTooltip("Skip the delay when writing to the Memory Card."); } + config::ImGuiCheckbox("Hold B for Instant Text", getSettings().game.instantText); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Make text scroll immediately by holding B."); + } + config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Prevents Link from playing a struggle animation\n" @@ -144,8 +149,10 @@ namespace dusk { config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n" - "Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); + ImGui::SetTooltip("Enables the gyroscope on supported controllers\n" + "while in look mode (C-Up) and while aiming the\n" + "Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s),\n" + "Ball and Chain, and Dominion Rod."); } config::ImGuiCheckbox("Gyro Rollgoal", getSettings().game.enableGyroRollgoal); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e4cace9ff0..cc33fc12a3 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -35,6 +35,7 @@ UserSettings g_userSettings = { .noMissClimbing {"game.noMissClimbing", false}, .fastTears {"game.fastTears", false}, .instantSaves {"game.instantSaves", false}, + .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, // Preferences @@ -120,6 +121,7 @@ void registerSettings() { Register(g_userSettings.game.fastClimbing); Register(g_userSettings.game.fastTears); Register(g_userSettings.game.instantSaves); + Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis);