From 5f33489465bc3d2da0de6daa7b7eabe6878d674f Mon Sep 17 00:00:00 2001 From: Irastris Date: Mon, 27 Apr 2026 01:04:45 -0400 Subject: [PATCH 01/28] Register interp callback for d_a_obj_lv8Lift --- include/d/actor/d_a_obj_lv8Lift.h | 3 +++ src/d/actor/d_a_obj_lv8Lift.cpp | 41 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/d/actor/d_a_obj_lv8Lift.h b/include/d/actor/d_a_obj_lv8Lift.h index c5a8ae9f27..c327f4cf3c 100644 --- a/include/d/actor/d_a_obj_lv8Lift.h +++ b/include/d/actor/d_a_obj_lv8Lift.h @@ -58,6 +58,9 @@ public: void setNextPoint(); int Draw(); int Delete(); +#if TARGET_PC + friend void daL8Lift_interp_callback(bool isSimFrame, void* pUserWork); +#endif u8 getPthID() { return fopAcM_GetParamBit(this, 0, 8); } u8 getMoveSpeed() { return fopAcM_GetParamBit(this, 8, 4); } diff --git a/src/d/actor/d_a_obj_lv8Lift.cpp b/src/d/actor/d_a_obj_lv8Lift.cpp index 4f475a626c..8fa9422325 100644 --- a/src/d/actor/d_a_obj_lv8Lift.cpp +++ b/src/d/actor/d_a_obj_lv8Lift.cpp @@ -10,6 +10,10 @@ #include "d/d_path.h" #include "d/d_bg_w.h" +#if TARGET_PC +#include "dusk/frame_interpolation.h" +#endif + daL8Lift_HIO_c::daL8Lift_HIO_c() { mStopDisappearTime = 30; mStartMoveTime = 60; @@ -380,7 +384,44 @@ void daL8Lift_c::setNextPoint() { mCurrentPoint = next_point; } +#if TARGET_PC +void daL8Lift_interp_callback(bool isSimFrame, void* pUserWork) { + daL8Lift_c* lift = static_cast(pUserWork); + if (lift == NULL || lift->mpModel == NULL) { + return; + } + + g_env_light.settingTevStruct(0x10, &lift->current.pos, &lift->tevStr); + g_env_light.setLightTevColorType_MAJI(lift->mpModel, &lift->tevStr); + + J3DModelData* modelData = lift->mpModel->getModelData(); + J3DMaterial* materialp = modelData->getMaterialNodePointer(0); + + if (materialp->getTexGenBlock()->getTexMtx(1) != NULL) { + J3DTexMtxInfo* mtx_info = &materialp->getTexGenBlock()->getTexMtx(1)->getTexMtxInfo(); + if (mtx_info != NULL) { + Mtx m; + C_MTXLightOrtho(m, 100.0f, -100.0f, -100.0f, 100.0f, 1.0f, 1.0f, 0.0f, 0.0f); + mDoMtx_stack_c::XrotS(0x4000); + mDoMtx_stack_c::transM(-lift->current.pos.x, -lift->current.pos.y, -lift->current.pos.z); + cMtx_concat(m, mDoMtx_stack_c::get(), mtx_info->mEffectMtx); + } + } + + lift->mBtk.entry(modelData); + + J3DGXColor* color = materialp->getTevKColor(1); + color->r = l_HIO.mColorR; + color->g = l_HIO.mColorG; + color->b = l_HIO.mColorB; +} +#endif + int daL8Lift_c::Draw() { +#if TARGET_PC + dusk::frame_interp::add_interpolation_callback(&daL8Lift_interp_callback, this); +#endif + g_env_light.settingTevStruct(16, ¤t.pos, &tevStr); g_env_light.setLightTevColorType_MAJI(mpModel, &tevStr); J3DModelData* modelData = mpModel->getModelData(); From 9b259143be4e50316d5d415d1566e180ce3f3392 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Mon, 27 Apr 2026 12:05:26 -0400 Subject: [PATCH 02/28] Better Freecam code + Allow Freecam in Target mode --- src/d/d_camera.cpp | 53 +++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index c59cf1c525..471c93bb19 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -794,16 +794,15 @@ void dCamera_c::updatePad() { if (mTriggerLeftLast > mCamSetup.ManualEndVal()) { if (mLockLActive == 0) { + #if TARGET_PC + mCamParam.mManualMode = 0; + #endif mLockLJustActivated = 1; } else { mLockLJustActivated = 0; } mLockLActive = 1; - - #if TARGET_PC - mCamParam.mManualMode = 0; - #endif } else { mLockLJustActivated = 0; mLockLActive = 0; @@ -1178,12 +1177,6 @@ bool dCamera_c::Run() { } else { sp0F = (this->*engine_tbl[mCamParam.Algorythmn(mCamStyle)])(mCamStyle); - #if TARGET_PC - if (mCamParam.Algorythmn(mCamStyle) != 1) { - mCamParam.mManualMode = 0; - } - #endif - field_0x170++; field_0x160++; mCurCamStyleTimer++; @@ -3527,6 +3520,12 @@ void dCamera_c::checkGroundInfo() { } bool dCamera_c::chaseCamera(s32 param_0) { +#if TARGET_PC + if (freeCamera()) { + return 1; + } +#endif + static f32 JumpCushion = 0.9f; f32 charge_latitude = mCamSetup.ChargeLatitude(); int charge_timer = mCamSetup.ChargeTimer(); @@ -4631,10 +4630,6 @@ bool dCamera_c::chaseCamera(s32 param_0) { sp110 = mViewCache.mDirection.R(); mViewCache.mDirection.R(mViewCache.mDirection.R() + (fVar55 - mViewCache.mDirection.R()) * chase->field_0x74); - #if TARGET_PC - freeCamera(); - #endif - chase->field_0x64 = mViewCache.mCenter + mViewCache.mDirection.Xyz(); mViewCache.mEye = chase->field_0x64; @@ -7482,6 +7477,9 @@ bool dCamera_c::freeCamera() { return false; } + mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree(); + mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree(); + cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f}; f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY); @@ -7498,14 +7496,29 @@ bool dCamera_c::freeCamera() { mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity * 4.0f; } - if (mCamParam.mManualMode) { - mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, -35.0f, 60.0f); - mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle); - mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle); - mViewCache.mDirection.mRadius = std::clamp((mCamParam.freeYAngle + 35.0f) * 10.0f, 300.0f, 10000.0f); + if (!mCamParam.mManualMode) { + return false; } - return mCamParam.mManualMode; + f32 minYAngle = -10.0f; + f32 maxAngle = 50.0f; + + mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, minYAngle, maxAngle); + mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle); + mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle); + f32 currentLerp = (mCamParam.freeYAngle - minYAngle) / (maxAngle - minYAngle); + mViewCache.mDirection.mRadius = std::lerp(200.0f, 1000.0f, currentLerp); + + cXyz finalCenter = mpPlayerActor->current.pos; + finalCenter.y += mIsWolf ? 90.0f : 100.0f; + mViewCache.mCenter = finalCenter; + + cXyz finalEye = finalCenter + mViewCache.mDirection.Xyz(); + mViewCache.mEye = finalEye; + + mViewCache.mFovy = 60.0f; + + return true; } #endif From d662db69f0a565e1f296f76eba42e0d160bd90e0 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Mon, 27 Apr 2026 17:04:42 -0400 Subject: [PATCH 03/28] Fix bugs with Disable Main HUD --- src/d/d_meter2.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/d/d_meter2.cpp b/src/d/d_meter2.cpp index 5bd321e7a8..fa6774d0a5 100644 --- a/src/d/d_meter2.cpp +++ b/src/d/d_meter2.cpp @@ -316,6 +316,12 @@ int dMeter2_c::_execute() { } int dMeter2_c::_draw() { + #if TARGET_PC + if (dusk::getSettings().game.disableMainHUD) { + return 1; + } + #endif + if (mpMap != NULL) { mpMap->_draw(); } @@ -424,12 +430,6 @@ void dMeter2_c::setLifeZero() { void dMeter2_c::checkStatus() { mStatus = 0; - #if TARGET_PC - if (dusk::getSettings().game.disableMainHUD) { - mStatus |= 0xF0000000; - } - #endif - field_0x12c = field_0x128; field_0x128 = daPy_py_c::checkNowWolf(); From 0c1372f98694b85e654141c4cb80c00ef6491ba6 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 27 Apr 2026 21:55:57 -0600 Subject: [PATCH 04/28] Revert "Kinda crappy initial controller support" This reverts commit 23a91a37be19b596e9bbd03e513309b84a62c67a. --- extern/aurora | 2 +- src/dusk/imgui/ImGuiConsole.cpp | 33 +--------- src/dusk/imgui/ImGuiConsole.hpp | 1 - src/dusk/imgui/ImGuiMenuGame.cpp | 100 +++++-------------------------- src/dusk/imgui/ImGuiMenuGame.hpp | 3 - 5 files changed, 19 insertions(+), 120 deletions(-) diff --git a/extern/aurora b/extern/aurora index 7784b6fc95..a6a3d3a65a 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 7784b6fc95568551499c87bd093b78d86e194eba +Subproject commit a6a3d3a65ae0de6de8b60629cf47fd0f446c21cb diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index a94c7d1f5b..6bca83482e 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -56,21 +56,6 @@ ImGuiWindow* FindDragScrollWindow(ImGuiWindow* window) { } return nullptr; } - -void FocusLastMenuBarItem() { - ImGuiContext& g = *ImGui::GetCurrentContext(); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - const ImGuiID itemId = g.LastItemData.ID; - if (window == nullptr || itemId == 0) { - return; - } - - ImGui::FocusWindow(window); - ImGui::SetNavID(itemId, ImGuiNavLayer_Menu, g.CurrentFocusScopeId, - ImGui::WindowRectAbsToRel(window, g.LastItemData.NavRect)); - ImGui::SetNavCursorVisibleAfterMove(); - g.NavHighlightItemUnderNav = true; -} } // namespace namespace dusk { @@ -344,17 +329,7 @@ namespace dusk { } m_isHidden = !getSettings().backend.duskMenuOpen; - if (dusk::IsGameLaunched) { - if (ImGui::IsKeyPressed(ImGuiKey_F1)) { - m_isHidden = !m_isHidden; - } - if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack)) { - m_isHidden = !m_isHidden; - m_focusMenuBar = !m_isHidden; - } - } - - bool showMenu = !dusk::IsGameLaunched || !m_isHidden; + bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden); if (dusk::IsGameLaunched) { const bool menuOpen = !m_isHidden; if (getSettings().backend.duskMenuOpen != menuOpen) { @@ -368,10 +343,6 @@ namespace dusk { ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); if (showMenu && ImGui::BeginMainMenuBar()) { m_menuGame.draw(); - if (m_focusMenuBar) { - FocusLastMenuBarItem(); - m_focusMenuBar = false; - } m_menuTools.draw(); const auto fpsLabel = @@ -396,7 +367,7 @@ namespace dusk { if (dusk::IsGameLaunched && !m_isLaunchInitialized) { m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ? "Tap to toggle menu"s : - "Press F1 or Minus/Back to toggle menu"s, + "Press F1 to toggle menu"s, 2.5f); m_isLaunchInitialized = true; if (getSettings().game.liveSplitEnabled) { diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index fc7b4c51bc..a6c8b48fc7 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -41,7 +41,6 @@ private: float mouseHideTimer = 0.0f; bool m_isHidden = true; - bool m_focusMenuBar = false; bool m_isLaunchInitialized = false; bool m_touchTapActive = false; bool m_touchTapMoved = false; diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 251fabd227..d0da643b0c 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -23,15 +23,6 @@ namespace { constexpr int kInternalResolutionScaleMax = 12; - -bool is_controller_neutral(int port) { - if (port < 0) { - return true; - } - - return PADGetNativeButtonPressed(port) == -1 && - PADGetNativeAxisPulled(port).nativeAxis == -1; -} } // namespace namespace aurora::gx { @@ -205,7 +196,7 @@ namespace dusk { ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n" "the first released version."); } - + config::ImGuiCheckbox("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Enables rotating Link in the collection menu with the C-Stick"); @@ -651,90 +642,39 @@ namespace dusk { void ImGuiMenuGame::windowControllerConfig() { if (!m_showControllerConfig) { - if (m_controllerConfig.m_isReading || - m_controllerConfig.m_suppressRemapActivationUntilRelease) - { - m_controllerConfig.m_isReading = false; - m_controllerConfig.m_pendingButtonMapping = nullptr; - m_controllerConfig.m_pendingAxisMapping = nullptr; - m_controllerConfig.m_pendingPort = -1; - m_controllerConfig.m_waitForInputRelease = false; - m_controllerConfig.m_suppressRemapActivationUntilRelease = false; - m_controllerConfig.m_suppressRemapActivationPort = -1; - PADBlockInput(false); - } return; } - bool suppressRemapActivationThisFrame = m_controllerConfig.m_suppressRemapActivationUntilRelease; - if (m_controllerConfig.m_suppressRemapActivationUntilRelease && - is_controller_neutral(m_controllerConfig.m_suppressRemapActivationPort)) - { - m_controllerConfig.m_suppressRemapActivationUntilRelease = false; - m_controllerConfig.m_suppressRemapActivationPort = -1; - PADBlockInput(false); - } - - if ((m_controllerConfig.m_pendingButtonMapping != nullptr || - m_controllerConfig.m_pendingAxisMapping != nullptr) && - m_controllerConfig.m_waitForInputRelease) - { - m_controllerConfig.m_waitForInputRelease = - !is_controller_neutral(m_controllerConfig.m_pendingPort); - } - // if pending for a button mapping, check to set new input - if (m_controllerConfig.m_pendingButtonMapping != nullptr && - !m_controllerConfig.m_waitForInputRelease) - { + if (m_controllerConfig.m_pendingButtonMapping != nullptr) { s32 nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort); if (nativeButton != -1) { - const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingButtonMapping->nativeButton = nativeButton; m_controllerConfig.m_pendingButtonMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - m_controllerConfig.m_isReading = false; - m_controllerConfig.m_waitForInputRelease = false; - m_controllerConfig.m_suppressRemapActivationUntilRelease = true; - m_controllerConfig.m_suppressRemapActivationPort = suppressPort; - suppressRemapActivationThisFrame = true; - PADBlockInput(true); + PADBlockInput(false); PADSerializeMappings(); } } // if pending for an axis mapping, check to set new input - if (m_controllerConfig.m_pendingAxisMapping != nullptr && - !m_controllerConfig.m_waitForInputRelease) - { + if (m_controllerConfig.m_pendingAxisMapping != nullptr) { auto nativeAxis = PADGetNativeAxisPulled(m_controllerConfig.m_pendingPort); if (nativeAxis.nativeAxis != -1) { - const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingAxisMapping->nativeAxis = nativeAxis; m_controllerConfig.m_pendingAxisMapping->nativeButton = -1; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - m_controllerConfig.m_isReading = false; - m_controllerConfig.m_waitForInputRelease = false; - m_controllerConfig.m_suppressRemapActivationUntilRelease = true; - m_controllerConfig.m_suppressRemapActivationPort = suppressPort; - suppressRemapActivationThisFrame = true; - PADBlockInput(true); + PADBlockInput(false); PADSerializeMappings(); } else { auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort); if (nativeButton != -1) { - const int suppressPort = m_controllerConfig.m_pendingPort; m_controllerConfig.m_pendingAxisMapping->nativeAxis = {-1, AXIS_SIGN_POSITIVE}; m_controllerConfig.m_pendingAxisMapping->nativeButton = nativeButton; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - m_controllerConfig.m_isReading = false; - m_controllerConfig.m_waitForInputRelease = false; - m_controllerConfig.m_suppressRemapActivationUntilRelease = true; - m_controllerConfig.m_suppressRemapActivationPort = suppressPort; - suppressRemapActivationThisFrame = true; - PADBlockInput(true); + PADBlockInput(false); PADSerializeMappings(); } } @@ -770,10 +710,6 @@ namespace dusk { m_controllerConfig.m_pendingButtonMapping = nullptr; m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; - m_controllerConfig.m_waitForInputRelease = false; - m_controllerConfig.m_isReading = false; - m_controllerConfig.m_suppressRemapActivationUntilRelease = false; - m_controllerConfig.m_suppressRemapActivationPort = -1; PADBlockInput(false); } @@ -850,7 +786,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingButtonMapping == &btnMappingList[i]) { - dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", btnName); + dispName = fmt::format("Press a Key...##{}", btnName); } else { const char* nativeName = GetNameForGamepadButton(gamepad, btnMappingList[i].nativeButton); if (nativeName == nullptr) { @@ -861,11 +797,10 @@ namespace dusk { bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { + if (pressed) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingButtonMapping = &btnMappingList[i]; - m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -895,18 +830,17 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[trigger]) { - dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", axisName); + dispName = fmt::format("Press a Key...##{}", axisName); } else { dispName = fmt::format("{0}##-{1}", PADGetNativeAxisName(axisMappingList[trigger].nativeAxis), trigger); } bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { + if (pressed) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[trigger]; - m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -963,7 +897,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) { - dispName = fmt::format("{}##{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label); + dispName = fmt::format("Press a Key...##{}", label); } else { if (axisMappingList[axis].nativeAxis.nativeAxis != -1) { const char* signStr; @@ -982,11 +916,10 @@ namespace dusk { } bool pressed = ImGui::Button(dispName.c_str(), btnSize); - if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { + if (pressed) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis]; - m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -1027,7 +960,7 @@ namespace dusk { std::string dispName; if (m_controllerConfig.m_isReading && m_controllerConfig.m_pendingAxisMapping == &axisMappingList[axis]) { - dispName = fmt::format("{}##sub{}", m_controllerConfig.m_waitForInputRelease ? "Release..." : "Press a Key...", label); + dispName = fmt::format("Press a Key...##sub{}", label); } else { if (axisMappingList[axis].nativeAxis.nativeAxis != -1) { const char* signStr; @@ -1046,11 +979,10 @@ namespace dusk { } bool pressed = ImGui::Button(fmt::format("{0}##sub{1}", dispName, label).c_str(), btnSize); - if (pressed && !m_controllerConfig.m_isReading && !suppressRemapActivationThisFrame) { + if (pressed) { m_controllerConfig.m_isReading = true; m_controllerConfig.m_pendingPort = m_controllerConfig.m_selectedPort; m_controllerConfig.m_pendingAxisMapping = &axisMappingList[axis]; - m_controllerConfig.m_waitForInputRelease = true; PADBlockInput(true); } } @@ -1081,7 +1013,7 @@ namespace dusk { PADSerializeMappings(); } } - + if (PADSupportsRumbleIntensity(m_controllerConfig.m_selectedPort)) { ImGuiBeginGroupPanel("Rumble Intensity", ImVec2(150 * scale, -1)); u16 low; @@ -1100,7 +1032,7 @@ namespace dusk { if (ImGui::Button(fmt::format("{0}...##rumbleTest", m_controllerConfig.m_isRumbling ? "Stop": "Test").c_str(), {-1, 0})) { PADControlMotor(m_controllerConfig.m_selectedPort, !m_controllerConfig.m_isRumbling ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP_HARD); m_controllerConfig.m_isRumbling ^= 1; - } + } ImGuiEndGroupPanel(); } ImGuiEndGroupPanel(); diff --git a/src/dusk/imgui/ImGuiMenuGame.hpp b/src/dusk/imgui/ImGuiMenuGame.hpp index c902c92359..e54541a010 100644 --- a/src/dusk/imgui/ImGuiMenuGame.hpp +++ b/src/dusk/imgui/ImGuiMenuGame.hpp @@ -68,9 +68,6 @@ namespace dusk { PADButtonMapping* m_pendingButtonMapping = nullptr; PADAxisMapping* m_pendingAxisMapping = nullptr; int m_pendingPort = -1; - bool m_waitForInputRelease = false; - bool m_suppressRemapActivationUntilRelease = false; - int m_suppressRemapActivationPort = -1; bool m_isRumbling = false; } m_controllerConfig; From 30b7087f30f67de26f94868508e163977eea7ce0 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Mon, 27 Apr 2026 22:04:42 -0700 Subject: [PATCH 05/28] add option for only fishing once for sera's cat --- extern/aurora | 2 +- include/dusk/settings.h | 1 + src/d/actor/d_a_npc_ne.cpp | 5 ++--- src/dusk/imgui/ImGuiFirstRunPreset.cpp | 1 + src/dusk/imgui/ImGuiMenuGame.cpp | 5 +++++ src/dusk/settings.cpp | 2 ++ 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/extern/aurora b/extern/aurora index a6a3d3a65a..7784b6fc95 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit a6a3d3a65ae0de6de8b60629cf47fd0f446c21cb +Subproject commit 7784b6fc95568551499c87bd093b78d86e194eba diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 2acc69e43c..e426e301fa 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -76,6 +76,7 @@ struct UserSettings { ConfigVar fastClimbing; ConfigVar noMissClimbing; ConfigVar fastTears; + ConfigVar no2ndFishForCat; ConfigVar instantSaves; ConfigVar instantText; ConfigVar sunsSong; diff --git a/src/d/actor/d_a_npc_ne.cpp b/src/d/actor/d_a_npc_ne.cpp index 1324c56cf5..90e33adea5 100644 --- a/src/d/actor/d_a_npc_ne.cpp +++ b/src/d/actor/d_a_npc_ne.cpp @@ -956,7 +956,7 @@ static void npc_ne_tame(npc_ne_class* i_this) { i_this->mpMorf->setPlaySpeed(i_this->mAnmSpeed); /* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */ - if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) { + if (IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) { if (fpcEx_Search(s_fish_sub, _this) != NULL) { i_this->mAction = npc_ne_class::ACT_HOME; i_this->mMode = 10; @@ -2948,8 +2948,7 @@ static int daNpc_Ne_Execute(npc_ne_class* i_this) { if (i_this->mWantsFish && (i_this->mCounter & 0xf) == 0) { /* dSv_event_flag_c::F_0470 - Fishing Pond - Reserved for fishing */ - if (dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470]) - && i_this->mDistToTarget < 1500.0f) { + if ((IF_DUSK(dusk::getSettings().game.no2ndFishForCat) || dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[470])) && i_this->mDistToTarget < 1500.0f) { if (fopAcM_SearchByName(fpcNm_MG_ROD_e) != NULL) { i_this->mNoFollow = false; } else { diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp index 2a9bcc5c6e..b728b8cd24 100644 --- a/src/dusk/imgui/ImGuiFirstRunPreset.cpp +++ b/src/dusk/imgui/ImGuiFirstRunPreset.cpp @@ -30,6 +30,7 @@ static void ApplyPresetHD() { s.game.biggerWallets.setValue(true); s.game.invertCameraXAxis.setValue(true); s.game.freeCamera.setValue(true); + s.game.no2ndFishForCat.setValue(true); } static void ApplyPresetDusk() { diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index d0da643b0c..deced34b8e 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -267,6 +267,11 @@ namespace dusk { ImGui::SetTooltip("Link won't recoil when his sword hits walls."); } + config::ImGuiCheckbox("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Only need to fish once for Sera's cat to return."); + } + config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save."); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index ee6ba663ca..2eac6d5a19 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -36,6 +36,7 @@ UserSettings g_userSettings = { .fastClimbing {"game.fastClimbing", false}, .noMissClimbing {"game.noMissClimbing", false}, .fastTears {"game.fastTears", false}, + .no2ndFishForCat {"game.no2ndFishForCat", false}, .instantSaves {"game.instantSaves", false}, .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, @@ -147,6 +148,7 @@ void registerSettings() { Register(g_userSettings.game.instantDeath); Register(g_userSettings.game.fastClimbing); Register(g_userSettings.game.fastTears); + Register(g_userSettings.game.no2ndFishForCat); Register(g_userSettings.game.instantSaves); Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); From ea528ed9d9bdb79489e5a696f8a700dac9e0f153 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 03:41:04 -0700 Subject: [PATCH 06/28] clear single achievement and fix achievement tab moving around --- include/dusk/achievements.h | 1 + src/dusk/achievements.cpp | 12 ++++++++++++ src/dusk/imgui/ImGuiAchievements.cpp | 18 +++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/include/dusk/achievements.h b/include/dusk/achievements.h index cd4294b6f1..fb2da89c69 100644 --- a/include/dusk/achievements.h +++ b/include/dusk/achievements.h @@ -40,6 +40,7 @@ public: void save(); void tick(); void clearAll(); + void clearOne(const char* key); std::vector getAchievements() const; bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); } diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 071332a7c2..99d4648b98 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -426,6 +426,18 @@ void AchievementSystem::clearAll() { save(); } +void AchievementSystem::clearOne(const char* key) { + for (auto& e : m_entries) { + if (std::string(e.achievement.key) == key) { + e.achievement.progress = 0; + e.achievement.unlocked = false; + e.extra = {}; + break; + } + } + save(); +} + void AchievementSystem::processEntry(Entry& e) { if (e.achievement.unlocked) { return; diff --git a/src/dusk/imgui/ImGuiAchievements.cpp b/src/dusk/imgui/ImGuiAchievements.cpp index 4b844b8386..479e307e30 100644 --- a/src/dusk/imgui/ImGuiAchievements.cpp +++ b/src/dusk/imgui/ImGuiAchievements.cpp @@ -131,7 +131,7 @@ void ImGuiAchievements::draw(bool& open) { continue; } - const std::string tabLabel = fmt::format("{} ({}/{})", catInfo.label, catUnlocked, catTotal); + const std::string tabLabel = fmt::format("{} ({}/{})###{}", catInfo.label, catUnlocked, catTotal, catInfo.label); ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color); const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str()); @@ -152,6 +152,7 @@ void ImGuiAchievements::draw(bool& open) { continue; } ImGui::PushID(a.key); + ImGui::BeginGroup(); ImGui::PushStyleColor( ImGuiCol_Text, @@ -190,6 +191,21 @@ void ImGuiAchievements::draw(bool& open) { ImGui::PopStyleColor(); } + ImGui::EndGroup(); + + if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("##ctx"); + } + + if (ImGui::BeginPopup("##ctx")) { + ImGui::TextDisabled("%s", a.name); + ImGui::Separator(); + if (ImGui::MenuItem("Clear Achievement")) { + AchievementSystem::get().clearOne(a.key); + } + ImGui::EndPopup(); + } + ImGui::Spacing(); ImGui::PopID(); } From 0d37cb4e5420a9cb61a28f00babeb09ed1528dd5 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 04:01:22 -0700 Subject: [PATCH 07/28] thank you berry much achievement --- src/dusk/achievements.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 99d4648b98..4a7dac5e4d 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -46,6 +46,21 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "plumm_max", + "Thank You Berry Much", + "Score 61,454 points in the Plumm minigame.", + AchievementCategory::Minigame, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (dComIfGs_getBalloonScore() >= 61454) { + a.progress = 1; + } + }, + {} + }, { { "rollgoal_8", From 92391d5eb87a1053fd99722a3f2870ceccb3189b Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Tue, 28 Apr 2026 04:06:29 -0700 Subject: [PATCH 08/28] add overview and ai notice to readme --- README.md | 6 ++++++ extern/aurora | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b6d6cec863..a66a423c5e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ - ### **[Official Website](https://twilitrealm.dev)** - ### **[Discord](https://discord.gg/QACynxeyna)** +# Overview +Dusk is a reverse-engineered reimplementation of Twilight Princess. +It aims to be as accurate as possible to the original while also providing new options, enhancements, and tools to customize your experience. + # Setup **⚠️ Dusk does NOT provide any copyrighted assets. You must provide your own copy of the game.** @@ -27,5 +31,7 @@ First make sure your dump of the game is clean and supported by Dusk. You can do # Building If you'd like to build Dusk from source, please read the [build instructions](docs/building.md). +Pull Requests are welcomed! Note that we do not accept contributions that are primarily AI generated and will close your PR if we suspect as much. + # Credits Special thanks to the [TP decompilation](https://github.com/zeldaret/tp) team, the GC/Wii decompilation community, the [Aurora](https://github.com/encounter/aurora) developers, the [TP speedrunning community](https://zsrtp.link), and all [contributors](https://github.com/TwilitRealm/dusk/graphs/contributors). diff --git a/extern/aurora b/extern/aurora index 7784b6fc95..8a2b80ecb1 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 7784b6fc95568551499c87bd093b78d86e194eba +Subproject commit 8a2b80ecb104625319c2818fd6d752bab8617679 From 47863b34c260f7d86dd0e194b063e1e8db32b407 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 05:54:09 -0700 Subject: [PATCH 09/28] achievement signal system --- include/dusk/achievements.h | 8 ++++++++ src/d/actor/d_a_obj_carry.cpp | 9 +++++++++ src/dusk/achievements.cpp | 25 +++++++++++++++++++++++++ src/dusk/imgui/ImGuiAchievements.cpp | 1 + 4 files changed, 43 insertions(+) diff --git a/include/dusk/achievements.h b/include/dusk/achievements.h index fb2da89c69..ca4676ab2d 100644 --- a/include/dusk/achievements.h +++ b/include/dusk/achievements.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "nlohmann/json.hpp" @@ -14,6 +16,7 @@ enum class AchievementCategory : uint8_t { Collection, Challenge, Minigame, + Misc, Glitched }; @@ -42,6 +45,10 @@ public: void clearAll(); void clearOne(const char* key); + // Signals are visible to all achievement checks within the same tick, then cleared. + void signal(const char* key); + bool hasSignal(const char* key) const; + std::vector getAchievements() const; bool hasPendingUnlock() const { return !m_pendingUnlocks.empty(); } std::string consumePendingUnlock(); @@ -58,6 +65,7 @@ private: void processEntry(Entry& e); std::vector m_entries; + std::unordered_set m_signals; bool m_loaded = false; bool m_dirty = false; std::queue m_pendingUnlocks; diff --git a/src/d/actor/d_a_obj_carry.cpp b/src/d/actor/d_a_obj_carry.cpp index a8265f69f6..b83f9cd82f 100644 --- a/src/d/actor/d_a_obj_carry.cpp +++ b/src/d/actor/d_a_obj_carry.cpp @@ -21,6 +21,9 @@ #include "d/d_lib.h" #include "d/d_debug_viewer.h" #include "f_op/f_op_kankyo_mng.h" +#ifdef TARGET_PC +#include "dusk/achievements.h" +#endif static const cM3dGCylS l_cyl_info[] = { { 0.0f, 0.0f, 0.0f, 30.0f, 100.0f }, @@ -3582,6 +3585,12 @@ bool daObjCarry_c::cc_damage_proc_ironball() { } } +#ifdef TARGET_PC + if (mCannon && mCyl.ChkAtHit() && mCyl.GetAtHitAc() == daPy_getPlayerActorClass()) { + dusk::AchievementSystem::get().signal("iron_ball_hit_player"); + } +#endif + return var_r26; } diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 4a7dac5e4d..58e99a3413 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -8,6 +8,7 @@ #include "d/actor/d_a_player.h" #include "d/d_demo.h" #include "f_pc/f_pc_name.h" +#include "f_op/f_op_actor_mng.h" #include #include @@ -273,6 +274,21 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "friendly_fire", + "Friendly Fire", + "Get hit by your own cannonball.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (AchievementSystem::get().hasSignal("iron_ball_hit_player")) { + a.progress = 1; + } + }, + {} + }, { { "back_in_time", @@ -441,6 +457,14 @@ void AchievementSystem::clearAll() { save(); } +void AchievementSystem::signal(const char* key) { + m_signals.insert(key); +} + +bool AchievementSystem::hasSignal(const char* key) const { + return m_signals.count(key) > 0; +} + void AchievementSystem::clearOne(const char* key) { for (auto& e : m_entries) { if (std::string(e.achievement.key) == key) { @@ -485,6 +509,7 @@ void AchievementSystem::tick() { for (auto& e : m_entries) { processEntry(e); } + m_signals.clear(); if (m_dirty) { save(); m_dirty = false; diff --git a/src/dusk/imgui/ImGuiAchievements.cpp b/src/dusk/imgui/ImGuiAchievements.cpp index 479e307e30..113be5d95d 100644 --- a/src/dusk/imgui/ImGuiAchievements.cpp +++ b/src/dusk/imgui/ImGuiAchievements.cpp @@ -111,6 +111,7 @@ void ImGuiAchievements::draw(bool& open) { {AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)}, {AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)}, {AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)}, + {AchievementCategory::Misc, "Misc", ImVec4(0.65f, 0.65f, 0.65f, 1.0f)}, {AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)}, }; From d99ed2729b1d499633c9d8c6e6f536ce9683a38b Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 05:57:58 -0700 Subject: [PATCH 10/28] fix achievements window sizing --- src/dusk/imgui/ImGuiAchievements.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dusk/imgui/ImGuiAchievements.cpp b/src/dusk/imgui/ImGuiAchievements.cpp index 113be5d95d..f03e4176ed 100644 --- a/src/dusk/imgui/ImGuiAchievements.cpp +++ b/src/dusk/imgui/ImGuiAchievements.cpp @@ -76,8 +76,8 @@ void ImGuiAchievements::draw(bool& open) { return; } - ImGui::SetNextWindowSizeConstraints(ImVec2(640, 200), ImVec2(800, 900)); - ImGui::SetNextWindowSize(ImVec2(640, 480), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900)); + ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver); if (!ImGui::Begin( "Achievements", &open, From 3c25633ee9fd1b77740f5c96ae64dacc612764b3 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 06:08:04 -0700 Subject: [PATCH 11/28] speed_target avoid ub --- src/d/actor/d_a_e_th.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/d/actor/d_a_e_th.cpp b/src/d/actor/d_a_e_th.cpp index f963350da4..43ad8ec19b 100644 --- a/src/d/actor/d_a_e_th.cpp +++ b/src/d/actor/d_a_e_th.cpp @@ -282,6 +282,11 @@ static void e_th_spin_B(e_th_class* i_this) { i_this->current.pos += spC; f32 speed_target; + + #if AVOID_UB + speed_target = 0; + #endif + f32 anm_frame = i_this->mpModelMorf->getFrame(); switch (i_this->mMode) { From 2dc494dc1c6e71e8ebbf07741e9a6417f715a210 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 06:14:56 -0700 Subject: [PATCH 12/28] Finish achievement --- src/d/d_menu_letter.cpp | 8 ++++++++ src/dusk/achievements.cpp | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/d/d_menu_letter.cpp b/src/d/d_menu_letter.cpp index a0155ef6b4..589b2553c0 100644 --- a/src/d/d_menu_letter.cpp +++ b/src/d/d_menu_letter.cpp @@ -17,6 +17,10 @@ #include "d/d_msg_scrn_arrow.h" #include "d/d_lib.h" +#ifdef TARGET_PC +#include "dusk/achievements.h" +#endif + #if VERSION == VERSION_GCN_JPN #define D_MENU_LETTER_LINE_MAX 9 #else @@ -514,6 +518,10 @@ void dMenu_Letter_c::read_open_init() { setAButtonString(0); setBButtonString(0); mpBlackTex->setAlpha(0); + + #ifdef TARGET_PC + dusk::AchievementSystem::get().signal("open_letter"); + #endif } void dMenu_Letter_c::read_open_move() { diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 58e99a3413..88dc43910c 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -376,6 +376,24 @@ std::vector AchievementSystem::makeEntries() { } }, {} + }, + { + { + "email-me", + "Email Me", + "Read a letter during the Dark Beast Ganon fight.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + + void* dbgExists = fopAcM_SearchByName(fpcNm_B_MGN_e); + if(dbgExists && AchievementSystem::get().hasSignal("open_letter")) { + a.progress = 1; + } + + }, + {} } }; } From bb6db3caea11d1bbe89f1d06764322a46c2fc1f5 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 06:18:40 -0700 Subject: [PATCH 13/28] remove newlines --- src/dusk/achievements.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 88dc43910c..5a603f4ac7 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -386,12 +386,10 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { - void* dbgExists = fopAcM_SearchByName(fpcNm_B_MGN_e); - if(dbgExists && AchievementSystem::get().hasSignal("open_letter")) { + if (dbgExists && AchievementSystem::get().hasSignal("open_letter")) { a.progress = 1; } - }, {} } From 7566949b424a96188ae73454680ac6fbf250b438 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 06:21:41 -0700 Subject: [PATCH 14/28] fix friendly fire achievement --- src/d/actor/d_a_alink_damage.inc | 13 +++++++++++++ src/d/actor/d_a_obj_carry.cpp | 9 --------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/d/actor/d_a_alink_damage.inc b/src/d/actor/d_a_alink_damage.inc index ac5175e314..e095fc0393 100644 --- a/src/d/actor/d_a_alink_damage.inc +++ b/src/d/actor/d_a_alink_damage.inc @@ -8,6 +8,10 @@ #include "d/actor/d_a_horse.h" #include "d/actor/d_a_crod.h" #include "d/d_msg_object.h" +#ifdef TARGET_PC +#include "d/actor/d_a_obj_carry.h" +#include "dusk/achievements.h" +#endif #if DEBUG #include "d/d_s_menu.h" @@ -677,6 +681,15 @@ BOOL daAlink_c::checkDamageAction() { } setDamagePoint(dmg, at_mtrl == dCcD_MTRL_FIRE || at_mtrl == dCcD_MTRL_ICE, TRUE, 0); + +#ifdef TARGET_PC + if (tghit_ac_name == fpcNm_Obj_Carry_e) { + auto* carry = static_cast(tghit_ac); + if (carry->prm_chk_type_ironball() && carry->checkCannon()) { + dusk::AchievementSystem::get().signal("iron_ball_hit_player"); + } + } +#endif if (armor_no_dmg && at_mtrl != dCcD_MTRL_ELECTRIC && at_mtrl != dCcD_MTRL_ICE) { setGuardSe(var_r29); diff --git a/src/d/actor/d_a_obj_carry.cpp b/src/d/actor/d_a_obj_carry.cpp index b83f9cd82f..a8265f69f6 100644 --- a/src/d/actor/d_a_obj_carry.cpp +++ b/src/d/actor/d_a_obj_carry.cpp @@ -21,9 +21,6 @@ #include "d/d_lib.h" #include "d/d_debug_viewer.h" #include "f_op/f_op_kankyo_mng.h" -#ifdef TARGET_PC -#include "dusk/achievements.h" -#endif static const cM3dGCylS l_cyl_info[] = { { 0.0f, 0.0f, 0.0f, 30.0f, 100.0f }, @@ -3585,12 +3582,6 @@ bool daObjCarry_c::cc_damage_proc_ironball() { } } -#ifdef TARGET_PC - if (mCannon && mCyl.ChkAtHit() && mCyl.GetAtHitAc() == daPy_getPlayerActorClass()) { - dusk::AchievementSystem::get().signal("iron_ball_hit_player"); - } -#endif - return var_r26; } From ddaf50c01d8e096fa358e23074635a6450dce0ce Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 06:53:20 -0700 Subject: [PATCH 15/28] long jump attack achievement --- src/dusk/achievements.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 58e99a3413..e0730447d5 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -289,6 +289,43 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "long_jump_attack", + "Long Jump Attack", + "Travel more than 20 meters in a single jump attack before landing.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + static bool inJump = false; + static float startX = 0.0f, startZ = 0.0f; + + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link == nullptr) { + inJump = false; + return; + } + + if (!inJump) { + if (link->mProcID == daAlink_c::PROC_CUT_JUMP) { + inJump = true; + startX = link->current.pos.x; + startZ = link->current.pos.z; + } + } else if (link->mProcID == daAlink_c::PROC_CUT_JUMP_LAND) { + inJump = false; + const float dx = link->current.pos.x - startX; + const float dz = link->current.pos.z - startZ; + if (dx * dx + dz * dz >= 2000.0f * 2000.0f) { + a.progress = 1; + } + } else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) { + inJump = false; + } + }, + {} + }, { { "back_in_time", From 94a99e8da0f5609de53e03c0ec32f6f2dae0be71 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 07:12:29 -0700 Subject: [PATCH 16/28] Add iron boots achievement --- src/dusk/achievements.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 5a603f4ac7..7ebe1e7710 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -379,7 +379,7 @@ std::vector AchievementSystem::makeEntries() { }, { { - "email-me", + "email_me", "Email Me", "Read a letter during the Dark Beast Ganon fight.", AchievementCategory::Misc, @@ -392,7 +392,27 @@ std::vector AchievementSystem::makeEntries() { } }, {} + }, + { + { + "heavy-hitter", + "Heavy Hitter", + "Wear the Iron Boots during the end credits.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) { + return; + } + if (daPy_getPlayerActorClass()->checkEquipHeavyBoots()) { + a.progress = 1; + } + }, + {} } + }; } From 2ed2268579958bd127df7923d3f6c91f40177c27 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 07:19:18 -0700 Subject: [PATCH 17/28] fix newline --- src/dusk/achievements.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index 7ebe1e7710..9a88d947b5 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -412,7 +412,6 @@ std::vector AchievementSystem::makeEntries() { }, {} } - }; } From 4d12cc8ea25ea53c22fc64f13f0d5212a05e4ce0 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 28 Apr 2026 11:05:29 -0400 Subject: [PATCH 18/28] Fix draw crash with Super Clawshot enabled --- src/d/actor/d_a_alink_hook.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/d/actor/d_a_alink_hook.inc b/src/d/actor/d_a_alink_hook.inc index d152190161..73049af08b 100644 --- a/src/d/actor/d_a_alink_hook.inc +++ b/src/d/actor/d_a_alink_hook.inc @@ -18,6 +18,10 @@ enum { }; void daAlink_c::hsChainShape_c::draw() { + if (dusk::getSettings().game.superClawshot) { + return; + } + static const int dummy = 0; daAlink_c* alink = (daAlink_c*)getUserArea(); From f75faf6b06c60bb642df15d94db86b4dd23d7c1c Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Tue, 28 Apr 2026 18:03:06 -0700 Subject: [PATCH 19/28] fix instant text clearing shop messages too early --- include/d/d_msg_object.h | 3 +++ src/d/d_msg_class.cpp | 9 +-------- src/d/d_msg_object.cpp | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/include/d/d_msg_object.h b/include/d/d_msg_object.h index b55ea73904..d2ac4ecb2a 100644 --- a/include/d/d_msg_object.h +++ b/include/d/d_msg_object.h @@ -67,6 +67,9 @@ public: bool isStaffMessage(); bool isSaveMessage(); bool isTalkMessage(); +#if TARGET_PC + bool isShopItemMessage(); +#endif const char* getSmellName(); const char* getPortalName(); const char* getBombName(); diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index 4040eb0d7f..fab6906ee0 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -1987,13 +1987,6 @@ bool jmessage_tSequenceProcessor::do_isReady() { } #endif -#if TARGET_PC - if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) { - field_0xb2 = 1; - pReference->setSendTimer(0); - } -#endif - if (dComIfGp_checkMesgBgm()) { bool isItemMusicPlaying = true; if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET && @@ -2066,7 +2059,7 @@ bool jmessage_tSequenceProcessor::do_isReady() { case 0: case 5: case 6: - if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0) { + if (mDoCPd_c::getTrigA(PAD_1) || field_0xb2 != 0 IF_DUSK(|| (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)))) { field_0xa4 = 0; pReference->onBatchFlag(); pReference->setCharCnt(D_MSG_CLASS_CHAR_CNT_MAX); diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index ae0e3d8427..22e722d790 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -32,6 +32,8 @@ #if TARGET_PC #include "dusk/settings.h" +#include +#include #endif static void dMsgObject_addFundRaising(s16 param_0); @@ -1594,7 +1596,7 @@ u8 dMsgObject_c::isSend() { return 2; } } else { - if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0) && !isShopItemMessage()) ||) mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } @@ -1866,6 +1868,40 @@ bool dMsgObject_c::isTalkMessage() { return true; } +#if TARGET_PC +bool dMsgObject_c::isShopItemMessage() { + + // Probably a better way to do this than just listing every message id, but this works for now + // Note: Keep contents sorted so we can use binary search + const auto shopMsgIds = std::to_array>({ + {}, + // zel_01.bmg - Seras Shop + {7001, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7013, 7014, 7022, 7023, 7028, 7029, + 7044, 7045, 7053}, + // zel_02.bmg - Kakariko Shops + {5251, 5253, 5254, 5256, 5258, 5259, 5653, 5654, 5656, 5660, 5661, 5664, 5665, 5697, 5698, + 5699, 5803, 5804, 5806, 5810, 5811, 5812, 5814, 5821, 5823, 5824, 5987, 5988, 5989, 5990, + 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999}, + // zel_03.bmg - Death Mountain Shop + {5303, 5304, 5306, 5310, 5311, 5314, 5315, 5322, 5323, 5324, 5496, 5497, 5498, 5499}, + // zel_04.bmg - Castle Town Shops + {5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5431, + 5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, 5441, 5444, 5449, 5450, 5451, 5452, + 5462}, + // zel_05.bmg - Oocca Shop + {9428, 9429, 9430, 9431, 9432, 9437, 9443, 9448, 9449, 9451, 9459} + }); + + u16 id = mMessageID; + s16 group = dMsgObject_getGroupID(); + if (group < shopMsgIds.size()) { + return std::ranges::binary_search(shopMsgIds[group], id); + } + return false; + +} +#endif + const char* dMsgObject_c::getSmellName() { JMSMesgInfo_c* info_header_p = (JMSMesgInfo_c*)((char*)mpMsgRes + 0x20); char* data_ptr = (char*)info_header_p + info_header_p->header.size; From b26896cad53142d4dde73d6df6fc4869b4ab28da Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Tue, 28 Apr 2026 18:05:40 -0700 Subject: [PATCH 20/28] includes --- src/d/d_msg_object.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 22e722d790..00dfc6626e 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -34,6 +34,7 @@ #include "dusk/settings.h" #include #include +#include #endif static void dMsgObject_addFundRaising(s16 param_0); From e15f5bcee9a48932c66d4a4499a4a2ee23e26d3f Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Wed, 29 Apr 2026 03:40:49 +0000 Subject: [PATCH 21/28] Improve map outline rendering --- src/d/d_map_path.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/d/d_map_path.cpp b/src/d/d_map_path.cpp index f94cdc9475..911dd09cdd 100644 --- a/src/d/d_map_path.cpp +++ b/src/d/d_map_path.cpp @@ -16,6 +16,7 @@ #ifdef TARGET_PC constexpr u16 kMapResolutionMultiplier = 4; +constexpr u16 kMapCircleSize = 16 * kMapResolutionMultiplier; #endif void dMpath_n::dTexObjAggregate_c::create() { @@ -32,6 +33,47 @@ void dMpath_n::dTexObjAggregate_c::create() { JUT_ASSERT(74, image->magFilter == GX_NEAR); mDoLib_setResTimgObj(image, mp_texObj[lp1], 0, NULL); } + +#if TARGET_PC + auto hqCircle = JKR_NEW TGXTexObj(); + + static bool hqCircleDrawn = false; + static u8 hqCircleData[kMapCircleSize * kMapCircleSize]; + + if (!hqCircleDrawn) { + const auto center = kMapCircleSize / 2.0f; + const auto radiusSq = center * center; + const auto blocksAcross = kMapCircleSize >> 3; + + for (u16 y = 0; y < kMapCircleSize; y++) { + for (u16 x = 0; x < kMapCircleSize; x++) { + // swizzle raster order to I8 blocks + auto blockX = x >> 3; + auto blockY = y >> 2; + auto blockIdx = (blockY * blocksAcross) + blockX; + + auto localX = x & 7; + auto localY = y & 3; + auto localIdx = (localY << 3) + localX; + + auto finalOffset = (blockIdx << 5) + localIdx; + + auto dx = (x + 0.5f) - center; + auto dy = (y + 0.5f) - center; + + // the original texture is in I4 format and uses 1 to indicate if inside the circle + // so we scale to I8 range: 255 / 15 = 17 + hqCircleData[finalOffset] = (dx * dx + dy * dy < radiusSq) ? 17 : 0; + } + } + hqCircleDrawn = true; + } + + GXInitTexObj(hqCircle, hqCircleData, kMapCircleSize, kMapCircleSize, GX_TF_I8, GX_CLAMP, + GX_CLAMP, GX_FALSE); + GXInitTexObjLOD(hqCircle, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); + mp_texObj[6] = hqCircle; +#endif } void dMpath_n::dTexObjAggregate_c::remove() { From afe54f22abe94ad52c56622579f45ae503081907 Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Wed, 29 Apr 2026 04:16:24 +0000 Subject: [PATCH 22/28] Write circle pixels linearly --- src/d/d_map_path.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/d/d_map_path.cpp b/src/d/d_map_path.cpp index 911dd09cdd..eca620ef98 100644 --- a/src/d/d_map_path.cpp +++ b/src/d/d_map_path.cpp @@ -44,33 +44,34 @@ void dMpath_n::dTexObjAggregate_c::create() { const auto center = kMapCircleSize / 2.0f; const auto radiusSq = center * center; const auto blocksAcross = kMapCircleSize >> 3; + const auto totalPixels = sizeof(hqCircleData); - for (u16 y = 0; y < kMapCircleSize; y++) { - for (u16 x = 0; x < kMapCircleSize; x++) { - // swizzle raster order to I8 blocks - auto blockX = x >> 3; - auto blockY = y >> 2; - auto blockIdx = (blockY * blocksAcross) + blockX; + for (size_t i = 0; i < totalPixels; i++) { + // 8x4 block swizzling for I8 + const auto blockIdx = i >> 5; + const auto localIdx = i & 31; - auto localX = x & 7; - auto localY = y & 3; - auto localIdx = (localY << 3) + localX; + const auto blockY = blockIdx / blocksAcross; + const auto blockX = blockIdx % blocksAcross; - auto finalOffset = (blockIdx << 5) + localIdx; + const auto localY = localIdx >> 3; + const auto localX = localIdx & 7; - auto dx = (x + 0.5f) - center; - auto dy = (y + 0.5f) - center; + const auto x = (blockX << 3) + localX; + const auto y = (blockY << 2) + localY; - // the original texture is in I4 format and uses 1 to indicate if inside the circle - // so we scale to I8 range: 255 / 15 = 17 - hqCircleData[finalOffset] = (dx * dx + dy * dy < radiusSq) ? 17 : 0; - } + const auto dx = (x + 0.5f) - center; + const auto dy = (y + 0.5f) - center; + + // the original texture is in I4 format and uses 1 to indicate if inside the circle + // so we scale to I8 range: 255 / 15 = 17 + hqCircleData[i] = (dx * dx + dy * dy < radiusSq) ? 17 : 0; } hqCircleDrawn = true; } GXInitTexObj(hqCircle, hqCircleData, kMapCircleSize, kMapCircleSize, GX_TF_I8, GX_CLAMP, - GX_CLAMP, GX_FALSE); + GX_CLAMP, GX_FALSE); GXInitTexObjLOD(hqCircle, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); mp_texObj[6] = hqCircle; #endif From 24dd02fc810c5406e1338b6e14105bcca6741fd5 Mon Sep 17 00:00:00 2001 From: madeline Date: Wed, 29 Apr 2026 05:28:28 -0700 Subject: [PATCH 23/28] better back in time condition --- src/dusk/achievements.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index e0730447d5..dd9907be3f 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -335,18 +335,13 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { - static int titleNoDemoFrames = 0; if (fopAcM_SearchByName(fpcNm_TITLE_e) == nullptr) { - titleNoDemoFrames = 0; return; } - const auto* link = static_cast(daPy_getPlayerActorClass()); - if (link != nullptr && dDemo_c::getMode() == 0) { - if (++titleNoDemoFrames >= 60) { + const auto* player = static_cast(daPy_getPlayerActorClass()); + + if (player != nullptr && player->mDemo.getDemoMode() == 1) { a.progress = 1; - } - } else { - titleNoDemoFrames = 0; } }, {} From c803bfb545c551b298d8b50a818bd27d5777bde4 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Wed, 29 Apr 2026 09:14:51 -0700 Subject: [PATCH 24/28] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index 8a2b80ecb1..c77a4d0c3c 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 8a2b80ecb104625319c2818fd6d752bab8617679 +Subproject commit c77a4d0c3c6a0d9f584a30e6ab1661634959c32b From 36dc43c6024c3719e86eabe6137cc875628d402f Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Wed, 29 Apr 2026 20:13:07 -0400 Subject: [PATCH 25/28] Fix changing tunics crash while on top of mirror (#596) * Fix changing tunics while reflection is active * Revert "Fix changing tunics while reflection is active" This reverts commit 89927dc7a638342f2a9a79511e2445789fb62006. * Really fix changing tunics while reflection is active * Fix transforming on ice again --------- Co-authored-by: MelonSpeedruns Co-authored-by: Irastris --- include/d/actor/d_a_mirror.h | 1 + src/d/actor/d_a_mirror.cpp | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/d/actor/d_a_mirror.h b/include/d/actor/d_a_mirror.h index 06c5603899..e2f9a51e30 100644 --- a/include/d/actor/d_a_mirror.h +++ b/include/d/actor/d_a_mirror.h @@ -27,6 +27,7 @@ public: /* 0x17C */ cXyz mViewScale; #if TARGET_PC bool mbReset = false; + bool mbHadEntry = false; #endif }; diff --git a/src/d/actor/d_a_mirror.cpp b/src/d/actor/d_a_mirror.cpp index 86ef4314f3..5c5218328e 100644 --- a/src/d/actor/d_a_mirror.cpp +++ b/src/d/actor/d_a_mirror.cpp @@ -40,6 +40,7 @@ dMirror_packet_c::dMirror_packet_c() { void dMirror_packet_c::reset() { #if TARGET_PC mbReset = true; + mbHadEntry = false; #else mModelCount = 0; #endif @@ -84,11 +85,21 @@ void dMirror_packet_c::calcMinMax() { } int dMirror_packet_c::entryModel(J3DModel* i_model) { +#if TARGET_PC + if (mbReset) { + mModelCount = 0; + mbReset = false; + } +#endif + if (mModelCount >= 0x40) { return 0; } mModels[mModelCount++] = i_model; +#if TARGET_PC + mbHadEntry = true; +#endif return 1; } @@ -592,13 +603,6 @@ int daMirror_c::execute() { return 1; } -#if TARGET_PC - if (mPacket.mbReset) { - mPacket.mModelCount = 0; - mPacket.mbReset = false; - } -#endif - daPy_py_c* player = daPy_getLinkPlayerActorClass(); JUT_ASSERT(0, player != NULL); @@ -624,6 +628,12 @@ int daMirror_c::draw() { mDoExt_modelUpdateDL(mpModel); } +#if TARGET_PC + if (mPacket.mbReset && !mPacket.mbHadEntry) { + mPacket.mModelCount = 0; + } + mPacket.mbHadEntry = true; +#endif dComIfGd_getOpaListBG()->entryImm(&mPacket, 0); return 1; } From fbf63b075ad5e2c484c4f7e3f2308a5ff7eaa886 Mon Sep 17 00:00:00 2001 From: doop <56421834+dooplecks@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:28:47 +0000 Subject: [PATCH 26/28] Correct JPADrawInfo proj matrix on widescreen Fixes #337. --- src/m_Do/m_Do_graphic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 425d1e25fe..9bd6e5e327 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -2146,6 +2146,7 @@ int mDoGph_Painter() { // FRAME INTERP NOTE: Call setViewMtx earlier so that it's interpolated in time for draw_info to use it j3dSys.setViewMtx(camera_p->view.viewMtx); JPADrawInfo draw_info(j3dSys.getViewMtx(), camera_p->view.fovy, camera_p->view.aspect); + mDoGph_gInf_c::setWideZoomLightProjection(draw_info.mPrjMtx); #else JPADrawInfo draw_info(camera_p->view.viewMtx, camera_p->view.fovy, camera_p->view.aspect); #endif From fecd1d568373aa7903e0bb92b9db459ec9d03041 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Wed, 29 Apr 2026 22:33:02 -0700 Subject: [PATCH 27/28] Update aurora --- extern/aurora | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/aurora b/extern/aurora index c77a4d0c3c..d3f34bfea8 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit c77a4d0c3c6a0d9f584a30e6ab1661634959c32b +Subproject commit d3f34bfea84b8b70bd1c8b13d78a3d67a3e4d332 From 1ac6df8de7956a790b7782e74a1503568f922904 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 30 Apr 2026 07:52:44 -0700 Subject: [PATCH 28/28] mirror clip fix closes #581 --- src/d/actor/d_a_obj_mirror_chain.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/d/actor/d_a_obj_mirror_chain.cpp b/src/d/actor/d_a_obj_mirror_chain.cpp index 92c062f7c8..da4a09e1c5 100644 --- a/src/d/actor/d_a_obj_mirror_chain.cpp +++ b/src/d/actor/d_a_obj_mirror_chain.cpp @@ -59,7 +59,8 @@ void dScissorBegin_packet_c::draw() { } if (sp50 >= 4) { - GXSetScissor(FB_WIDTH + 1, FB_HEIGHT + 1, 0, 0); + IF_DUSK(GXSetColorUpdate(GX_FALSE)); + IF_NOT_DUSK(GXSetScissor(FB_WIDTH + 1, FB_HEIGHT + 1, 0, 0)); return; } @@ -170,7 +171,8 @@ void dScissorBegin_packet_c::draw() { } if (spBC.z < 0.0f || var_f31 > sp6C || sp7C < sp70 || var_f30 > sp64 || sp78 < sp68) { - GXSetScissor(FB_WIDTH + 1, FB_HEIGHT + 1, 0, 0); + IF_DUSK(GXSetColorUpdate(GX_FALSE)); + IF_NOT_DUSK(GXSetScissor(FB_WIDTH + 1, FB_HEIGHT + 1, 0, 0)); } else { var_f31 = cLib_minLimit(var_f31, sp70); sp7C = cLib_maxLimit(sp7C, sp6C); @@ -187,6 +189,7 @@ void dScissorEnd_packet_c::draw() { #endif GXSetScissor(l_scissor[0], l_scissor[1], l_scissor[2], l_scissor[3]); + IF_DUSK(GXSetColorUpdate(GX_TRUE)); } static int createSolidHeap(fopAc_ac_c* i_this) {