From be70a5a1236c0b7ef309cc239432fd61d7634f6b Mon Sep 17 00:00:00 2001 From: louist103 <35883445+louist103@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:51:58 -0500 Subject: [PATCH] Item tracker (#707) * Core tracker code mostly done. * Format * Clean up enums. * Format * Header cleanup * Update * use CVars * format * Add editor to modern menu * format * Fix item tracker cvar loading/saving and add item tracker button for modern menu * update button label --------- Co-authored-by: Archez --- mm/2s2h/BenGui/BenGui.cpp | 18 +- mm/2s2h/BenGui/BenMenuBar.cpp | 14 + mm/2s2h/BenGui/SearchableMenuItems.h | 9 + mm/2s2h/Enhancements/Trackers/ItemTracker.cpp | 506 ++++++++++++++++++ mm/2s2h/Enhancements/Trackers/ItemTracker.h | 82 +++ .../Trackers/ItemTrackerSettings.cpp | 77 +++ .../Trackers/ItemTrackerSettings.h | 12 + 7 files changed, 717 insertions(+), 1 deletion(-) create mode 100644 mm/2s2h/Enhancements/Trackers/ItemTracker.cpp create mode 100644 mm/2s2h/Enhancements/Trackers/ItemTracker.h create mode 100644 mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.cpp create mode 100644 mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.h diff --git a/mm/2s2h/BenGui/BenGui.cpp b/mm/2s2h/BenGui/BenGui.cpp index 4a9fa2967..51ae063ea 100644 --- a/mm/2s2h/BenGui/BenGui.cpp +++ b/mm/2s2h/BenGui/BenGui.cpp @@ -21,6 +21,9 @@ #include "include/global.h" #include "include/z64audio.h" +#include "Enhancements/Trackers/ItemTracker.h" +#include "Enhancements/Trackers/ItemTrackerSettings.h" + namespace BenGui { // MARK: - Delegates @@ -38,6 +41,8 @@ std::shared_ptr mEventLogWindow; std::shared_ptr mBenMenu; std::shared_ptr mBenInputEditorWindow; std::shared_ptr mNotificationWindow; +std::shared_ptr mItemTrackerWindow; +std::shared_ptr mItemTrackerSettingsWindow; void SetupGuiElements() { auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); @@ -94,6 +99,13 @@ void SetupGuiElements() { mEventLogWindow = std::make_shared("gWindows.EventLog", "Event Log", ImVec2(520, 600)); gui->AddGuiWindow(mEventLogWindow); + + mItemTrackerWindow = std::make_shared("gWindows.ItemTracker", "Item Tracker"); + gui->AddGuiWindow(mItemTrackerWindow); + + mItemTrackerSettingsWindow = std::make_shared("gWindows.ItemTrackerSettings", + "Item Tracker Settings", ImVec2(800, 400)); + gui->AddGuiWindow(mItemTrackerSettingsWindow); gui->SetPadBtnTogglesMenu(); mNotificationWindow = std::make_shared("gWindows.Notifications", "Notifications Window"); @@ -102,6 +114,9 @@ void SetupGuiElements() { } void Destroy() { + auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); + + gui->RemoveAllGuiWindows(); mBenMenuBar = nullptr; mBenMenu = nullptr; mStatsWindow = nullptr; @@ -111,9 +126,10 @@ void Destroy() { mCollisionViewerWindow = nullptr; mEventLogWindow = nullptr; mNotificationWindow = nullptr; - mSaveEditorWindow = nullptr; mHudEditorWindow = nullptr; mActorViewerWindow = nullptr; + mItemTrackerWindow = nullptr; + mItemTrackerSettingsWindow = nullptr; } } // namespace BenGui diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index c3d3a0547..58b7f6f39 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -12,6 +12,9 @@ #include "2s2h/DeveloperTools/DeveloperTools.h" #include "HudEditor.h" +#include "2s2h/Enhancements/Trackers/ItemTracker.h" +#include "2s2h/Enhancements/Trackers/ItemTrackerSettings.h" + extern "C" { #include "z64.h" #include "functions.h" @@ -345,6 +348,8 @@ void DrawSettingsMenu() { } extern std::shared_ptr mHudEditorWindow; +extern std::shared_ptr mItemTrackerWindow; +extern std::shared_ptr mItemTrackerSettingsWindow; void DrawEnhancementsMenu() { if (UIWidgets::BeginMenu("Enhancements")) { @@ -799,6 +804,15 @@ void DrawEnhancementsMenu() { { .tooltip = "Enables the Hud Editor window, allowing you to edit your hud" }); } + if (mItemTrackerWindow) { + UIWidgets::WindowButton("Item Tracker", "gWindows.ItemTracker", mItemTrackerWindow); + } + + if (mItemTrackerSettingsWindow) { + UIWidgets::WindowButton("Item Tracker Settings", "gWindows.ItemTrackerSettings", + mItemTrackerSettingsWindow); + } + ImGui::EndMenu(); } } diff --git a/mm/2s2h/BenGui/SearchableMenuItems.h b/mm/2s2h/BenGui/SearchableMenuItems.h index 89e6a77cd..122344851 100644 --- a/mm/2s2h/BenGui/SearchableMenuItems.h +++ b/mm/2s2h/BenGui/SearchableMenuItems.h @@ -1424,6 +1424,15 @@ void AddEnhancements() { "Enables the HUD Editor window, allowing you to modify your HUD", WIDGET_WINDOW_BUTTON, { .size = UIWidgets::Sizes::Inline, .windowName = "HUD Editor" } } } } }); + enhancementsSidebar.push_back( + { "Item Tracker", + 1, + { // Item Tracker Settings + { { "Popout Item Tracker Settings", + "gWindows.ItemTrackerSettings", + "", + WIDGET_WINDOW_BUTTON, + { .size = UIWidgets::Sizes::Inline, .windowName = "Item Tracker Settings" } } } } }); } void AddDevTools() { diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp b/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp new file mode 100644 index 000000000..641ffb97e --- /dev/null +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker.cpp @@ -0,0 +1,506 @@ +#include "ItemTracker.h" +#include "Context.h" +#include "config/Config.h" +#include "assets/archives/icon_item_static/icon_item_static_yar.h" +#include "assets/archives/icon_item_24_static/icon_item_24_static_yar.h" + +#define CFG_TRACKER_ITEM(var) ("ItemTracker." var) + +using namespace Ship; + +ItemTrackerWindow::~ItemTrackerWindow() { + auto config = Context::GetInstance()->GetConsoleVariables(); + + config->SetFloat(CFG_TRACKER_ITEM("BgColorR"), mBgColor.x); + config->SetFloat(CFG_TRACKER_ITEM("BgColorG"), mBgColor.y); + config->SetFloat(CFG_TRACKER_ITEM("BgColorB"), mBgColor.z); + config->SetFloat(CFG_TRACKER_ITEM("BgColorA"), mBgColor.w); + config->SetFloat(CFG_TRACKER_ITEM("IconSize"), mIconSize); + config->SetFloat(CFG_TRACKER_ITEM("IconSpacing"), mIconSpacing); + config->SetFloat(CFG_TRACKER_ITEM("TextSize"), mTextSize); + config->SetFloat(CFG_TRACKER_ITEM("TextOffset"), mTextOffset); + config->SetInteger(CFG_TRACKER_ITEM("WindowType"), (int8_t)mWindowType); + config->SetInteger(CFG_TRACKER_ITEM("IsDraggable"), mIsDraggable); + config->SetInteger(CFG_TRACKER_ITEM("OnlyDrawPaused"), mOnlyDrawPaused); + config->SetInteger(CFG_TRACKER_ITEM("DrawCurrentAmmo"), mCapacityModes[ItemTrackerCapacityMode::DrawCurrent]); + config->SetInteger(CFG_TRACKER_ITEM("DrawMaxAmmo"), mCapacityModes[ItemTrackerCapacityMode::DrawCurCapacity]); + config->SetInteger(CFG_TRACKER_ITEM("DrawMaxCapacity"), mCapacityModes[ItemTrackerCapacityMode::DrawMaxCapacity]); + config->SetInteger(CFG_TRACKER_ITEM("InventoryDrawMode"), (int8_t)mItemDrawModes[SECTION_INVENTORY]); + config->SetInteger(CFG_TRACKER_ITEM("MasksDrawMode"), (int8_t)mItemDrawModes[SECTION_MASKS]); + config->SetInteger(CFG_TRACKER_ITEM("SongsDrawMode"), (int8_t)mItemDrawModes[SECTION_SONGS]); + config->SetInteger(CFG_TRACKER_ITEM("DungeonDrawMode"), (int8_t)mItemDrawModes[SECTION_DUNGEON]); + + config->Save(); +} + +void ItemTrackerWindow::LoadSettings() { + auto config = Context::GetInstance()->GetConsoleVariables(); + + mCapacityModes.fill(false); + + mBgColor.x = config->GetFloat(CFG_TRACKER_ITEM("BgColorR"), 0.0f); + mBgColor.y = config->GetFloat(CFG_TRACKER_ITEM("BgColorG"), 0.0f); + mBgColor.z = config->GetFloat(CFG_TRACKER_ITEM("BgColorB"), 0.0f); + mBgColor.w = config->GetFloat(CFG_TRACKER_ITEM("BgColorA"), 0.0f); + mIconSize = config->GetFloat(CFG_TRACKER_ITEM("IconSize"), 13.0f); + mIconSpacing = config->GetFloat(CFG_TRACKER_ITEM("IconSpacing"), 12.0f); + mTextSize = config->GetFloat(CFG_TRACKER_ITEM("TextSize"), 13.0f); + mTextOffset = config->GetFloat(CFG_TRACKER_ITEM("TextOffset"), 0.0f); + mWindowType = + (TrackerWindowType)config->GetInteger(CFG_TRACKER_ITEM("WindowType"), (int8_t)TrackerWindowType::Floating); + mIsDraggable = config->GetInteger(CFG_TRACKER_ITEM("IsDraggable"), false); + mOnlyDrawPaused = config->GetInteger(CFG_TRACKER_ITEM("OnlyDrawPaused"), false); + mCapacityModes[ItemTrackerCapacityMode::DrawCurrent] = + config->GetInteger(CFG_TRACKER_ITEM("DrawCurrentAmmo"), false); + mCapacityModes[ItemTrackerCapacityMode::DrawCurCapacity] = + config->GetInteger(CFG_TRACKER_ITEM("DrawMaxAmmo"), false); + mCapacityModes[ItemTrackerCapacityMode::DrawMaxCapacity] = + config->GetInteger(CFG_TRACKER_ITEM("DrawMaxCapacity"), false); + mItemDrawModes[SECTION_INVENTORY] = (ItemTrackerDisplayType)config->GetInteger( + CFG_TRACKER_ITEM("InventoryDrawMode"), (int32_t)ItemTrackerDisplayType::Hidden); + mItemDrawModes[SECTION_MASKS] = (ItemTrackerDisplayType)config->GetInteger(CFG_TRACKER_ITEM("MasksDrawMode"), + (int32_t)ItemTrackerDisplayType::Hidden); + mItemDrawModes[SECTION_SONGS] = (ItemTrackerDisplayType)config->GetInteger(CFG_TRACKER_ITEM("SongsDrawMode"), + (int32_t)ItemTrackerDisplayType::Hidden); + mItemDrawModes[SECTION_DUNGEON] = (ItemTrackerDisplayType)config->GetInteger( + CFG_TRACKER_ITEM("DungeonDrawMode"), (int32_t)ItemTrackerDisplayType::Hidden); +} + +void ItemTrackerWindow::BeginFloatingWindows(const char* name, ImGuiWindowFlags flags) { + ImGuiWindowFlags windowFlags = flags; + + if (windowFlags == 0) { + windowFlags |= + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize; + } + + if (mWindowType == TrackerWindowType::Floating) { + ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); + windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar; + + if (!mIsDraggable) { + windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; + } + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::Begin(name, nullptr, windowFlags); +} + +void EndFloatingWindows() { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::End(); +} + +static constexpr ImVec4 opaqueTex = { 1.0f, 1.0f, 1.0f, 1.0f }; +static constexpr ImVec4 fadedTex = { 0.5f, 0.5f, 0.5f, 0.5f }; + +void DrawItem(char* tex, bool drawFaded, float itemSize) { + ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(tex), ImVec2(itemSize, itemSize), + ImVec2(0, 0), ImVec2(1, 1), drawFaded ? fadedTex : opaqueTex); +} + +static constexpr std::array songInfo = { + ImVec4(0.588f, 1.0f, 0.392f, 1.0f), // QUEST_SONG_SONATA + ImVec4(1.0f, 0.313f, 0.156f, 1.0f), // QUEST_SONG_LULLABY + ImVec4(0.392f, 0.588f, 1.0f, 1.0f), // QUEST_SONG_BOSSA_NOVA + ImVec4(1.0f, 0.627f, 0.0f, 1.0f), // QUEST_SONG_ELEGY + ImVec4(1.0f, 0.392f, 1.0f, 1.0f), // QUEST_SONG_OATH +}; + +void ItemTrackerWindow::DrawNote(size_t songIndex, bool drawFaded) { + ImVec4 color; + // Scale the note to 24*36 from 16*24 because all other items assume 36*36. + constexpr float noteTo36scale = 36.0f / 24.0f; + + const float iconScale = mIconSize / 36.0f; + + // Scale the note icon with the rest of the items. + const ImVec2 scaledNoteSize(noteTo36scale * 16.0f * iconScale, noteTo36scale * 24.0f * iconScale); + + if (songIndex >= 5) { + color = songInfo[songIndex - 5]; + } else { + color = { 1.0f, 1.0f, 1.0f, 1.0f }; + } + if (drawFaded) { + color.w = .5f; + } + ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gItemIconSongNoteTex), + scaledNoteSize, ImVec2(0, 0), ImVec2(1, 1), color); +} + +extern "C" { +#include "z64save.h" +#include "macros.h" +#include "z64.h" +extern void* gItemIcons[131]; +extern uint8_t gItemSlots[77]; +extern SaveContext gSaveContext; +extern u32 gBitFlags[32]; +extern PlayState* gPlayState; +extern u16 gUpgradeCapacities[][4]; +extern u32 gUpgradeMasks[]; +extern u8 gUpgradeShifts[]; +} + +// The textures in gItemIcons aren't in the same order as the subscreen +static constexpr std::array sMaskTextures = { + gItemIconPostmansHatTex, gItemIconAllNightMaskTex, gItemIconBlastMaskTex, gItemIconStoneMaskTex, + gItemIconGreatFairyMaskTex, gItemIconDekuMaskTex, gItemIconKeatonMaskTex, gItemIconBremenMaskTex, + gItemIconBunnyHoodTex, gItemIconDonGeroMaskTex, gItemIconMaskOfScentsTex, gItemIconGoronMaskTex, + gItemIconRomaniMaskTex, gItemIconCircusLeaderMaskTex, gItemIconKafeisMaskTex, gItemIconCouplesMaskTex, + gItemIconMaskOfTruthTex, gItemIconZoraMaskTex, gItemIconKamaroMaskTex, gItemIconGibdoMaskTex, + gItemIconGaroMaskTex, gItemIconCaptainsHatTex, gItemIconGiantsMaskTex, gItemIconFierceDeityMaskTex, +}; + +// The songs in the enum are in the wrong order and has the unused songs +static constexpr std::array sSongBits = { + QUEST_SONG_TIME, QUEST_SONG_HEALING, QUEST_SONG_EPONA, QUEST_SONG_SOARING, QUEST_SONG_STORMS, + QUEST_SONG_SONATA, QUEST_SONG_LULLABY, QUEST_SONG_BOSSA_NOVA, QUEST_SONG_ELEGY, QUEST_SONG_OATH, +}; + +bool ItemTrackerWindow::HasAmmoCount(int itemId) { + switch (itemId) { + case ITEM_BOW: + case ITEM_BOMB: + case ITEM_BOMBCHU: + case ITEM_DEKU_STICK: + case ITEM_DEKU_NUT: + return true; + default: + return false; + } +} + +ItemTrackerWindow::AmmoInfo ItemTrackerWindow::GetAmmoInfo(int itemId) { + AmmoInfo info; + switch (itemId) { + case ITEM_BOW: + info = { .cur = (uint8_t)AMMO(ITEM_BOW), .curCap = (uint8_t)CUR_CAPACITY(UPG_QUIVER), .maxCap = 50 }; + break; + case ITEM_BOMB: + info = { .cur = (uint8_t)AMMO(ITEM_BOMB), .curCap = (uint8_t)CUR_CAPACITY(UPG_BOMB_BAG), .maxCap = 40 }; + break; + case ITEM_BOMBCHU: + info = { .cur = (uint8_t)AMMO(ITEM_BOMBCHU), .curCap = (uint8_t)CUR_CAPACITY(UPG_BOMB_BAG), .maxCap = 40 }; + break; + case ITEM_DEKU_STICK: + info = { .cur = (uint8_t)AMMO(ITEM_DEKU_STICK), + .curCap = (uint8_t)CUR_CAPACITY(UPG_DEKU_STICKS), + .maxCap = 30 }; + break; + case ITEM_DEKU_NUT: + info = { .cur = (uint8_t)AMMO(ITEM_DEKU_NUT), + .curCap = (uint8_t)CUR_CAPACITY(UPG_DEKU_NUTS), + .maxCap = 40 }; + break; + default: + info = { 0 }; + } + return info; +} + +void ItemTrackerWindow::DrawAmmoCount(int itemId, const ImVec2& iconPos) { + // Zeroing 16 bytes is a little more optimized than 10 + char ammoStr[16] = { 0 }; + char curStr[4] = { 0 }; + char curCapStr[4] = { 0 }; + char maxCapStr[4] = { 0 }; + AmmoInfo info; + + if (!HasAmmoCount(itemId)) { + return; + } + + info = GetAmmoInfo(itemId); + + if (mCapacityModes[ItemTrackerCapacityMode::DrawCurrent]) { + snprintf(curStr, std::size(curStr), "%d", info.cur); + if (mCapacityModes[ItemTrackerCapacityMode::DrawCurCapacity] || + mCapacityModes[ItemTrackerCapacityMode::DrawMaxCapacity]) { + strncat(curStr, "/", 1); + } + } + + if (mCapacityModes[ItemTrackerCapacityMode::DrawCurCapacity]) { + snprintf(curCapStr, std::size(curStr), "%d", info.curCap); + if (mCapacityModes[ItemTrackerCapacityMode::DrawMaxCapacity]) { + strncat(curCapStr, "/", 1); + } + } + + if (mCapacityModes[ItemTrackerCapacityMode::DrawMaxCapacity]) { + snprintf(maxCapStr, std::size(curStr), "%d", info.maxCap); + } + + ImGui::SetWindowFontScale(mTextSize / 13.0f); + ImVec2 iconPos2 = ImGui::GetCursorScreenPos(); + + snprintf(ammoStr, std::size(ammoStr), "%s%s%s", curStr, curCapStr, maxCapStr); + + float x = iconPos2.x + (mIconSize / 2.0f) - (ImGui::CalcTextSize(ammoStr).x / 2.0f); + // Normalize the offset based on the icon being 36x36 to account for larger icons. + ImGui::SetCursorScreenPos({ x, iconPos2.y - (mTextOffset / 36.0f) * mIconSize }); + + ImGui::Text("%s", ammoStr); +} + +int ItemTrackerWindow::DrawItems(int columns, int prevDrawnColumns) { + int topPadding = 0; + size_t i = 0; + // Draw items minus bottles + for (; i < 24 - BOTTLE_MAX; i++) { + bool drawFaded = false; + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + char* texToDraw; + const ImVec2 pos = { (column * (mIconSize + mIconSpacing) + 8.0f), + (row * (mIconSize + mIconSpacing)) + 8.0f + topPadding }; + + ImGui::SetCursorPos(pos); + + if (gSaveContext.save.saveInfo.inventory.items[i] == ITEM_NONE) { + if (column == 5) { + // BENTODO these slots can hold multiple items. Something fun to do would be to draw the multitile + // design here instead of nothing DrawMultiItem(nullptr, false, row, column); + continue; + } + texToDraw = (char*)gItemIcons[i]; + drawFaded = true; + } else { + texToDraw = (char*)gItemIcons[gSaveContext.save.saveInfo.inventory.items[i]]; + } + ImGui::BeginGroup(); + DrawItem(texToDraw, drawFaded, mIconSize); + DrawAmmoCount((int)i, pos); + ImGui::EndGroup(); + } + + // DrawBottles + for (; i < 24; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (mIconSize + mIconSpacing) + 8.0f), + (row * (mIconSize + mIconSpacing)) + 8.0f + topPadding)); + + if (gSaveContext.save.saveInfo.inventory.items[i] == ITEM_NONE) { + DrawItem(const_cast(gItemIconEmptyBottleTex), true, mIconSize); + } else { + DrawItem((char*)gItemIcons[gSaveContext.save.saveInfo.inventory.items[i]], false, mIconSize); + } + } + return 4; +} + +int ItemTrackerWindow::DrawMasks(int columns, int prevDrawnColumns) { + int topPadding = 0; + // Masks + for (size_t i = 0; i < 24; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (mIconSize + mIconSpacing) + 8.0f), + (row * (mIconSize + mIconSpacing)) + 8.0f + topPadding)); + + DrawItem(const_cast(sMaskTextures[i]), gSaveContext.save.saveInfo.inventory.items[i + 24] == ITEM_NONE, + mIconSize); + } + return 4; +} + +static int RoundDown(int orig, int nearest) { + int res = orig % nearest; + return orig - res; +} + +int ItemTrackerWindow::DrawSongs(int columns, int prevDrawnColumns) { + int topPadding = 0; + + for (size_t i = 0; i < sSongBits.size(); i++) { + int row = prevDrawnColumns + (i / 5); + int column = i % 5; + ImGui::SetCursorPos(ImVec2((column * (mIconSize + mIconSpacing) + 8.0f), + (row * (mIconSize + mIconSpacing)) + 8.0f + topPadding)); + + DrawNote(i, !CHECK_QUEST_ITEM(sSongBits[i])); + } + return 2; +} + +int ItemTrackerWindow::DrawDungeonItemsVert(int columns, int prevDrawnColumns) { + int topPadding = 0; + + // The icon size is based on 36x36. + float iconScale = mIconSize / 36.0f; + // Yes this does nothing. But for other icon sizes it will. + float squareIconSize = iconScale * 36.0f; + float rectIconSize = iconScale * 36.0f; + + for (size_t i = 0; i < 4; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (squareIconSize + mIconSpacing) + 8.0f), + (row * (squareIconSize + mIconSpacing)) + 8.0f + topPadding)); + DrawItem(static_cast(gItemIcons[i + ITEM_REMAINS_ODOLWA]), !CHECK_QUEST_ITEM(i), squareIconSize); + } + + prevDrawnColumns++; + + for (size_t i = 0; i < 4; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (rectIconSize + mIconSpacing) + 8.0f), + (row * (rectIconSize + mIconSpacing)) + 8.0f + topPadding)); + DrawItem(const_cast(gQuestIconBossKeyTex), !CHECK_DUNGEON_ITEM(DUNGEON_BOSS_KEY, i), rectIconSize); + } + + prevDrawnColumns++; + + for (size_t i = 0; i < 4; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (rectIconSize + mIconSpacing) + 8.0f), + (row * (rectIconSize + mIconSpacing)) + 8.0f + topPadding)); + DrawItem(const_cast(gQuestIconCompassTex), !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, i), rectIconSize); + } + + prevDrawnColumns++; + + for (size_t i = 0; i < 4; i++) { + int row = prevDrawnColumns + (i / columns); + int column = i % columns; + ImGui::SetCursorPos(ImVec2((column * (rectIconSize + mIconSpacing) + 8.0f), + (row * (rectIconSize + mIconSpacing)) + 8.0f + topPadding)); + DrawItem(const_cast(gQuestIconDungeonMapTex), !CHECK_DUNGEON_ITEM(DUNGEON_MAP, i), rectIconSize); + } + + return 4; +} + +void ItemTrackerWindow::DrawItemsInRows(int columns) { + float iconSize = mIconSize; + float iconSpacing = mIconSpacing; + int topPadding = 0; + int mainWindowPos = 0; + int advancedBy = 0; + + if (gPlayState == nullptr) { + ImGui::Text("Item tracker not available"); + return; + } + if (mOnlyDrawPaused && gPlayState->pauseCtx.state == 0) { + return; + } + if (mItemDrawModes[SECTION_INVENTORY] != ItemTrackerDisplayType::Hidden) { + if (mItemDrawModes[SECTION_INVENTORY] == ItemTrackerDisplayType::Seperate) { + BeginFloatingWindows("Items"); + } + advancedBy = DrawItems(6, mainWindowPos); + if (mItemDrawModes[SECTION_INVENTORY] == ItemTrackerDisplayType::Seperate) { + EndFloatingWindows(); + } else { + mainWindowPos += advancedBy; + } + } + + if (mItemDrawModes[SECTION_MASKS] != ItemTrackerDisplayType::Hidden) { + int drawPos = mainWindowPos; + if (mItemDrawModes[SECTION_MASKS] == ItemTrackerDisplayType::Seperate) { + drawPos = 0; + BeginFloatingWindows("Masks"); + } + advancedBy = DrawMasks(6, drawPos); + if (mItemDrawModes[SECTION_MASKS] == ItemTrackerDisplayType::Seperate) { + EndFloatingWindows(); + } else { + mainWindowPos += advancedBy; + } + } + + if (mItemDrawModes[SECTION_SONGS] != ItemTrackerDisplayType::Hidden) { + int drawPos = mainWindowPos; + if (mItemDrawModes[SECTION_SONGS] == ItemTrackerDisplayType::Seperate) { + drawPos = 0; + BeginFloatingWindows("Songs"); + } + advancedBy = DrawSongs(5, drawPos); + if (mItemDrawModes[SECTION_SONGS] == ItemTrackerDisplayType::Seperate) { + EndFloatingWindows(); + } else { + mainWindowPos += advancedBy; + } + } + + if (mItemDrawModes[SECTION_DUNGEON] != ItemTrackerDisplayType::Hidden) { + int drawPos = mainWindowPos; + if (mItemDrawModes[SECTION_DUNGEON] == ItemTrackerDisplayType::Seperate) { + drawPos = 0; + BeginFloatingWindows("Dungeon Items"); + } + advancedBy = DrawDungeonItemsVert(6, drawPos); + if (mItemDrawModes[SECTION_DUNGEON] == ItemTrackerDisplayType::Seperate) { + EndFloatingWindows(); + } else { + mainWindowPos += advancedBy; + } + } +} + +ImVec4* ItemTrackerWindow::GetBgColorPtr() { + return &mBgColor; +} + +float* ItemTrackerWindow::GetIconSizePtr() { + return &mIconSize; +} + +float* ItemTrackerWindow::GetIconSpacingPtr() { + return &mIconSpacing; +} + +float* ItemTrackerWindow::GetTextSizePtr() { + return &mTextSize; +} + +float* ItemTrackerWindow::GetTextOffsetPtr() { + return &mTextOffset; +} + +TrackerWindowType* ItemTrackerWindow::GetWindowTypePtr() { + return &mWindowType; +} + +bool* ItemTrackerWindow::GetIsDraggablePtr() { + return &mIsDraggable; +} + +bool* ItemTrackerWindow::GetOnlyShowPausedPtr() { + return &mOnlyDrawPaused; +} + +ItemTrackerDisplayType* ItemTrackerWindow::GetDrawModePtr(ItemTrackerSection type) { + return &mItemDrawModes[type]; +} + +bool* ItemTrackerWindow::GetCapacityModePtr(ItemTrackerCapacityMode mode) { + return &mCapacityModes[mode]; +} + +void ItemTrackerWindow::Draw() { + if (!IsVisible()) { + return; + } + BeginFloatingWindows("Inventory Items Tracker"); + DrawItemsInRows(); + EndFloatingWindows(); +} +void ItemTrackerWindow::InitElement() { + LoadSettings(); +} + +void ItemTrackerWindow::DrawElement() { +} diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker.h b/mm/2s2h/Enhancements/Trackers/ItemTracker.h new file mode 100644 index 000000000..6f8cbe1c5 --- /dev/null +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker.h @@ -0,0 +1,82 @@ +#include "window/gui/Gui.h" +#include "window/gui/GuiWindow.h" +#include + +typedef enum class TrackerWindowType : uint8_t { + Floating, + Window, +} TrackerWindowType; + +typedef enum class ItemTrackerDisplayType : int8_t { + Hidden, + MainWindow, + Seperate, +} ItemTrackerDisplayType; + +typedef enum { + SECTION_INVENTORY, + SECTION_MASKS, + SECTION_SONGS, + SECTION_DUNGEON, + SECTION_MAX, +} ItemTrackerSection; + +typedef enum { + DrawCurrent, + DrawCurCapacity, + DrawMaxCapacity, + DrawCapacityMax, +} ItemTrackerCapacityMode; + +class ItemTrackerWindow : public Ship::GuiWindow { + typedef struct AmmoInfo { + uint8_t cur; + uint8_t curCap; + uint8_t maxCap; + } AmmoInfo; + + public: + using GuiWindow::GuiWindow; + ~ItemTrackerWindow(); + void Draw() override; + + ImVec4* GetBgColorPtr(); + float* GetIconSizePtr(); + float* GetIconSpacingPtr(); + float* GetTextSizePtr(); + float* GetTextOffsetPtr(); + TrackerWindowType* GetWindowTypePtr(); + bool* GetIsDraggablePtr(); + bool* GetOnlyShowPausedPtr(); + ItemTrackerDisplayType* GetDrawModePtr(ItemTrackerSection type); + bool* GetCapacityModePtr(ItemTrackerCapacityMode mode); + void LoadSettings(); + + protected: + void InitElement() override; + void DrawElement() override; + void UpdateElement() override{}; + + private: + void BeginFloatingWindows(const char* name, ImGuiWindowFlags flags = 0); + void DrawItemsInRows(int columns = 6); + int DrawItems(int columns, int startAt); + int DrawMasks(int columns, int startAt); + int DrawSongs(int columns, int startAt); + void DrawNote(size_t songIndex, bool drawFaded); + int DrawDungeonItemsVert(int columns, int startAt); + void DrawAmmoCount(int itemId, const ImVec2& iconPos); + bool HasAmmoCount(int itemId); + AmmoInfo GetAmmoInfo(int itemId); + + ImVec4 mBgColor; + float mIconSize = 36.0f; + float mIconSpacing = 12.0f; + float mTextSize = 13.0f; + float mTextOffset = 0.0f; + TrackerWindowType mWindowType = TrackerWindowType::Floating; + bool mIsDraggable = false; + bool mOnlyDrawPaused = false; + std::array mCapacityModes; + std::array mItemDrawModes; +}; diff --git a/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.cpp b/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.cpp new file mode 100644 index 000000000..01c2089aa --- /dev/null +++ b/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.cpp @@ -0,0 +1,77 @@ +#include "ItemTrackerSettings.h" +#include "ItemTracker.h" +#include "../../BenGui/UIWidgets.hpp" + +namespace BenGui { +extern std::shared_ptr mItemTrackerWindow; +} + +using namespace BenGui; + +void ItemTrackerSettingsWindow::UpdateElement() { +} + +void ItemTrackerSettingsWindow::InitElement() { +} + +static const char* windowTypes[2] = { "Floating", "Window" }; +static const char* displayTypes[3] = { "Hidden", "Main Window", "Separate" }; +static const char* displayModes[2] = { "Always", "Combo Button Hold" }; + +void ItemTrackerSettingsWindow::DrawElement() { + ImGui::SetNextWindowSize(ImVec2(733, 472), ImGuiCond_FirstUseEver); + const UIWidgets::FloatSliderOptions sliderOpts = { .format = "%.0f", .step = 1.0f }; + + if (!ImGui::BeginChild("Item Tracker Settings")) { + ImGui::EndChild(); + return; + } + + UIWidgets::WindowButton("Show/Hide Item Tracker", "gWindows.ItemTracker", mItemTrackerWindow, + { .size = UIWidgets::Sizes::Inline }); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); + ImGui::BeginTable("itemTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); + ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("BG Color"); + ImGui::SameLine(); + + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + + ImGui::ColorEdit4("BG Color", (float*)mItemTrackerWindow->GetBgColorPtr(), + ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_NoLabel); + + UIWidgets::Combobox("Window Type", mItemTrackerWindow->GetWindowTypePtr(), windowTypes); + UIWidgets::Checkbox("Enable Dragging", mItemTrackerWindow->GetIsDraggablePtr()); + UIWidgets::Checkbox("Only enable while paused", mItemTrackerWindow->GetOnlyShowPausedPtr()); + + UIWidgets::SliderFloat("Icon size : %.0fpx", mItemTrackerWindow->GetIconSizePtr(), 0.0f, 128.0f, sliderOpts); + UIWidgets::SliderFloat("Icon margins : %.0fpx", mItemTrackerWindow->GetIconSpacingPtr(), -5.0f, 50.0f, sliderOpts); + UIWidgets::SliderFloat("Text size : %.0fpx", mItemTrackerWindow->GetTextSizePtr(), 1.0f, 30.0f, sliderOpts); + UIWidgets::SliderFloat("Text Offset : %0.fpx", mItemTrackerWindow->GetTextOffsetPtr(), 0.0f, 40.0f, sliderOpts); + + ImGui::TableNextColumn(); + + UIWidgets::Combobox("Inventory", mItemTrackerWindow->GetDrawModePtr(SECTION_INVENTORY), displayTypes); + UIWidgets::Combobox("Masks", mItemTrackerWindow->GetDrawModePtr(SECTION_MASKS), displayTypes); + UIWidgets::Combobox("Songs", mItemTrackerWindow->GetDrawModePtr(SECTION_SONGS), displayTypes); + UIWidgets::Combobox("Dungeon Items", mItemTrackerWindow->GetDrawModePtr(SECTION_DUNGEON), displayTypes); + + UIWidgets::Checkbox("Draw Current Ammo", + mItemTrackerWindow->GetCapacityModePtr(ItemTrackerCapacityMode::DrawCurrent)); + + UIWidgets::Checkbox("Draw Current Capacity", + mItemTrackerWindow->GetCapacityModePtr(ItemTrackerCapacityMode::DrawCurCapacity)); + + UIWidgets::Checkbox("Draw Max Capacity", + mItemTrackerWindow->GetCapacityModePtr(ItemTrackerCapacityMode::DrawMaxCapacity)); + + ImGui::PopStyleVar(1); + ImGui::EndTable(); + + ImGui::EndChild(); +} diff --git a/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.h b/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.h new file mode 100644 index 000000000..b4df6fd4c --- /dev/null +++ b/mm/2s2h/Enhancements/Trackers/ItemTrackerSettings.h @@ -0,0 +1,12 @@ +#include "window/gui/Gui.h" +#include "window/gui/GuiWindow.h" + +class ItemTrackerSettingsWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + protected: + void InitElement() override; + void DrawElement() override; + void UpdateElement() override; +};