From 2dc494dc1c6e71e8ebbf07741e9a6417f715a210 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 06:14:56 -0700 Subject: [PATCH 01/10] 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 02/10] 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 94a99e8da0f5609de53e03c0ec32f6f2dae0be71 Mon Sep 17 00:00:00 2001 From: Julian Maynes Date: Tue, 28 Apr 2026 07:12:29 -0700 Subject: [PATCH 03/10] 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 04/10] 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 f75faf6b06c60bb642df15d94db86b4dd23d7c1c Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Tue, 28 Apr 2026 18:03:06 -0700 Subject: [PATCH 05/10] 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 06/10] 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 07/10] 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 08/10] 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 09/10] 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 10/10] 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