From c2de6524c8f26ad934ea0e8ac4478c088258a6cd Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Fri, 28 Aug 2026 00:47:33 -0700 Subject: [PATCH] add dungeon item display in item wheel --- mods/randomizer/CMakeLists.txt | 1 + mods/randomizer/src/draw.cpp | 48 +++++++ mods/randomizer/src/draw.hpp | 7 + mods/randomizer/src/hooks.cpp | 231 +++++++++++++++++++++++++++++++-- mods/randomizer/src/tools.cpp | 7 + mods/randomizer/src/tools.h | 4 +- 6 files changed, 289 insertions(+), 9 deletions(-) create mode 100644 mods/randomizer/src/draw.cpp create mode 100644 mods/randomizer/src/draw.hpp diff --git a/mods/randomizer/CMakeLists.txt b/mods/randomizer/CMakeLists.txt index 3f015d862a..6c6dbae00b 100644 --- a/mods/randomizer/CMakeLists.txt +++ b/mods/randomizer/CMakeLists.txt @@ -83,6 +83,7 @@ set(RANDOMIZER_GENERATOR_SOURCES ) set(RANDOMIZER_SOURCES + src/draw.cpp src/flags.cpp src/messages.cpp src/randomizer_context.cpp diff --git a/mods/randomizer/src/draw.cpp b/mods/randomizer/src/draw.cpp new file mode 100644 index 0000000000..5933424b39 --- /dev/null +++ b/mods/randomizer/src/draw.cpp @@ -0,0 +1,48 @@ +// This code is copied/modified from the decompgz project and has been modified to fit the needs of this framework. +// https://github.com/zsrtp/decompgz + +#include "draw.hpp" +#include "d/d_com_inf_game.h" +#include "m_Do/m_Do_mtx.h" + +static void setupGXPrimitive() { + GXSetNumChans(1); + GXSetNumTexGens(0); + GXSetNumTevStages(1); + GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); + GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC); + GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA); + GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + + GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_REG, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); + + GXSetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); + GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_OR); + GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0); + GXSetCullMode(GX_CULL_NONE); + + GXLoadPosMtxImm(g_mDoMtx_identity, GX_PNMTX0); + GXSetCurrentMtx(0); + + GXClearVtxDesc(); + GXSetVtxDesc(GX_VA_POS, GX_DIRECT); + GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); +} + +void drawFilledRect(f32 x, f32 y, f32 w, f32 h, GXColor color) { + setupGXPrimitive(); + + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + GXPosition2f32(x, y); + GXColor4u8(color.r, color.g, color.b, color.a); + GXPosition2f32(x + w, y); + GXColor4u8(color.r, color.g, color.b, color.a); + GXPosition2f32(x + w, y + h); + GXColor4u8(color.r, color.g, color.b, color.a); + GXPosition2f32(x, y + h); + GXColor4u8(color.r, color.g, color.b, color.a); + GXEnd(); +} diff --git a/mods/randomizer/src/draw.hpp b/mods/randomizer/src/draw.hpp new file mode 100644 index 0000000000..f7eb6c9337 --- /dev/null +++ b/mods/randomizer/src/draw.hpp @@ -0,0 +1,7 @@ +// This code is copied/modified from the decompgz project and has been modified to fit the needs of this framework. +// https://github.com/zsrtp/decompgz +#pragma once + +#include + +void drawFilledRect(f32 x, f32 y, f32 w, f32 h, GXColor color); diff --git a/mods/randomizer/src/hooks.cpp b/mods/randomizer/src/hooks.cpp index 6587e81f34..3ee285b804 100644 --- a/mods/randomizer/src/hooks.cpp +++ b/mods/randomizer/src/hooks.cpp @@ -2,6 +2,7 @@ #include "session.hpp" #include "randomizer_context.hpp" #include "ui/rando_config.hpp" +#include "draw.hpp" #include "flags.h" #include "stages.h" #include "tools.h" @@ -186,6 +187,9 @@ DEFINE_HOOK(&dMenu_Ring_c::textScaleHIO, dMenu_Ring_c__textScaleHIO); #endif DEFINE_HOOK_SYMBOL(dMenu_Ring_c__destructor_sig, void(dMenu_Ring_c*), dMenu_Ring_c__destructor); +DEFINE_HOOK(&dMenu_Ring_c::_create, dMenu_Ring_c__create); +DEFINE_HOOK(&dMenu_Ring_c::_move, dMenu_Ring_c__move); +DEFINE_HOOK(&dMenu_Ring_c::_draw, dMenu_Ring_c__draw); DEFINE_HOOK(&dMenu_Ring_c::setActiveCursor, dMenu_Ring_c__setActiveCursor); DEFINE_HOOK(&dMenu_Ring_c::getItemMaxNum, dMenu_Ring_c__getItemMaxNum); DEFINE_HOOK(&dMenu_Ring_c::getItemNum, dMenu_Ring_c__getItemNum); @@ -2587,26 +2591,230 @@ void hookPostObjZraRockCreate(ModContext*, void* args, void* retval, void*) { } } -J2DPicture* g_dpadIcon{}; +// Things used for rando additional data +struct ItemTexture { + J2DPicture* picture = nullptr; + u8 buffer[0xC00]{}; +}; +ItemTexture gmKeyIcon{}; +ItemTexture bedKeyIcon{}; +ItemTexture bigKeyIcon{}; +ItemTexture dungeonMapIcon{}; +ItemTexture compassIcon{}; +ItemTexture pumpkinIcon{}; +ItemTexture cheeseIcon{}; +J2DTextBox* itemWheelDunText = nullptr; +J2DTextBox* itemWheelKeyText = nullptr; +JUTFont* itemWheelTextFont = nullptr; +bool shouldDisplayMenu = false; + +void hookPostMenuRingCreate(ModContext*, void*, void*, void*) { + itemWheelTextFont = mDoExt_getMesgFont(); + + // Setup our menu text + itemWheelDunText = JKR_NEW J2DTextBox(); + itemWheelDunText->setFont(itemWheelTextFont); + itemWheelDunText->setFontSize(16.0f, 16.0f); + itemWheelDunText->setLineSpace(25.0f); + itemWheelKeyText = JKR_NEW J2DTextBox(); + itemWheelKeyText->setFont(itemWheelTextFont); + itemWheelKeyText->setFontSize(16.0f, 16.0f); + itemWheelKeyText->setLineSpace(25.0f); + + // Setup dungeon item picture icons + gmKeyIcon.picture = JKR_NEW J2DPicture(); + bedKeyIcon.picture = JKR_NEW J2DPicture(); + bigKeyIcon.picture = JKR_NEW J2DPicture(); + dungeonMapIcon.picture = JKR_NEW J2DPicture(); + compassIcon.picture = JKR_NEW J2DPicture(); + pumpkinIcon.picture = JKR_NEW J2DPicture(); + cheeseIcon.picture = JKR_NEW J2DPicture(); + + // Read dungeon item textures + dMeter2Info_readItemTexture(dItemNo_LV2_BOSS_KEY_e, &gmKeyIcon.buffer, gmKeyIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_LV5_BOSS_KEY_e, &bedKeyIcon.buffer, bedKeyIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_BOSS_KEY_e, &bigKeyIcon.buffer, bigKeyIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_MAP_e, &dungeonMapIcon.buffer, dungeonMapIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_COMPUS_e, &compassIcon.buffer, compassIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_TOMATO_PUREE_e, &pumpkinIcon.buffer, pumpkinIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + dMeter2Info_readItemTexture(dItemNo_TASTE_e, &cheeseIcon.buffer, cheeseIcon.picture, NULL, NULL, NULL, NULL, NULL, NULL, -1); + + // Get data to display on the menu + const u32 shadowsCount = numFusedShadows(); + const u32 shardsCount = numMirrorShards(); + const u8 ftKeyNum = getTempleKeysFound(0x10); + const u8 ftTotalKeyNum = 4; + const u8 gmKeyNum = getTempleKeysFound(0x11); + const u8 gmTotalKeyNum = 3; + const u8 lbtKeyNum = getTempleKeysFound(0x12); + const u8 lbtTotalKeyNum = 3; + const u8 agKeyNum = getTempleKeysFound(0x13); + const u8 agTotalKeyNum = 5; + const u8 sprKeyNum = getTempleKeysFound(0x14); + const u8 sprTotalKeyNum = 4; + const u8 totKeyNum = getTempleKeysFound(0x15); + const u8 totTotalKeyNum = 3; + const u8 citsKeyNum = getTempleKeysFound(0x16); + const u8 citsTotalKeyNum = 1; + const u8 potKeyNum = getTempleKeysFound(0x17); + const u8 potTotalKeyNum = 7; + const u8 hcKeyNum = getTempleKeysFound(0x18); + const u8 hcTotalKeyNum = 3; + const u8 campKeyNum = getTempleKeysFound(0xA); + const u8 campTotalKeyNum = 1; + bool hasFaronGateKey = dComIfGs_isStageSwitch(0x2, 0x14); + bool hasCoroGateKey = dComIfGs_isStageSwitch(0x2, 0xC); + bool hasGateKey = haveItem(dItemNo_BOSSRIDER_KEY_e); + + // Create and set the text strings + char itemWheelTextBuf[300]; + snprintf(itemWheelTextBuf, sizeof(itemWheelTextBuf), "Shadows: %d/3 Key Legend:\nShards: %d/4 Current (Total)\n\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", shadowsCount, shardsCount, "Forest", "Mines", "Lakebed", "Arbiters", "Snowpeak", "Time", "City", "Palace", "Hyrule", "Desert", "Faron Gate", "Coro Gate", "Gate Keys"); + itemWheelDunText->setString(itemWheelTextBuf); + + snprintf(itemWheelTextBuf, sizeof(itemWheelTextBuf), "\n\n\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%d (%d)\n%s\n%s\n%s\n", ftKeyNum, ftTotalKeyNum, gmKeyNum, gmTotalKeyNum, lbtKeyNum, lbtTotalKeyNum, agKeyNum, agTotalKeyNum, sprKeyNum, sprTotalKeyNum, totKeyNum, totTotalKeyNum, citsKeyNum, citsTotalKeyNum, potKeyNum, potTotalKeyNum, hcKeyNum, hcTotalKeyNum, campKeyNum, campTotalKeyNum, getYesNoText(hasFaronGateKey), getYesNoText(hasCoroGateKey),getYesNoText(hasGateKey)); + itemWheelKeyText->setString(itemWheelTextBuf); +} + +void hookPostMenuRingMove(ModContext*, void*, void*, void*) { + // Check every frame if we've toggled displaying the menu + if (mDoCPd_c::getTrigStart(PAD_1)) { + shouldDisplayMenu = !shouldDisplayMenu; + } +} + +HookAction hookPreMenuRingDraw(ModContext*, void* args, void*, void*) { + auto menuRing = mods::arg(args, 0); + + // Create the toggle text + J2DTextBox textbox; + JUtility::TColor white(255, 255, 255, 255); + textbox.setFont(itemWheelTextFont); + textbox.setFontSize(16.f, 16.f); + textbox.setLineSpace(16.f); + textbox.setString("Press Start\nto toggle\nadditional\ndata"); + textbox.setCharColor(white); + textbox.setGradColor(white); + textbox.draw(menuRing->mCenterPosX + 465.f, menuRing->mCenterPosY + 157.f); + + return HOOK_CONTINUE; +} + +void hookPostMenuRingDraw(ModContext*, void* args, void* retval, void*) { + auto menuRing = mods::arg(args, 0); + + if (shouldDisplayMenu) { + const f32 ringPosX = menuRing->mCenterPosX; + const f32 ringPosY = menuRing->mCenterPosY; + f32 windowPosXOffset = 95.f; + f32 windowPosYOffset = 20.f; + GXColor colorBlk = {0, 0, 0, 255}; + + // Draw the background first + drawFilledRect(ringPosX + windowPosXOffset, ringPosY + windowPosYOffset, 305.f, 410.f, colorBlk); + + windowPosXOffset += 7.f; + windowPosYOffset += 20.f; + + // Draw the text + itemWheelDunText->draw(ringPosX + windowPosXOffset, ringPosY + windowPosYOffset); + itemWheelKeyText->draw(ringPosX + windowPosXOffset + 120.f, ringPosY + windowPosYOffset); + + windowPosYOffset += 58.f; + + // For each dungeon draw map, compass, and big key icons + for (int i = 0x10; i < 0x19; i++) { + if (dComIfGs_isDungeonItemBossKey(i)) { + if (i == 0x11) { + gmKeyIcon.picture->draw(ringPosX + windowPosXOffset + 170.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + else if (i == 0x14) { + bedKeyIcon.picture->draw(ringPosX + windowPosXOffset + 170.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + else { + bigKeyIcon.picture->draw(ringPosX + windowPosXOffset + 170.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + } + + if (dComIfGs_isDungeonItemMap(i)) { + dungeonMapIcon.picture->draw(ringPosX + windowPosXOffset + 195.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + if (dComIfGs_isDungeonItemCompass(i)) { + compassIcon.picture->draw(ringPosX + windowPosXOffset + 220.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + + // For Snowpeak, draw pumpkin and cheese + if (i == 0x14) { + if (dComIfGs_isEventBit(TOLD_YETA_ABOUT_PUMPKIN)) { + pumpkinIcon.picture->draw(ringPosX + windowPosXOffset + 245.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + if (dComIfGs_isEventBit(TOLD_YETA_ABOUT_CHEESE)) { + cheeseIcon.picture->draw(ringPosX + windowPosXOffset + 270.f, ringPosY + windowPosYOffset, 23.f, 23.f, false, false, false); + } + } + windowPosYOffset += 25.f; + } + } +} + +J2DPicture* dpadIcon{}; void hookPostMenuRingTextScaleHIO(ModContext*, void* args, void*, void*) { auto menuRing = mods::arg(args, 0); if (menuRing->mItemSlots[menuRing->mCurrentSlot] == 0x15) { // Draw d-pad icon to indicate switching between Ilia quest items if (getWarashibeItemCount() >= 2) { - if (g_dpadIcon == nullptr) { - g_dpadIcon = JKR_NEW J2DPicture((ResTIMG*)dComIfGp_getMain2DArchive()->getResource('TIMG', "font_51.bti")); + if (dpadIcon == nullptr) { + dpadIcon = JKR_NEW J2DPicture((ResTIMG*)dComIfGp_getMain2DArchive()->getResource('TIMG', "font_51.bti")); } - g_dpadIcon->setAlpha(menuRing->mAlphaRate * 255.0); - g_dpadIcon->draw(menuRing->mCenterPosX + 330.f, menuRing->mCenterPosY + 194.f, 30.f, 30.f, false, false, false); + dpadIcon->setAlpha(menuRing->mAlphaRate * 255.0); + dpadIcon->draw(menuRing->mCenterPosX + 330.f, menuRing->mCenterPosY + 194.f, 30.f, 30.f, false, false, false); } } } void hookPostMenuRingDestructor(ModContext*, void*, void*, void*) { - if (g_dpadIcon != nullptr) { - JKR_DELETE(g_dpadIcon); - g_dpadIcon = nullptr; + // Free our custom menu stuff + if (dpadIcon != nullptr) { + JKR_DELETE(dpadIcon); + dpadIcon = nullptr; } + if (itemWheelDunText != nullptr) { + JKR_DELETE(itemWheelDunText); + itemWheelDunText = nullptr; + } + if (itemWheelKeyText != nullptr) { + JKR_DELETE(itemWheelKeyText); + itemWheelKeyText = nullptr; + } + if (gmKeyIcon.picture != nullptr) { + JKR_DELETE(gmKeyIcon.picture); + gmKeyIcon.picture = nullptr; + } + if (bedKeyIcon.picture != nullptr) { + JKR_DELETE(bedKeyIcon.picture); + bedKeyIcon.picture = nullptr; + } + if (bigKeyIcon.picture != nullptr) { + JKR_DELETE(bigKeyIcon.picture); + bigKeyIcon.picture = nullptr; + } + if (dungeonMapIcon.picture != nullptr) { + JKR_DELETE(dungeonMapIcon.picture); + dungeonMapIcon.picture = nullptr; + } + if (compassIcon.picture != nullptr) { + JKR_DELETE(compassIcon.picture); + compassIcon.picture = nullptr; + } + if (pumpkinIcon.picture != nullptr) { + JKR_DELETE(pumpkinIcon.picture); + pumpkinIcon.picture = nullptr; + } + if (cheeseIcon.picture != nullptr) { + JKR_DELETE(cheeseIcon.picture); + cheeseIcon.picture = nullptr; + } + mDoExt_removeMesgFont(); + itemWheelTextFont = nullptr; } void hookPostMenuRingSetActiveCursor(ModContext*, void* args, void*, void*) { @@ -3270,6 +3478,10 @@ ModResult initialize() { ADD_HOOK_POST(dMenu_Ring_c__textScaleHIO, hookPostMenuRingTextScaleHIO); ADD_HOOK_POST(dMenu_Ring_c__destructor, hookPostMenuRingDestructor); + ADD_HOOK_POST(dMenu_Ring_c__create, hookPostMenuRingCreate); + ADD_HOOK_POST(dMenu_Ring_c__move, hookPostMenuRingMove); + ADD_HOOK_PRE(dMenu_Ring_c__draw, hookPreMenuRingDraw); + ADD_HOOK_POST(dMenu_Ring_c__draw, hookPostMenuRingDraw); ADD_HOOK_POST(dMenu_Ring_c__setActiveCursor, hookPostMenuRingSetActiveCursor); ADD_HOOK_POST(dMenu_Ring_c__getItemMaxNum, hookPostMenuRingGetItemMaxNum); ADD_HOOK_POST(dMenu_Ring_c__getItemNum, hookPostMenuRingGetItemNum); @@ -3402,6 +3614,9 @@ ModResult uninstall() { mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); + mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); mods::hook::uninstall(svc_hook); diff --git a/mods/randomizer/src/tools.cpp b/mods/randomizer/src/tools.cpp index 14f3db406a..8b6149d086 100644 --- a/mods/randomizer/src/tools.cpp +++ b/mods/randomizer/src/tools.cpp @@ -868,3 +868,10 @@ u8 getAreaKeyNum(int i_stageNo) { return dComIfGs_getSaveData()->getSave(i_stageNo).getBit().getKeyNum(); } + +const char* getYesNoText(bool flag) { + if (flag) { + return "Yes"; + } + return "No"; +} \ No newline at end of file diff --git a/mods/randomizer/src/tools.h b/mods/randomizer/src/tools.h index 81fe24dbf3..e177c6d611 100644 --- a/mods/randomizer/src/tools.h +++ b/mods/randomizer/src/tools.h @@ -64,4 +64,6 @@ void loadAncientDocumentNum(); bool tracker_isEventBit(u16 flag); bool tracker_isStageSwitch(int stage, int flag); -bool tracker_isStageItem(int stage, int flag); \ No newline at end of file +bool tracker_isStageItem(int stage, int flag); + +const char* getYesNoText(bool flag); \ No newline at end of file