From 0d37cb4e5420a9cb61a28f00babeb09ed1528dd5 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 04:01:22 -0700 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 7566949b424a96188ae73454680ac6fbf250b438 Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 28 Apr 2026 06:21:41 -0700 Subject: [PATCH 6/8] 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 7/8] 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 4d12cc8ea25ea53c22fc64f13f0d5212a05e4ce0 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 28 Apr 2026 11:05:29 -0400 Subject: [PATCH 8/8] 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();