diff options
| author | Garrett Cox <garrettjcox@gmail.com> | 2026-01-16 15:39:27 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-16 15:39:27 -0600 |
| commit | 4430281d1aab4076c40e62e5bb5be1383361afe3 (patch) | |
| tree | 4702727949eecd6b0576508e6d5b896a2f889ada | |
| parent | 254cc60e1d718fb73d40610d8ca51455ce54bfef (diff) | |
UX pass on item tracker overhaul (#1460)
| -rw-r--r-- | mm/2s2h/BenGui/UIWidgets.cpp | 2 | ||||
| -rw-r--r-- | mm/2s2h/DeveloperTools/SaveEditor.h | 2 | ||||
| -rw-r--r-- | mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.cpp | 798 | ||||
| -rw-r--r-- | mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.h | 63 | ||||
| -rw-r--r-- | mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTrackerSettings.cpp | 1041 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ConvertItem.cpp | 17 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Items.cpp | 17 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/StaticData.h | 1 | ||||
| -rw-r--r-- | mm/2s2h/ShipUtils.cpp | 157 | ||||
| -rw-r--r-- | mm/2s2h/ShipUtils.h | 6 |
10 files changed, 918 insertions, 1186 deletions
diff --git a/mm/2s2h/BenGui/UIWidgets.cpp b/mm/2s2h/BenGui/UIWidgets.cpp index 80c8dbdbe..61efce012 100644 --- a/mm/2s2h/BenGui/UIWidgets.cpp +++ b/mm/2s2h/BenGui/UIWidgets.cpp @@ -138,7 +138,7 @@ void PushStyleInput(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(color.x, color.y, color.z, 0.6f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 6.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 5.0f); } diff --git a/mm/2s2h/DeveloperTools/SaveEditor.h b/mm/2s2h/DeveloperTools/SaveEditor.h index dcf458d91..ffc85cda9 100644 --- a/mm/2s2h/DeveloperTools/SaveEditor.h +++ b/mm/2s2h/DeveloperTools/SaveEditor.h @@ -36,6 +36,8 @@ typedef struct { std::vector<FlagEntry> entries; } FlagTable; +extern std::vector<ItemId> safeItemsForInventorySlot[SLOT_MASK_FIERCE_DEITY + 1]; + // Reference https://tcrf.net/Proto:The_Legend_of_Zelda:_Majora's_Mask/Debug_Version/Event_Editor // The source was last referenced on 2025-08-05 and had a last updated value of 2024-06-29 // clang-format off diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.cpp b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.cpp index c8c4852f2..c0e7eebcc 100644 --- a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.cpp +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.cpp @@ -15,6 +15,7 @@ extern "C" { #include <macros.h> #include <functions.h> #include "2s2h_assets.h" +#include "assets/archives/icon_item_static/icon_item_static_yar.h" #include "overlays/actors/ovl_En_Si/z_en_si.h" } @@ -24,403 +25,227 @@ extern std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow; #define FORMAT_COUNT "{}/{}" -ImVec4 trackerWindowOpacity = ImVec4(0, 0, 0, 0.5f); +std::vector<TrackerGroup> itemTrackerGroups; -std::vector<ImVec4> dungeonKeyColors = { - { 0.9f, 0.33f, 0.56f, 0.4f }, - { 0.1f, 0.54f, 0.16f, 0.4f }, - { 0.61f, 0.04f, 0.86f, 0.4f }, - { 0.58f, 0.65f, 0.15f, 0.4f }, -}; - -extern TrackerImageObject GetTextureObject(int16_t itemId, bool isRandoItem) { - int16_t currentItemId = ITEM_NONE; - int16_t bottleId = 0; +TrackerImageObject GetImageObject(TrackerItemType itemType, u32 itemId) { bool itemObtained = false; + TrackerImageObject trackerImageObject = { + .textureColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f), + .textureDimensions = ImVec2(ITEM_TEXTURE_SIZE, ITEM_TEXTURE_SIZE), + }; - if (isRandoItem) { - TrackerImageObject randoImageObject; - randoImageObject.textureColor = Ship_GetRandoItemColorTint(itemId); - - switch (itemId) { - case RI_FROG_BLUE: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_33_01); - break; - case RI_FROG_CYAN: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_32_40); - break; - case RI_FROG_PINK: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_32_80); - break; - case RI_FROG_WHITE: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_33_02); - break; - case RI_OWL_CLOCK_TOWN_SOUTH: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_CLOCK_TOWN); - break; - case RI_OWL_GREAT_BAY_COAST: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_GREAT_BAY_COAST); - break; - case RI_OWL_IKANA_CANYON: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_IKANA_CANYON); - break; - case RI_OWL_MILK_ROAD: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_MILK_ROAD); - break; - case RI_OWL_MOUNTAIN_VILLAGE: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_MOUNTAIN_VILLAGE); - break; - case RI_OWL_SNOWHEAD: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_SNOWHEAD); - break; - case RI_OWL_SOUTHERN_SWAMP: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_SOUTHERN_SWAMP); - break; - case RI_OWL_STONE_TOWER: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_STONE_TOWER); - break; - case RI_OWL_WOODFALL: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_WOODFALL); - break; - case RI_OWL_ZORA_CAPE: - itemObtained = GET_OWL_STATUE_ACTIVATED(OWL_WARP_ZORA_CAPE); - break; - case RI_SOUL_BOSS_GOHT: - case RI_SOUL_BOSS_GYORG: - case RI_SOUL_BOSS_MAJORA: - case RI_SOUL_BOSS_ODOLWA: - case RI_SOUL_BOSS_TWINMOLD: - case RI_SOUL_ENEMY_ALIEN: - case RI_SOUL_ENEMY_ARMOS: - case RI_SOUL_ENEMY_BAD_BAT: - case RI_SOUL_ENEMY_BEAMOS: - case RI_SOUL_ENEMY_BOE: - case RI_SOUL_ENEMY_BUBBLE: - case RI_SOUL_ENEMY_CAPTAIN_KEETA: - case RI_SOUL_ENEMY_CHUCHU: - case RI_SOUL_ENEMY_DEATH_ARMOS: - case RI_SOUL_ENEMY_DEEP_PYTHON: - case RI_SOUL_ENEMY_DEKU_BABA: - case RI_SOUL_ENEMY_DEXIHAND: - case RI_SOUL_ENEMY_DINOLFOS: - case RI_SOUL_ENEMY_DODONGO: - case RI_SOUL_ENEMY_DRAGONFLY: - case RI_SOUL_ENEMY_EENO: - case RI_SOUL_ENEMY_EYEGORE: - case RI_SOUL_ENEMY_FREEZARD: - case RI_SOUL_ENEMY_GARO: - case RI_SOUL_ENEMY_GEKKO: - case RI_SOUL_ENEMY_GIANT_BEE: - case RI_SOUL_ENEMY_GOMESS: - case RI_SOUL_ENEMY_GUAY: - case RI_SOUL_ENEMY_HIPLOOP: - case RI_SOUL_ENEMY_IGOS_DU_IKANA: - case RI_SOUL_ENEMY_IRON_KNUCKLE: - case RI_SOUL_ENEMY_KEESE: - case RI_SOUL_ENEMY_LEEVER: - case RI_SOUL_ENEMY_LIKE_LIKE: - case RI_SOUL_ENEMY_MAD_SCRUB: - case RI_SOUL_ENEMY_NEJIRON: - case RI_SOUL_ENEMY_OCTOROK: - case RI_SOUL_ENEMY_PEAHAT: - case RI_SOUL_ENEMY_PIRATE: - case RI_SOUL_ENEMY_POE: - case RI_SOUL_ENEMY_REDEAD: - case RI_SOUL_ENEMY_SHELLBLADE: - case RI_SOUL_ENEMY_SKULLFISH: - case RI_SOUL_ENEMY_SKULLTULA: - case RI_SOUL_ENEMY_SNAPPER: - case RI_SOUL_ENEMY_STALCHILD: - case RI_SOUL_ENEMY_TAKKURI: - case RI_SOUL_ENEMY_TEKTITE: - case RI_SOUL_ENEMY_WALLMASTER: - case RI_SOUL_ENEMY_WART: - case RI_SOUL_ENEMY_WIZROBE: - case RI_SOUL_ENEMY_WOLFOS: - itemObtained = Flags_GetRandoInf(SOUL_RI_TO_RANDO_INF(itemId)); - break; - case RI_TINGLE_MAP_CLOCK_TOWN: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_CLOCK_TOWN); - break; - case RI_TINGLE_MAP_WOODFALL: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_WOODFALL); - break; - case RI_TINGLE_MAP_SNOWHEAD: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_SNOWHEAD); - break; - case RI_TINGLE_MAP_ROMANI_RANCH: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_ROMANI_RANCH); - break; - case RI_TINGLE_MAP_GREAT_BAY: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_GREAT_BAY); - break; - case RI_TINGLE_MAP_STONE_TOWER: - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_TINGLE_MAP_BOUGHT_STONE_TOWER); - break; - case RI_TIME_DAY_1: - case RI_TIME_DAY_2: - case RI_TIME_DAY_3: - randoImageObject.textureColor = ImVec4(1.0f, 0.9f, 0.3f, 1.0f); // Yellow/gold for sun - itemObtained = Flags_GetRandoInf( - static_cast<RandoInf>(RANDO_INF_OBTAINED_CLOCK_DAY_1 + - Rando::ClockItems::GetHalfDayIndexFromClockItem((RandoItemId)itemId))); - break; - case RI_TIME_NIGHT_1: - case RI_TIME_NIGHT_2: - case RI_TIME_NIGHT_3: - randoImageObject.textureColor = ImVec4(0.5f, 0.7f, 1.0f, 1.0f); // Light blue for moon - itemObtained = Flags_GetRandoInf( - static_cast<RandoInf>(RANDO_INF_OBTAINED_CLOCK_DAY_1 + - Rando::ClockItems::GetHalfDayIndexFromClockItem((RandoItemId)itemId))); - break; - case RI_TRIFORCE_PIECE: - itemObtained = gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces > 0; - break; - case RI_OCARINA_BUTTON_A: - case RI_OCARINA_BUTTON_C_DOWN: - case RI_OCARINA_BUTTON_C_LEFT: - case RI_OCARINA_BUTTON_C_RIGHT: - case RI_OCARINA_BUTTON_C_UP: - itemObtained = Flags_GetRandoInf(RANDO_INF_OBTAINED_OCARINA_BUTTON_A + (itemId - RI_OCARINA_BUTTON_A)); - break; - default: - break; - } - randoImageObject.textureColor.w = itemObtained ? 1.0f : 0.4f; + switch (itemType) { + case TRACKER_ITEM_RANDO: { + RandoItemId randoItemId = (RandoItemId)itemId; + switch (randoItemId) { + case RI_GS_TOKEN_SWAMP: { + itemObtained = Inventory_GetSkullTokenCount(SCENE_KINSTA1) > 0; + } break; + case RI_GS_TOKEN_OCEAN: { + itemObtained = Inventory_GetSkullTokenCount(SCENE_KINDAN2) > 0; + } break; + case RI_WOODFALL_SMALL_KEY: { + itemObtained = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE) > 0; + } break; + case RI_SNOWHEAD_SMALL_KEY: { + itemObtained = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_SNOWHEAD_TEMPLE) > 0; + } break; + case RI_GREAT_BAY_SMALL_KEY: { + itemObtained = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_GREAT_BAY_TEMPLE) > 0; + } break; + case RI_STONE_TOWER_SMALL_KEY: { + itemObtained = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE) > 0; + } break; + case RI_WOODFALL_STRAY_FAIRY: { + itemObtained = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE] > 0; + } break; + case RI_SNOWHEAD_STRAY_FAIRY: { + itemObtained = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_SNOWHEAD_TEMPLE] > 0; + } break; + case RI_GREAT_BAY_STRAY_FAIRY: { + itemObtained = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_GREAT_BAY_TEMPLE] > 0; + } break; + case RI_STONE_TOWER_STRAY_FAIRY: { + itemObtained = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE] > 0; + } break; + default: { + itemObtained = !Rando::IsItemObtainable(randoItemId); + } break; + } - randoImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)Rando::StaticData::GetIconTexturePath((RandoItemId)itemId)); - randoImageObject.textureDimensions = - ImVec2(ITEM_TEXTURE_SIZE, - itemId >= RI_OWL_CLOCK_TOWN_SOUTH && itemId <= RI_OWL_ZORA_CAPE ? 32.0f : ITEM_TEXTURE_SIZE); + trackerImageObject.textureColor = Ship_GetRandoItemColorTint(randoItemId); + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + Rando::StaticData::GetIconTexturePath(randoItemId)); + if (randoItemId >= RI_OWL_CLOCK_TOWN_SOUTH && randoItemId <= RI_OWL_ZORA_CAPE) { + trackerImageObject.textureDimensions.y = 24.0f; + } else if (randoItemId >= RI_SONG_ELEGY && randoItemId <= RI_SONG_TIME) { + trackerImageObject.textureDimensions.x = 32.0f; + } + } break; + case TRACKER_ITEM_SLOT: { + itemObtained = gSaveContext.save.saveInfo.inventory.items[itemId] != ITEM_NONE; + auto vanillaItemId = gSaveContext.save.saveInfo.inventory.items[itemId]; + if (vanillaItemId == ITEM_NONE) { + vanillaItemId = safeItemsForInventorySlot[itemId][0]; + } - return randoImageObject; - } else { - if (itemId >= ITEM_BOTTLE_1 && itemId <= ITEM_BOTTLE_6) { - bottleId = SLOT_BOTTLE_1 + (itemId - ITEM_BOTTLE_1); - currentItemId = ITEM_BOTTLE; - } - if (itemId == ITEM_SKULL_TOKEN_SWAMP || itemId == ITEM_SKULL_TOKEN_OCEAN) { - currentItemId = ITEM_SKULL_TOKEN; - } - if (itemId >= ITEM_CLOCK_TOWN_STRAY_FAIRY && itemId <= ITEM_STONE_TOWER_STRAY_FAIRY) { - currentItemId = ITEM_STRAY_FAIRIES; - } - if (itemId >= ITEM_WOODFALL_DUNGEON_MAP && itemId <= ITEM_STONE_TOWER_KEY_BOSS) { - const int dungeonIndex = (itemId - ITEM_WOODFALL_DUNGEON_MAP) / 4; - const int itemTypeIndex = (itemId - ITEM_WOODFALL_DUNGEON_MAP) % 4; + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + (const char*)gItemIcons[vanillaItemId]); + } break; + case TRACKER_ITEM_SWORD: { + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) > EQUIP_VALUE_SWORD_NONE) { + itemObtained = true; + } + int vanillaItemId = ITEM_SWORD_KOKIRI; + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) > EQUIP_VALUE_SWORD_KOKIRI) { + vanillaItemId = ITEM_SWORD_KOKIRI + GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI; + } - switch (itemTypeIndex) { - case 0: - currentItemId = ITEM_DUNGEON_MAP; - break; - case 1: - currentItemId = ITEM_COMPASS; - break; - case 2: - currentItemId = ITEM_KEY_SMALL; - break; - case 3: - currentItemId = ITEM_KEY_BOSS; - break; + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + (const char*)gItemIcons[vanillaItemId]); + } break; + case TRACKER_ITEM_SHIELD: { + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) > EQUIP_VALUE_SHIELD_NONE) { + itemObtained = true; + } + int vanillaItemId = ITEM_SHIELD_HERO; + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) > EQUIP_VALUE_SHIELD_HERO) { + vanillaItemId = ITEM_SHIELD_MIRROR; } - } - } - if (currentItemId == ITEM_NONE) { - switch (itemId) { - case ITEM_MOONS_TEAR: - currentItemId = ITEM_MOONS_TEAR; - for (int16_t i = ITEM_MOONS_TEAR; i <= ITEM_DEED_OCEAN; i++) { - if (gSaveContext.save.saveInfo.inventory.items[SLOT_TRADE_DEED] == i) { - currentItemId = i; - break; - } - } - break; - case ITEM_ROOM_KEY: - currentItemId = ITEM_ROOM_KEY; - for (int16_t i = ITEM_ROOM_KEY; i <= ITEM_LETTER_MAMA; i++) { - if (gSaveContext.save.saveInfo.inventory.items[SLOT_TRADE_KEY_MAMA] == i) { - currentItemId = i; - break; - } - } - break; - case ITEM_LETTER_TO_KAFEI: - currentItemId = ITEM_LETTER_TO_KAFEI; - for (int16_t i = ITEM_LETTER_TO_KAFEI; i <= ITEM_PENDANT_OF_MEMORIES; i++) { - if (gSaveContext.save.saveInfo.inventory.items[SLOT_TRADE_COUPLE] == i) { - currentItemId = i; - break; - } - } - break; - case ITEM_SWORD_KOKIRI: - currentItemId = ITEM_SWORD_KOKIRI + (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI); - if (currentItemId < ITEM_SWORD_KOKIRI) { - currentItemId = ITEM_SWORD_KOKIRI; - } - break; - case ITEM_SHIELD_HERO: - currentItemId = ITEM_SHIELD_HERO + (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) - EQUIP_VALUE_SHIELD_HERO); - if (currentItemId < ITEM_SHIELD_HERO) { - currentItemId = ITEM_SHIELD_HERO; - } - break; - case ITEM_WALLET_ADULT: - currentItemId = ITEM_WALLET_ADULT + CUR_UPG_VALUE(UPG_WALLET) - 1; - if (currentItemId < ITEM_WALLET_ADULT) { - currentItemId = ITEM_WALLET_ADULT; - } - break; - case ITEM_MAGIC_JAR_SMALL: - if (gSaveContext.save.saveInfo.playerData.magicLevel <= 1) { - currentItemId = ITEM_MAGIC_JAR_SMALL; - } else { - currentItemId = ITEM_MAGIC_JAR_BIG; - } - break; - default: - currentItemId = itemId; - break; - } - } + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + (const char*)gItemIcons[vanillaItemId]); + } break; + case TRACKER_ITEM_WALLET: { + if (CUR_UPG_VALUE(UPG_WALLET) >= 1) { + itemObtained = true; + } + auto vanillaItemId = ITEM_WALLET_ADULT; + if (CUR_UPG_VALUE(UPG_WALLET) >= 2) { + vanillaItemId = ITEM_WALLET_GIANT; + } - TrackerImageObject imageObject = { - .textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)gItemIcons[currentItemId]), - .textureColor = Ship_GetItemColorTint(currentItemId), - .textureDimensions = - ImVec2(currentItemId >= ITEM_SONG_SONATA && currentItemId <= ITEM_SONG_SUN ? ITEM_TEXTURE_SIZE / 1.5f - : ITEM_TEXTURE_SIZE, - ITEM_TEXTURE_SIZE), - }; + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + (const char*)gItemIcons[vanillaItemId]); + } break; + case TRACKER_ITEM_MAGIC: { + if (gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + itemObtained = true; + } + auto vanillaItemId = ITEM_MAGIC_JAR_SMALL; + if (gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired) { + vanillaItemId = ITEM_MAGIC_JAR_BIG; + } - if (itemId >= ITEM_REMAINS_ODOLWA && itemId <= ITEM_BOMBERS_NOTEBOOK) { - itemObtained = CHECK_QUEST_ITEM(Ship_ConvertItemIdToQuest(itemId)); - } else if (itemId >= ITEM_SWORD_KOKIRI && itemId <= ITEM_SWORD_GILDED) { - itemObtained = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) >= EQUIP_VALUE_SWORD_KOKIRI; - } else if (itemId == ITEM_SHIELD_HERO || itemId == ITEM_SHIELD_MIRROR) { - itemObtained = GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) >= EQUIP_VALUE_SHIELD_HERO; - } else if (itemId == ITEM_WALLET_ADULT || itemId == ITEM_WALLET_GIANT) { - itemObtained = CUR_UPG_VALUE(UPG_WALLET) >= 1; - } else if (itemId == ITEM_MAGIC_JAR_SMALL || itemId == ITEM_MAGIC_JAR_BIG) { - itemObtained = gSaveContext.save.saveInfo.playerData.magicLevel != 0; - } else if (itemId == ITEM_HEART_CONTAINER) { - itemObtained = gSaveContext.save.saveInfo.playerData.doubleDefense; - } else if (itemId >= ITEM_BOTTLE_1 && itemId <= ITEM_BOTTLE_6) { - if (gSaveContext.save.saveInfo.inventory.items[bottleId] != ITEM_NONE && gPlayState) { - imageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)gItemIcons[gSaveContext.save.saveInfo.inventory.items[bottleId]]); - } else { - imageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)gItemIcons[ITEM_BOTTLE]); - } - itemObtained = gSaveContext.save.saveInfo.inventory.items[bottleId] != ITEM_NONE; - } else if (itemId == ITEM_SKULL_TOKEN_SWAMP || itemId == ITEM_SKULL_TOKEN_OCEAN) { - uint32_t tokenCount = 0; - if (itemId == ITEM_SKULL_TOKEN_SWAMP) { - tokenCount = Inventory_GetSkullTokenCount(SCENE_KINSTA1); - } else { - tokenCount = Inventory_GetSkullTokenCount(SCENE_KINDAN2); - } - itemObtained = tokenCount > 0; - } else if (itemId >= ITEM_CLOCK_TOWN_STRAY_FAIRY && itemId <= ITEM_STONE_TOWER_STRAY_FAIRY) { - if (itemId == ITEM_CLOCK_TOWN_STRAY_FAIRY) { - imageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)fairyIconTextures[0]); - imageObject.textureColor = ImVec4(1.0f, 0.9f, 0.5f, 0.4f); - itemObtained = CHECK_WEEKEVENTREG(WEEKEVENTREG_08_80); - } else { - imageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( - (const char*)fairyIconTextures[itemId - ITEM_WOODFALL_STRAY_FAIRY]); - itemObtained = gSaveContext.save.saveInfo.inventory.strayFairies[itemId - ITEM_WOODFALL_STRAY_FAIRY] > 0; - } - } else if (itemId >= ITEM_WOODFALL_DUNGEON_MAP && itemId <= ITEM_STONE_TOWER_KEY_BOSS) { - const int dungeonIndex = (itemId - ITEM_WOODFALL_DUNGEON_MAP) / 4; - const int itemTypeIndex = (itemId - ITEM_WOODFALL_DUNGEON_MAP) % 4; - switch (itemTypeIndex) { - case 0: - itemObtained = CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, dungeonIndex); - break; - case 1: - itemObtained = CHECK_DUNGEON_ITEM(DUNGEON_MAP, dungeonIndex); - break; - case 2: - imageObject.textureColor = dungeonKeyColors[dungeonIndex]; - itemObtained = DUNGEON_KEY_COUNT(dungeonIndex) > 0; - break; - case 3: - imageObject.textureColor = dungeonKeyColors[dungeonIndex]; - itemObtained = CHECK_DUNGEON_ITEM(DUNGEON_BOSS_KEY, dungeonIndex); - break; - } - } else { - itemObtained = INV_CONTENT(itemId) != ITEM_NONE; + trackerImageObject.textureId = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName( + (const char*)gItemIcons[vanillaItemId]); + } break; + default: + break; } - imageObject.textureColor.w = itemObtained ? 1.0f : 0.4f; - return imageObject; + trackerImageObject.textureColor.w = itemObtained ? 1.0f : 0.4f; + return trackerImageObject; } -std::string GetItemCounts(int16_t itemId, bool isRandoItem) { +std::string GetItemCounts(TrackerItemType itemType, u32 itemId) { std::string countStr = ""; - if (isRandoItem) { - if (itemId == RI_TRIFORCE_PIECE) { - int16_t maxPieces = gSaveContext.save.shipSaveInfo.rando.randoSaveOptions[RO_TRIFORCE_PIECES_REQUIRED]; - countStr = fmt::format(FORMAT_COUNT, gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces, - maxPieces == 1000 ? "1k" : std::to_string(maxPieces)); - } - } else { - int dungeonIndex = 0; - switch (itemId) { - case ITEM_BOW: - case ITEM_BOMB: - case ITEM_BOMBCHU: - case ITEM_DEKU_STICK: - case ITEM_DEKU_NUT: - case ITEM_MAGIC_BEANS: - case ITEM_POWDER_KEG: - countStr = std::to_string(AMMO(itemId)); - break; - case ITEM_WALLET_ADULT: - countStr = std::to_string(gSaveContext.save.saveInfo.playerData.rupees); - break; - case ITEM_PICTOGRAPH_BOX: - countStr = CHECK_QUEST_ITEM(QUEST_PICTOGRAPH) ? "1" : "0"; - break; - case ITEM_SKULL_TOKEN_SWAMP: - case ITEM_SKULL_TOKEN_OCEAN: - countStr = fmt::format( - FORMAT_COUNT, - Inventory_GetSkullTokenCount(itemId == ITEM_SKULL_TOKEN_SWAMP ? SCENE_KINSTA1 : SCENE_KINDAN2), - IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_SKULLTULA_TOKENS] : SPIDER_HOUSE_TOKENS_REQUIRED); - break; - case ITEM_WOODFALL_STRAY_FAIRY: - case ITEM_SNOWHEAD_STRAY_FAIRY: - case ITEM_GREAT_BAY_STRAY_FAIRY: - case ITEM_STONE_TOWER_STRAY_FAIRY: - countStr = fmt::format( - FORMAT_COUNT, gSaveContext.save.saveInfo.inventory.strayFairies[itemId - ITEM_WOODFALL_STRAY_FAIRY], - IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_STRAY_FAIRIES] : STRAY_FAIRY_SCATTERED_TOTAL); - break; - case ITEM_WOODFALL_KEY_SMALL: - case ITEM_SNOWHEAD_KEY_SMALL: - case ITEM_GREAT_BAY_KEY_SMALL: - case ITEM_STONE_TOWER_KEY_SMALL: - dungeonIndex = (itemId - ITEM_WOODFALL_DUNGEON_MAP) / 4; - countStr = DUNGEON_KEY_COUNT(dungeonIndex) < 0 ? "0" : std::to_string(DUNGEON_KEY_COUNT(dungeonIndex)); - break; - default: - break; - } + + switch (itemType) { + case TRACKER_ITEM_RANDO: { + switch (itemId) { + case RI_TRIFORCE_PIECE: { + auto max = RANDO_SAVE_OPTIONS[RO_TRIFORCE_PIECES_REQUIRED]; + countStr = fmt::format(FORMAT_COUNT, gSaveContext.save.shipSaveInfo.rando.foundTriforcePieces, + max > 999 ? "1k" : std::to_string(max)); + } break; + case RI_GS_TOKEN_OCEAN: + case RI_GS_TOKEN_SWAMP: { + auto max = + IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_SKULLTULA_TOKENS] : SPIDER_HOUSE_TOKENS_REQUIRED; + auto count = + Inventory_GetSkullTokenCount(itemId == RI_GS_TOKEN_SWAMP ? SCENE_KINSTA1 : SCENE_KINDAN2); + countStr = fmt::format(FORMAT_COUNT, count, max); + } break; + case RI_WOODFALL_STRAY_FAIRY: { + auto max = IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_STRAY_FAIRIES] : STRAY_FAIRY_SCATTERED_TOTAL; + auto count = gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE]; + countStr = fmt::format(FORMAT_COUNT, count, max); + } break; + case RI_SNOWHEAD_STRAY_FAIRY: { + auto max = IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_STRAY_FAIRIES] : STRAY_FAIRY_SCATTERED_TOTAL; + auto count = gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_SNOWHEAD_TEMPLE]; + countStr = fmt::format(FORMAT_COUNT, count, max); + } break; + case RI_GREAT_BAY_STRAY_FAIRY: { + auto max = IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_STRAY_FAIRIES] : STRAY_FAIRY_SCATTERED_TOTAL; + auto count = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_GREAT_BAY_TEMPLE]; + countStr = fmt::format(FORMAT_COUNT, count, max); + } break; + case RI_STONE_TOWER_STRAY_FAIRY: { + auto max = IS_RANDO ? RANDO_SAVE_OPTIONS[RO_MINIMUM_STRAY_FAIRIES] : STRAY_FAIRY_SCATTERED_TOTAL; + auto count = + gSaveContext.save.saveInfo.inventory.strayFairies[DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE]; + countStr = fmt::format(FORMAT_COUNT, count, max); + } break; + case RI_WOODFALL_SMALL_KEY: { + auto count = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_WOODFALL_TEMPLE); + if (count > 0) + countStr = fmt::format(FORMAT_COUNT, count, 1); + } break; + case RI_SNOWHEAD_SMALL_KEY: { + auto count = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_SNOWHEAD_TEMPLE); + if (count > 0) + countStr = fmt::format(FORMAT_COUNT, count, 3); + } break; + case RI_GREAT_BAY_SMALL_KEY: { + auto count = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_GREAT_BAY_TEMPLE); + if (count > 0) + countStr = fmt::format(FORMAT_COUNT, count, 1); + } break; + case RI_STONE_TOWER_SMALL_KEY: { + auto count = DUNGEON_KEY_COUNT(DUNGEON_SCENE_INDEX_STONE_TOWER_TEMPLE); + if (count > 0) + countStr = fmt::format(FORMAT_COUNT, count, 4); + } break; + default: + break; + } + } break; + case TRACKER_ITEM_SLOT: { + switch (itemId) { + case SLOT_BOW: + case SLOT_BOMB: + case SLOT_BOMBCHU: + case SLOT_DEKU_STICK: + case SLOT_DEKU_NUT: + case SLOT_MAGIC_BEANS: + case SLOT_POWDER_KEG: { + auto count = gSaveContext.save.saveInfo.inventory.ammo[itemId]; + if (count > 0) + countStr = std::to_string(count); + } break; + default: + break; + } + } break; + case TRACKER_ITEM_WALLET: { + countStr = std::to_string(CUR_CAPACITY(UPG_WALLET)); + } break; + default: + break; } + return countStr; } -void DrawItemCounts(int16_t itemId, bool isRandoItem, ImVec2 textureSize, float scale, ImVec2 currentPos) { - std::string itemCount = GetItemCounts(itemId, isRandoItem); +void DrawItemCounts(TrackerItemType itemType, u32 itemId, ImVec2 textureSize, float scale, ImVec2 currentPos) { + std::string itemCount = GetItemCounts(itemType, itemId); if (itemCount.empty()) { return; @@ -434,36 +259,121 @@ void DrawItemCounts(int16_t itemId, bool isRandoItem, ImVec2 textureSize, float ImGui::Text(itemCount.c_str()); } -void DrawItemSlot(int16_t itemId, float scale, bool isRandoItem) { +bool DrawItemTrackerSlot(TrackerItemType itemType, u32 itemId, float scale, bool clickable) { ImVec2 currentPos = ImGui::GetCursorPos(); - TrackerImageObject imageObject = GetTextureObject(itemId, isRandoItem); - ImGui::Image(imageObject.textureId, - ImVec2(imageObject.textureDimensions.x * scale, imageObject.textureDimensions.y * scale), ImVec2(0, 0), - ImVec2(1, 1), imageObject.textureColor, ImVec4(0, 0, 0, 0)); - UIWidgets::Tooltip(GetItemTrackerItemName(itemId, isRandoItem).c_str()); - if (CVarGetInteger("gSettings.ItemTracker.ItemCounts", 0)) { - DrawItemCounts(itemId, isRandoItem, imageObject.textureDimensions * scale, scale, currentPos); + ImVec2 cellSize(ITEM_TEXTURE_SIZE * scale, ITEM_TEXTURE_SIZE * scale); + + TrackerImageObject imageObject = GetImageObject(itemType, itemId); + + // Create an invisible button for layout + clicks + bool clicked = false; + if (clickable) { + imageObject.textureColor.w = 1.0f; // force full opacity for clickable items + clicked = ImGui::InvisibleButton(std::to_string(itemId).c_str(), cellSize); + } else { + ImGui::Dummy(cellSize); + } + + // Draw image centered inside the cell + ImVec2 p0 = ImGui::GetItemRectMin(); + ImVec2 p1 = ImGui::GetItemRectMax(); + + if (clickable) { + bool hovered = ImGui::IsItemHovered(); + bool held = ImGui::IsItemActive(); + + ImU32 bg; + if (held) { + bg = IM_COL32(0, 0, 0, 60); // pressed + } else if (hovered) { + bg = IM_COL32(255, 255, 255, 20); // hover + } else { + bg = IM_COL32(0, 0, 0, 0); // idle subtle + } + + ImGui::GetWindowDrawList()->AddRectFilled(p0, p1, bg, 4.0f); // small rounding + } + + float aspect = imageObject.textureDimensions.x / imageObject.textureDimensions.y; + + // fit to height + ImVec2 drawSize(cellSize.y * aspect, cellSize.y); + + // clamp if too wide + if (drawSize.x > cellSize.x) { + drawSize.x = cellSize.x; + drawSize.y = cellSize.x / aspect; + } + + ImVec2 offset((cellSize.x - drawSize.x) * 0.5f, (cellSize.y - drawSize.y) * 0.5f); + + // Draw a glow behind skulltula tokens + if (itemType == TRACKER_ITEM_RANDO && (itemId == RI_GS_TOKEN_SWAMP || itemId == RI_GS_TOKEN_OCEAN)) { + ImVec4 tintColor = ImVec4(25.0f / 255.0f, 251.0f / 255.0f, 0.0f, imageObject.textureColor.w); // Swamp tint + if (itemId == RI_GS_TOKEN_OCEAN) { + tintColor = ImVec4(0.0f, 209.0f / 256.0f, 231.0f / 256.0f, imageObject.textureColor.w); // Ocean tint + } + auto textureId = + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gMagicArrowEquipEffectTex); + + // Draw texture behind the actual item icon + ImGui::GetWindowDrawList()->AddImage(textureId, p0 + offset - ImVec2(8.0f, 8.0f), + p0 + offset + drawSize + ImVec2(8.0f, 8.0f), ImVec2(0, 0), ImVec2(1, 1), + ImGui::GetColorU32(tintColor)); + } + + if (itemType == TRACKER_ITEM_RANDO && itemId >= RI_SOUL_BOSS_GOHT && itemId <= RI_SOUL_BOSS_TWINMOLD) { + ImVec4 tintColor = + ImVec4(255.0f / 255.0f, 255.0f / 255.0f, 255.0f / 255.0f, imageObject.textureColor.w); // Swamp tint + auto textureId = + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(gMagicArrowEquipEffectTex); + + // Draw texture behind the actual item icon + ImGui::GetWindowDrawList()->AddImage(textureId, p0 + offset - ImVec2(8.0f, 8.0f), + p0 + offset + drawSize + ImVec2(8.0f, 8.0f), ImVec2(0, 0), ImVec2(1, 1), + ImGui::GetColorU32(tintColor)); + } + + ImGui::GetWindowDrawList()->AddImage(imageObject.textureId, p0 + offset, p0 + offset + drawSize, ImVec2(0, 0), + ImVec2(1, 1), ImGui::GetColorU32(imageObject.textureColor)); + auto itemName = GetItemTrackerItemName(itemType, itemId); + if (!itemName.empty()) { + UIWidgets::Tooltip(itemName.c_str()); } + + // Save last item data before drawing counts (which uses ImGui::Text and changes last item) + ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiLastItemData backup = g.LastItemData; + + if (CVarGetInteger("gSettings.ItemTracker.ItemCounts", 1)) { + DrawItemCounts(itemType, itemId, cellSize, scale, currentPos); + } + + // Restore last item data so drag/drop operations work correctly + g.LastItemData = backup; + + return clicked; } -void DrawItemWindowList(TrackerItemListObject windowObject, bool isRandoItem) { - int columns = windowObject.columnLength; - if (windowObject.itemList.size() < windowObject.columnLength) { - columns = windowObject.itemList.size(); +void DrawItemTrackerGroup(TrackerGroup& trackerGroup) { + int columns = trackerGroup.columns; + if (trackerGroup.items.size() < trackerGroup.columns) { + columns = trackerGroup.items.size(); } - if (ImGui::BeginTable(windowObject.windowName.c_str(), columns)) { - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(5, 5)); + float scale = CVarGetInteger("gSettings.ItemTracker.WindowGroup", 0) + ? trackerGroup.scale + : CVarGetFloat("gSettings.ItemTracker.Scale", 1.0f); - for (auto& item : windowObject.itemList) { + if (ImGui::BeginTable(trackerGroup.name.c_str(), columns)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + for (auto& [itemType, itemId] : trackerGroup.items) { ImGui::TableNextColumn(); - ImVec2 framePadding = ImVec2(item >= ITEM_SONG_SONATA && item <= ITEM_SONG_SUN ? ITEM_SONG_PADDING : 0, 0); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, framePadding); - DrawItemSlot(item, windowObject.windowScale, isRandoItem); - ImGui::PopStyleVar(1); + DrawItemTrackerSlot(itemType, itemId, scale, false); } - ImGui::PopStyleVar(1); + ImGui::PopStyleVar(2); ImGui::EndTable(); } } @@ -484,64 +394,52 @@ void ItemTrackerWindow::Draw() { if (!CVarGetInteger("gSettings.ItemTracker.WindowType", 0)) { windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking; } - shouldWindowSplit = CVarGetInteger("gSettings.ItemTracker.WindowGroup", 0); - - std::vector<std::vector<TrackerItemListObject>*> itemTrackerWindows = { - &BenGui::mItemTrackerWindow->namedItemWindows, - &BenGui::mItemTrackerWindow->randoItemWindows, - }; + bool shouldWindowSplit = CVarGetInteger("gSettings.ItemTracker.WindowGroup", 0); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); - uint32_t windowIndex = TRACKER_MAIN; - for (auto* window : itemTrackerWindows) { - if (!window->empty()) { - bool singleWindowOpen = false; - if (!shouldWindowSplit) { - ImVec4 mainBg = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); - mainBg.w = (*window)[0].windowOpacity; - ImGui::PushStyleColor(ImGuiCol_WindowBg, mainBg); - singleWindowOpen = - ImGui::Begin(windowIndex == TRACKER_MAIN ? "Main Tracker" : "Rando Tracker", nullptr, windowFlags); - } + // Adjust opacity + ImVec4 windowBg = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + windowBg.w = CVarGetFloat("gSettings.ItemTracker.Opacity", 0.5f); - uint32_t index = 0; - for (auto& object : *window) { - if (object.itemList.empty()) { - index++; - continue; - } - - bool isWindowOpen = false; - - if (shouldWindowSplit) { - ImVec4 bg = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); - bg.w = object.windowOpacity; - ImGui::PushStyleColor(ImGuiCol_WindowBg, bg); - std::string windowName = std::string(object.windowName) + "##" + std::to_string(index); - isWindowOpen = ImGui::Begin(windowName.c_str(), nullptr, windowFlags); - } else { - isWindowOpen = singleWindowOpen; - } - - if (isWindowOpen) { - DrawItemWindowList(object, windowIndex == TRACKER_MAIN ? false : true); - } - - if (shouldWindowSplit) { - ImGui::PopStyleColor(1); - ImGui::End(); - } - - index++; - } - if (!shouldWindowSplit) { - ImGui::PopStyleColor(1); - ImGui::End(); - } + bool singleWindowOpen = false; + if (!shouldWindowSplit) { + ImGui::PushStyleColor(ImGuiCol_WindowBg, windowBg); + singleWindowOpen = ImGui::Begin("Item Tracker", nullptr, windowFlags); + } + + uint32_t index = 0; + for (auto& group : itemTrackerGroups) { + if (group.items.empty()) { + index++; + continue; + } + + bool isWindowOpen = false; + + if (shouldWindowSplit) { + ImGui::PushStyleColor(ImGuiCol_WindowBg, windowBg); + std::string name = std::string(group.name) + "##" + std::to_string(index); + isWindowOpen = ImGui::Begin(name.c_str(), nullptr, windowFlags); + } else { + isWindowOpen = singleWindowOpen; } - windowIndex++; + + if (isWindowOpen) { + DrawItemTrackerGroup(group); + } + + if (shouldWindowSplit) { + ImGui::PopStyleColor(1); + ImGui::End(); + } + + index++; + } + if (!shouldWindowSplit) { + ImGui::PopStyleColor(1); + ImGui::End(); } ImGui::PopStyleColor(1); diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.h b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.h index 652b3a22b..2c650bd01 100644 --- a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.h +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTracker.h @@ -3,44 +3,15 @@ #include "ship/window/gui/GuiWindow.h" #define ITEM_TEXTURE_SIZE 46.0f -#define ITEM_SONG_PADDING 8.0f typedef enum { - TRACKER_MAIN, - TRACKER_RANDO, -} TrackerWindowTypes; - -typedef enum { - ITEM_BOTTLE_1 = 0x100, - ITEM_BOTTLE_2, - ITEM_BOTTLE_3, - ITEM_BOTTLE_4, - ITEM_BOTTLE_5, - ITEM_BOTTLE_6, - ITEM_SKULL_TOKEN_SWAMP, - ITEM_SKULL_TOKEN_OCEAN, - ITEM_CLOCK_TOWN_STRAY_FAIRY, - ITEM_WOODFALL_STRAY_FAIRY, - ITEM_SNOWHEAD_STRAY_FAIRY, - ITEM_GREAT_BAY_STRAY_FAIRY, - ITEM_STONE_TOWER_STRAY_FAIRY, - ITEM_WOODFALL_DUNGEON_MAP, - ITEM_WOODFALL_DUNGEON_COMPASS, - ITEM_WOODFALL_KEY_SMALL, - ITEM_WOODFALL_KEY_BOSS, - ITEM_SNOWHEAD_DUNGEON_MAP, - ITEM_SNOWHEAD_DUNGEON_COMPASS, - ITEM_SNOWHEAD_KEY_SMALL, - ITEM_SNOWHEAD_KEY_BOSS, - ITEM_GREAT_BAY_DUNGEON_MAP, - ITEM_GREAT_BAY_DUNGEON_COMPASS, - ITEM_GREAT_BAY_KEY_SMALL, - ITEM_GREAT_BAY_KEY_BOSS, - ITEM_STONE_TOWER_DUNGEON_MAP, - ITEM_STONE_TOWER_DUNGEON_COMPASS, - ITEM_STONE_TOWER_KEY_SMALL, - ITEM_STONE_TOWER_KEY_BOSS, -} TrackerTokenSlots; + TRACKER_ITEM_RANDO, + TRACKER_ITEM_SLOT, + TRACKER_ITEM_SWORD, + TRACKER_ITEM_SHIELD, + TRACKER_ITEM_WALLET, + TRACKER_ITEM_MAGIC, +} TrackerItemType; typedef struct { ImTextureID textureId; @@ -49,25 +20,21 @@ typedef struct { } TrackerImageObject; typedef struct { - std::string windowName; - int32_t columnLength; - float windowScale; - float windowOpacity; - std::vector<int16_t> itemList; -} TrackerItemListObject; + std::string name; + u8 columns; + float scale; + std::vector<std::pair<TrackerItemType, u32>> items; +} TrackerGroup; -TrackerImageObject GetTextureObject(int16_t itemId, bool isRandoItem = false); -extern std::string GetItemTrackerItemName(int16_t itemId, bool isRandoItem = false); -extern bool shouldWindowSplit; +extern std::vector<TrackerGroup> itemTrackerGroups; +extern bool DrawItemTrackerSlot(TrackerItemType itemType, u32 itemId, float scale, bool clickable); +extern std::string GetItemTrackerItemName(TrackerItemType itemType, u32 itemId); class ItemTrackerWindow : public Ship::GuiWindow { public: using GuiWindow::GuiWindow; void Draw() override; - std::vector<TrackerItemListObject> namedItemWindows; - std::vector<TrackerItemListObject> randoItemWindows; - protected: void InitElement() override; void DrawElement() override; diff --git a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTrackerSettings.cpp b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTrackerSettings.cpp index e02be5b4e..a17d1815c 100644 --- a/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTrackerSettings.cpp +++ b/mm/2s2h/Enhancements/Trackers/ItemTracker/ItemTrackerSettings.cpp @@ -3,6 +3,7 @@ #include "2s2h/BenGui/UIWidgets.hpp" #include "ShipUtils.h" #include "ship/config/Config.h" +#include "2s2h/ShipInit.hpp" namespace BenGui { extern std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow; @@ -15,358 +16,206 @@ void ItemTrackerSettingsWindow::UpdateElement() { static const char* windowTypes[2] = { "Floating", "Window" }; -bool shouldWindowSplit = false; -int16_t popupSlot = SLOT_OCARINA; -int16_t indexToRemove = ITEM_OCARINA_OF_TIME; -static std::string trackerInputName; -bool shouldTrackerPopUpOpen = false; -bool shouldRemove = false; +std::vector<TrackerGroup> itemTrackerGroupsAvailable; +void SaveItemTrackerLayout(); -std::vector<std::string> listOrder = { - "Inventory", "Bottles", "Masks", "Songs", "Quest", "Tokens", "Stray Fairies", "Dungeon", -}; - -std::vector<std::string> randoListOrder = { - "Frogs", "Ocarina Buttons", "Boss Souls", "Enemy Souls", "Owl Statues", "Time", "Tingle Maps", "Misc", -}; - -std::map<std::string, std::tuple<int16_t, int16_t, int16_t>> defaultItemLists = { - { "Inventory", { ITEM_OCARINA_OF_TIME, ITEM_LETTER_TO_KAFEI, 6 } }, - { "Bottles", { ITEM_BOTTLE_1, ITEM_BOTTLE_6, 6 } }, - { "Masks", { ITEM_MASK_POSTMAN, ITEM_MASK_FIERCE_DEITY, 6 } }, - { "Songs", { ITEM_SONG_TIME, ITEM_SONG_OATH, 5 } }, - { "Quest", { ITEM_REMAINS_ODOLWA, ITEM_BOMBERS_NOTEBOOK, 4 } }, - { "Tokens", { ITEM_SKULL_TOKEN_SWAMP, ITEM_SKULL_TOKEN_OCEAN, 2 } }, - { "Stray Fairies", { ITEM_CLOCK_TOWN_STRAY_FAIRY, ITEM_STONE_TOWER_STRAY_FAIRY, 5 } }, - { "Dungeon", { ITEM_WOODFALL_DUNGEON_MAP, ITEM_STONE_TOWER_KEY_BOSS, 4 } }, -}; - -std::map<std::string, std::tuple<int16_t, int16_t, int16_t>> randoItemLists = { - { "Frogs", { RI_FROG_BLUE, RI_FROG_WHITE, 4 } }, - { "Ocarina Buttons", { RI_OCARINA_BUTTON_A, RI_OCARINA_BUTTON_C_UP, 5 } }, - { "Boss Souls", { RI_SOUL_BOSS_GOHT, RI_SOUL_BOSS_TWINMOLD, 5 } }, - { "Enemy Souls", { RI_SOUL_ENEMY_ALIEN, RI_SOUL_ENEMY_WOLFOS, 6 } }, - { "Owl Statues", { RI_OWL_CLOCK_TOWN_SOUTH, RI_OWL_ZORA_CAPE, 5 } }, - { "Tingle Maps", { RI_TINGLE_MAP_CLOCK_TOWN, RI_TINGLE_MAP_WOODFALL, 6 } }, - { "Time", { RI_TIME_DAY_1, RI_TIME_NIGHT_3, 6 } }, - { "Misc", { RI_TRIFORCE_PIECE, RI_TRIFORCE_PIECE, 1 } }, -}; - -std::vector<std::string> dungeonPrefix = { - "Woodfall", - "Snowhead", - "Great Bay", - "Stone Tower", -}; - -std::pair<uint32_t, uint32_t> GetItemMapRange(uint32_t start, uint32_t end) { - std::pair<uint32_t, uint32_t> indexRange; - - for (size_t i = 0; i < itemIdToItemNameMap.size(); i++) { - if (itemIdToItemNameMap[i].first == start) { - indexRange.first = static_cast<int>(i); - } - if (itemIdToItemNameMap[i].first == end) { - indexRange.second = static_cast<int>(i); - } +std::vector<std::pair<TrackerItemType, u32>> GetItemsFromRange(TrackerItemType itemType, u32 start, u32 end) { + std::vector<std::pair<TrackerItemType, u32>> items; + for (u32 itemId = start; itemId <= end; itemId++) { + items.push_back({ itemType, itemId }); } - - return indexRange; + return items; } -std::string GetItemTrackerItemName(int16_t itemId, bool isRandoItem) { - std::string itemName = ""; - uint16_t fallBackCheck = 0; - if (isRandoItem) { - itemName = Rando::StaticData::Items[(RandoItemId)itemId].name; - } else { - if (itemId >= ITEM_BOTTLE_1 && itemId <= ITEM_BOTTLE_6) { - itemName = "Bottle"; - } else if (itemId == ITEM_SKULL_TOKEN_SWAMP) { - itemName = "Swamp Token"; - } else if (itemId == ITEM_SKULL_TOKEN_OCEAN) { - itemName = "Ocean Token"; - } else if (itemId == ITEM_CLOCK_TOWN_STRAY_FAIRY) { - itemName = "Clock Town Stray Fairy"; - } else if (itemId >= ITEM_WOODFALL_STRAY_FAIRY && itemId <= ITEM_STONE_TOWER_STRAY_FAIRY) { - itemName = dungeonPrefix[itemId - ITEM_WOODFALL_STRAY_FAIRY] + " Stray Fairy"; - } else if (itemId >= ITEM_WOODFALL_DUNGEON_MAP && itemId <= ITEM_STONE_TOWER_KEY_BOSS) { - switch (itemId) { - case ITEM_WOODFALL_DUNGEON_MAP: - case ITEM_SNOWHEAD_DUNGEON_MAP: - case ITEM_GREAT_BAY_DUNGEON_MAP: - case ITEM_STONE_TOWER_DUNGEON_MAP: - // Note the Map and Compass are swapped in vanilla code, this is correcting that. - itemName = dungeonPrefix[(itemId - ITEM_WOODFALL_DUNGEON_MAP) / 4] + " Compass"; - break; - case ITEM_WOODFALL_DUNGEON_COMPASS: - case ITEM_SNOWHEAD_DUNGEON_COMPASS: - case ITEM_GREAT_BAY_DUNGEON_COMPASS: - case ITEM_STONE_TOWER_DUNGEON_COMPASS: - itemName = dungeonPrefix[(itemId - ITEM_WOODFALL_DUNGEON_COMPASS) / 4] + " Map"; - break; - case ITEM_WOODFALL_KEY_SMALL: - case ITEM_SNOWHEAD_KEY_SMALL: - case ITEM_GREAT_BAY_KEY_SMALL: - case ITEM_STONE_TOWER_KEY_SMALL: - itemName = dungeonPrefix[(itemId - ITEM_WOODFALL_KEY_SMALL) / 4] + " Small Key"; - break; - case ITEM_WOODFALL_KEY_BOSS: - case ITEM_SNOWHEAD_KEY_BOSS: - case ITEM_GREAT_BAY_KEY_BOSS: - case ITEM_STONE_TOWER_KEY_BOSS: - itemName = dungeonPrefix[(itemId - ITEM_WOODFALL_KEY_BOSS) / 4] + " Boss Key"; - break; - default: - break; +std::string GetItemTrackerItemName(TrackerItemType itemType, u32 itemId) { + switch (itemType) { + case TRACKER_ITEM_RANDO: { + return Rando::StaticData::Items[(RandoItemId)itemId].name; + } break; + case TRACKER_ITEM_SLOT: { + auto vanillaItemId = gSaveContext.save.saveInfo.inventory.items[itemId]; + if (vanillaItemId == ITEM_NONE) { + vanillaItemId = safeItemsForInventorySlot[itemId][0]; } - } else if (itemId == ITEM_MAGIC_JAR_SMALL || itemId == ITEM_MAGIC_JAR_BIG) { - itemName = gSaveContext.save.saveInfo.playerData.magicLevel > 1 ? "Double Magic" : "Single Magic"; - } else if (itemId == ITEM_HEART_CONTAINER) { - itemName = "Double Defense"; - } else if (itemId >= ITEM_MOONS_TEAR && itemId <= ITEM_PENDANT_OF_MEMORIES) { - itemName = Ship_GetItemNameById(gSaveContext.save.saveInfo.inventory.items[gItemSlots[itemId]]); - } else if (itemId == ITEM_SWORD_KOKIRI) { - fallBackCheck = ITEM_SWORD_KOKIRI + (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) - EQUIP_VALUE_SWORD_KOKIRI); - itemName = Ship_GetItemNameById(fallBackCheck <= ITEM_SWORD_KOKIRI ? ITEM_SWORD_KOKIRI : fallBackCheck); - } else if (itemId == ITEM_SHIELD_HERO) { - fallBackCheck = ITEM_SHIELD_HERO + (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) - EQUIP_VALUE_SHIELD_HERO); - itemName = Ship_GetItemNameById(fallBackCheck <= ITEM_SHIELD_HERO ? ITEM_SHIELD_HERO : fallBackCheck); - } else if (itemId == ITEM_WALLET_ADULT) { - fallBackCheck = ITEM_WALLET_ADULT + CUR_UPG_VALUE(UPG_WALLET) - 1; - itemName = Ship_GetItemNameById(fallBackCheck <= ITEM_WALLET_ADULT ? ITEM_WALLET_ADULT : fallBackCheck); - } - } - - if (itemName == "") { - itemName = Ship_GetItemNameById(itemId); - } - - return itemName; -} - -TrackerItemListObject CreateTrackerObject() { - std::vector<int16_t> itemTrackerList; - TrackerItemListObject trackerObject = { - .windowName = trackerInputName.c_str(), - .columnLength = 6, - .windowScale = 1.0f, - .windowOpacity = 0.5f, - .itemList = itemTrackerList, - }; - return trackerObject; -} - -void ItemTrackerPopUpContext(int16_t itemId, bool isRandoItem) { - bool shouldClose = false; - uint32_t availableSlots = 0; - uint32_t windowIndex = 0; - std::vector<TrackerItemListObject>* window = - isRandoItem ? &BenGui::mItemTrackerWindow->randoItemWindows : &BenGui::mItemTrackerWindow->namedItemWindows; - if (shouldTrackerPopUpOpen && ImGui::BeginPopup("ItemWindowSubMenu")) { - for (auto& object : *window) { - ImGui::PushID(windowIndex); - auto findInObject = std::find(object.itemList.begin(), object.itemList.end(), itemId); - if (findInObject == object.itemList.end()) { - std::string windowStr = "Add to "; - windowStr += object.windowName; - if (UIWidgets::Button(windowStr.c_str(), { .size = ImVec2(0, 0), .color = WIDGET_COLOR })) { - object.itemList.push_back(itemId); - shouldClose = true; - } - availableSlots++; + RandoItemId randoItemId = Rando::StaticData::GetItemIdFromVanillaItemId(vanillaItemId); + return Rando::StaticData::Items[randoItemId].name; + } break; + case TRACKER_ITEM_SWORD: { + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_KOKIRI) { + return Rando::StaticData::Items[RI_SWORD_KOKIRI].name; } - ImGui::PopID(); - windowIndex++; - } - if (availableSlots == 0) { - ImGui::Text("No Slot Available"); - } - if (shouldClose) { - ImGui::CloseCurrentPopup(); - shouldTrackerPopUpOpen = false; - } - - ImGui::EndPopup(); - } -} - -void ItemTrackerDragAndDrop(std::vector<int16_t>& itemWindow, size_t i, TrackerImageObject itemObject) { - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - ImGui::SetDragDropPayload("TRACKER_DRAG", &i, sizeof(size_t)); - ImGui::ImageButton(std::to_string(itemWindow[i]).c_str(), itemObject.textureId, itemObject.textureDimensions, - ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), itemObject.textureColor); - ImGui::EndDragDropSource(); - } - - if (ImGui::BeginDragDropTarget()) { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("TRACKER_DRAG")) { - size_t srcIndex = *(const size_t*)payload->Data; - if (srcIndex != i && srcIndex < itemWindow.size()) { - auto item = itemWindow[srcIndex]; - itemWindow.erase(itemWindow.begin() + srcIndex); - - if (srcIndex < i) { - i--; - } - - itemWindow.insert(itemWindow.begin() + i, item); + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_RAZOR) { + return Rando::StaticData::Items[RI_SWORD_RAZOR].name; } - } - ImGui::EndDragDropTarget(); - } -} - -void DrawItemTrackerSlot(std::vector<int16_t>& itemList, int16_t itemId, bool shouldAdd, bool isRandoItem) { - TrackerImageObject imageObject = GetTextureObject(itemId, isRandoItem); - imageObject.textureColor.w = 1.0f; - if (ImGui::ImageButton(std::to_string(itemId).c_str(), imageObject.textureId, imageObject.textureDimensions, - ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), imageObject.textureColor)) { - if (shouldAdd) { - shouldTrackerPopUpOpen = true; - popupSlot = itemId; - ImGui::OpenPopup("ItemWindowSubMenu"); - } else { - shouldRemove = false; - for (int i = 0; i < itemList.size(); i++) { - if (itemList[i] == itemId) { - indexToRemove = i; - shouldRemove = true; - break; - } + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_GILDED) { + return Rando::StaticData::Items[RI_SWORD_GILDED].name; } - } + return "Sword (None)"; + } break; + case TRACKER_ITEM_SHIELD: { + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) == EQUIP_VALUE_SHIELD_HERO) { + return Rando::StaticData::Items[RI_SHIELD_HERO].name; + } + if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) == EQUIP_VALUE_SHIELD_MIRROR) { + return Rando::StaticData::Items[RI_SHIELD_MIRROR].name; + } + return "Shield (None)"; + } break; + case TRACKER_ITEM_WALLET: { + if (CUR_UPG_VALUE(UPG_WALLET) >= 2) { + return Rando::StaticData::Items[RI_WALLET_GIANT].name; + } + if (CUR_UPG_VALUE(UPG_WALLET) >= 1) { + return Rando::StaticData::Items[RI_WALLET_ADULT].name; + } + return "Wallet (None)"; + } break; + case TRACKER_ITEM_MAGIC: { + if (gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired) { + return Rando::StaticData::Items[RI_DOUBLE_MAGIC].name; + } + if (gSaveContext.save.saveInfo.playerData.isMagicAcquired) { + return Rando::StaticData::Items[RI_SINGLE_MAGIC].name; + } + return "Magic (None)"; + } break; + default: + break; } - UIWidgets::Tooltip(GetItemTrackerItemName(itemId, isRandoItem).c_str()); + + return ""; } -void DrawItemList(std::string listName, int columns) { - bool isRandoItem = false; - if (ImGui::BeginChild(listName.c_str(), ImVec2(0, 0), +struct DragDropPayload { + TrackerItemType itemType; + u32 itemId; + int sourceGroupIndex; + int sourceItemIndex; + bool fromAddMode; +}; + +void DrawItemTrackerGroupPreview(TrackerGroup& group, bool addMode, int groupIndex) { + static std::pair<TrackerItemType, u32> selectedItem = { TRACKER_ITEM_RANDO, 0 }; + + if (ImGui::BeginChild(group.name.c_str(), ImVec2(0, 0), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) { - if (ImGui::BeginTable(listName.c_str(), columns)) { - ImVec2 framePadding = ImVec2(listName == "Songs" ? ITEM_SONG_PADDING : 0, 0); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, framePadding); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(5, 5)); - std::vector<int16_t> emptyList; - - if (listName == "Frogs" || listName == "Ocarina Buttons" || listName == "Boss Souls" || - listName == "Enemy Souls" || listName == "Owl Statues" || listName == "Tingle Maps" || - listName == "Time" || listName == "Misc") { - for (int j = std::get<0>(randoItemLists.at(listName)); j <= std::get<1>(randoItemLists.at(listName)); - j++) { - ImGui::TableNextColumn(); - isRandoItem = true; - DrawItemTrackerSlot(emptyList, j, true, true); - } - } else if (listName == "Bottles" || listName == "Tokens" || listName == "Stray Fairies" || - listName == "Dungeon") { - for (int j = std::get<0>(defaultItemLists.at(listName)); - j <= std::get<1>(defaultItemLists.at(listName)); j++) { - ImGui::TableNextColumn(); - isRandoItem = false; - DrawItemTrackerSlot(emptyList, j, true, false); - } - } else { - std::pair<uint32_t, uint32_t> range = GetItemMapRange(std::get<0>(defaultItemLists.at(listName)), - std::get<1>(defaultItemLists.at(listName))); - for (int i = range.first; i <= range.second; i++) { - - if (itemIdToItemNameMap[i].first == ITEM_WALLET_ADULT) { - ImGui::TableNextColumn(); - DrawItemTrackerSlot(emptyList, ITEM_MAGIC_JAR_SMALL, true, false); - ImGui::TableNextColumn(); - DrawItemTrackerSlot(emptyList, ITEM_HEART_CONTAINER, true, false); + if (ImGui::BeginTable(group.name.c_str(), group.columns)) { + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + + // Use index-based loop to avoid iterator invalidation + for (size_t itemIndex = 0; itemIndex < group.items.size(); itemIndex++) { + auto& [itemType, itemId] = group.items[itemIndex]; + ImGui::TableNextColumn(); + ImGui::PushID((int)itemIndex); + if (DrawItemTrackerSlot(itemType, itemId, 1.0f, true)) { + if (addMode) { + selectedItem = { itemType, itemId }; + ImGui::OpenPopup("AddToGroup"); + } else { + group.items.erase( + std::remove(group.items.begin(), group.items.end(), std::make_pair(itemType, itemId)), + group.items.end()); + SaveItemTrackerLayout(); } - ImGui::TableNextColumn(); - DrawItemTrackerSlot(emptyList, itemIdToItemNameMap[i].first, true, isRandoItem); } - } - ImGui::PopStyleVar(2); - ItemTrackerPopUpContext(popupSlot, isRandoItem); - ImGui::EndTable(); - } - } - ImGui::EndChild(); -} -void DrawPreviewPane() { - bool isRandoItem = false; - uint32_t imguiId = 0; - uint32_t listIndex = 0; - uint32_t windowListIndex = TRACKER_MAIN; - if (ImGui::BeginChild("List Previews")) { - std::vector<std::vector<TrackerItemListObject>*> windowList = { - &BenGui::mItemTrackerWindow->namedItemWindows, - &BenGui::mItemTrackerWindow->randoItemWindows, - }; - - for (auto* window : windowList) { - ImGui::SeparatorText(windowListIndex == TRACKER_MAIN ? "Vanilla Tracker Windows" : "Rando Tracker Windows"); - listIndex = 0; - for (auto& object : *window) { - ImGui::PushID(imguiId); - ImGui::SeparatorText(object.windowName.c_str()); - ImGui::SameLine(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize("Clear Main").x * 1.5f)); - if (UIWidgets::Button("Clear", { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Red })) { - object.itemList.clear(); - } - ImGui::SameLine(); - if (UIWidgets::Button("x", { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Red })) { - auto& currentWindow = *windowList[windowListIndex]; - currentWindow.erase(currentWindow.begin() + listIndex); + // Drag and drop functionality + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { + DragDropPayload payload; + payload.itemType = itemType; + payload.itemId = itemId; + payload.sourceGroupIndex = groupIndex; + payload.sourceItemIndex = (int)itemIndex; + payload.fromAddMode = addMode; + ImGui::SetDragDropPayload("TRACKER_ITEM", &payload, sizeof(DragDropPayload)); + + // Display preview while dragging + DrawItemTrackerSlot(itemType, itemId, 0.5f, true); + ImGui::EndDragDropSource(); } - ImGui::PopID(); - if (object.itemList.size() == 0) { - listIndex++; - imguiId++; - continue; - } - if (ImGui::BeginTable(std::to_string(listIndex).c_str(), object.columnLength)) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(5, 5)); - for (int i = 0; i < object.itemList.size(); i++) { - ImGui::TableNextColumn(); - if (windowListIndex == TRACKER_RANDO) { - DrawItemTrackerSlot(object.itemList, object.itemList[i], false, true); - isRandoItem = true; + + // Only allow dropping on removeMode slots + if (!addMode && ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* imguiPayload = ImGui::AcceptDragDropPayload("TRACKER_ITEM")) { + DragDropPayload* payload = (DragDropPayload*)imguiPayload->Data; + + if (payload->fromAddMode) { + // Dragging from addMode to removeMode: insert at this position + group.items.insert(group.items.begin() + itemIndex, + std::make_pair(payload->itemType, payload->itemId)); + SaveItemTrackerLayout(); } else { - DrawItemTrackerSlot(object.itemList, (int16_t)object.itemList[i], false, false); + // Dragging from removeMode to removeMode: move item + if (payload->sourceGroupIndex == groupIndex) { + // Moving within same group + if (payload->sourceItemIndex != (int)itemIndex) { + auto item = group.items[payload->sourceItemIndex]; + group.items.erase(group.items.begin() + payload->sourceItemIndex); + + // Adjust target index if we removed an item before it + int targetIndex = (int)itemIndex; + if (payload->sourceItemIndex < (int)itemIndex) { + targetIndex--; + } + group.items.insert(group.items.begin() + targetIndex, item); + SaveItemTrackerLayout(); + } + } else { + // Moving between different groups + auto item = std::make_pair(payload->itemType, payload->itemId); + itemTrackerGroups[payload->sourceGroupIndex].items.erase( + itemTrackerGroups[payload->sourceGroupIndex].items.begin() + + payload->sourceItemIndex); + group.items.insert(group.items.begin() + itemIndex, item); + SaveItemTrackerLayout(); + } } - ItemTrackerDragAndDrop(object.itemList, i, GetTextureObject(object.itemList[i], isRandoItem)); } - if (shouldRemove) { - object.itemList.erase(object.itemList.begin() + indexToRemove); - shouldRemove = false; - indexToRemove = ITEM_OCARINA_OF_TIME; + ImGui::EndDragDropTarget(); + } + + // Check for popup in the same ID scope + if (addMode && ImGui::BeginPopup("AddToGroup")) { + UIWidgets::PushStyleMenuItem(WIDGET_COLOR); + ImGui::SeparatorText("Select group to add item to"); + for (size_t i = 0; i < itemTrackerGroups.size(); i++) { + if (ImGui::Selectable(itemTrackerGroups[i].name.c_str(), false)) { + itemTrackerGroups[i].items.push_back(selectedItem); + SaveItemTrackerLayout(); + ImGui::CloseCurrentPopup(); + } } - ImGui::PopStyleVar(2); - ImGui::EndTable(); + UIWidgets::PopStyleMenuItem(); + ImGui::EndPopup(); } - listIndex++; - imguiId++; + + ImGui::PopID(); } - windowListIndex++; + ImGui::PopStyleVar(2); + ImGui::EndTable(); } } ImGui::EndChild(); } -void DrawTrackerWindowOptions(int32_t windowIndex, TrackerItemListObject& windowObject) { +bool DrawTrackerWindowOptions(int32_t windowIndex, TrackerGroup& group) { ImGui::PushID(windowIndex); - int32_t columns = windowObject.columnLength; - float scale = windowObject.windowScale; - float opacity = windowObject.windowOpacity; + int32_t columns = group.columns; + float scale = group.scale; std::string trackerInputRename; if (windowIndex >= 0) { - if (UIWidgets::InputString("##windowname", &trackerInputRename, + if (UIWidgets::InputString("##name", &trackerInputRename, UIWidgets::InputOptions() .LabelPosition(UIWidgets::LabelPosition::None) .Color(WIDGET_COLOR) - .PlaceholderText(windowObject.windowName) - .DefaultValue(windowObject.windowName))) { - windowObject.windowName = trackerInputRename; + .PlaceholderText(group.name) + .DefaultValue(group.name))) { + if (!trackerInputRename.empty() && trackerInputRename != group.name) { + group.name = trackerInputRename; + SaveItemTrackerLayout(); + } } } if (UIWidgets::SliderInt("Columns", &columns, @@ -376,239 +225,377 @@ void DrawTrackerWindowOptions(int32_t windowIndex, TrackerItemListObject& window .DefaultValue(6) .LabelPosition(UIWidgets::LabelPosition::None) .Format("Columns: %i") - .Color(WIDGET_COLOR) - .Size(ImVec2(ImGui::GetContentRegionAvail().x / 2, 0)))) { - windowObject.columnLength = columns; + .Color(WIDGET_COLOR))) { + group.columns = columns; + SaveItemTrackerLayout(); } - ImGui::SameLine(); - if (UIWidgets::SliderFloat("Scale", &scale, - UIWidgets::FloatSliderOptions() - .Min(0.5f) - .Max(5.0f) - .DefaultValue(1.0f) - .LabelPosition(UIWidgets::LabelPosition::None) - .Format("Scale: %.1f") - .Step(0.5f) - .Color(WIDGET_COLOR) - .Size(ImVec2(ImGui::GetContentRegionAvail().x, 0)))) { - windowObject.windowScale = scale; + if (CVarGetInteger("gSettings.ItemTracker.WindowGroup", 0)) { + if (UIWidgets::SliderFloat("Scale", &scale, + UIWidgets::FloatSliderOptions() + .Min(0.2f) + .Max(3.0f) + .DefaultValue(1.0f) + .LabelPosition(UIWidgets::LabelPosition::None) + .Format("Scale: %.1f") + .Step(0.5f) + .Color(WIDGET_COLOR))) { + group.scale = scale; + SaveItemTrackerLayout(); + } } - if (UIWidgets::SliderFloat("Opacity", &opacity, - UIWidgets::FloatSliderOptions() - .Min(0) - .Max(1.0f) - .DefaultValue(0.5f) - .LabelPosition(UIWidgets::LabelPosition::None) - .Format("Opacity: %.1f") - .Step(0.1f) - .Color(WIDGET_COLOR) - .Size(ImVec2(ImGui::GetContentRegionAvail().x, 0)))) { - windowObject.windowOpacity = opacity; + if (UIWidgets::Button("Remove Group", { .color = UIWidgets::Colors::Red })) { + itemTrackerGroups.erase(itemTrackerGroups.begin() + windowIndex); + SaveItemTrackerLayout(); + ImGui::PopID(); + return true; } ImGui::PopID(); - UIWidgets::Separator(); + return false; } -void ApplyRandoItemGroup(std::string listName) { - std::tuple<int16_t, int16_t, int16_t> list = randoItemLists.at(listName); - TrackerItemListObject itemObject = { - .windowName = listName, - .columnLength = std::get<2>(list), - .windowScale = 1.0f, - .windowOpacity = 0.5f, - }; - for (int i = std::get<0>(list); i <= std::get<1>(list); i++) { - itemObject.itemList.push_back(i); - } - - BenGui::mItemTrackerWindow->randoItemWindows.push_back(itemObject); -} - -void ApplyDefaultItemGroup(std::string listName) { - std::tuple<int16_t, int16_t, int16_t> list = defaultItemLists.at(listName); - TrackerItemListObject itemObject = { - .windowName = listName, - .columnLength = std::get<2>(list), - .windowScale = 1.0f, - .windowOpacity = 0.5f, - }; - - if (listName == "Bottles" || listName == "Tokens" || listName == "Stray Fairies" || listName == "Dungeon") { - int16_t baseValue = std::get<0>(list); - int16_t maxValue = std::get<1>(list); - for (int j = baseValue; j <= maxValue; j++) { - itemObject.itemList.push_back(j); - } - } else { - std::pair<uint32_t, uint32_t> range = GetItemMapRange(std::get<0>(list), std::get<1>(list)); - for (int i = range.first; i <= range.second; i++) { - if (itemIdToItemNameMap[i].first == ITEM_WALLET_ADULT) { - itemObject.itemList.push_back(ITEM_MAGIC_JAR_SMALL); - itemObject.itemList.push_back(ITEM_HEART_CONTAINER); - } - itemObject.itemList.push_back(itemIdToItemNameMap[i].first); - } - } - BenGui::mItemTrackerWindow->namedItemWindows.push_back(itemObject); +void LoadAvailableWindows() { + itemTrackerGroupsAvailable.clear(); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Inventory", + .columns = 6, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_SLOT, SLOT_OCARINA, SLOT_BOTTLE_6), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Masks", + .columns = 6, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_SLOT, SLOT_MASK_POSTMAN, SLOT_MASK_FIERCE_DEITY), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Songs", + .columns = 5, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_SONG_TIME }, + { TRACKER_ITEM_RANDO, RI_SONG_HEALING }, + { TRACKER_ITEM_RANDO, RI_SONG_EPONA }, + { TRACKER_ITEM_RANDO, RI_SONG_SOARING }, + { TRACKER_ITEM_RANDO, RI_SONG_STORMS }, + { TRACKER_ITEM_RANDO, RI_SONG_SONATA }, + { TRACKER_ITEM_RANDO, RI_SONG_LULLABY }, + { TRACKER_ITEM_RANDO, RI_SONG_NOVA }, + { TRACKER_ITEM_RANDO, RI_SONG_ELEGY }, + { TRACKER_ITEM_RANDO, RI_SONG_OATH }, + }, + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Quest", + .columns = 5, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_REMAINS_ODOLWA }, + { TRACKER_ITEM_RANDO, RI_REMAINS_GOHT }, + { TRACKER_ITEM_RANDO, RI_REMAINS_GYORG }, + { TRACKER_ITEM_RANDO, RI_REMAINS_TWINMOLD }, + { TRACKER_ITEM_RANDO, RI_BOMBERS_NOTEBOOK }, + { TRACKER_ITEM_SWORD, RI_PROGRESSIVE_SWORD }, + { TRACKER_ITEM_SHIELD, RI_SHIELD_HERO }, + { TRACKER_ITEM_MAGIC, RI_PROGRESSIVE_MAGIC }, + { TRACKER_ITEM_RANDO, RI_DOUBLE_DEFENSE }, + { TRACKER_ITEM_WALLET, RI_PROGRESSIVE_WALLET }, + }, + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Tokens", + .columns = 2, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_GS_TOKEN_SWAMP }, + { TRACKER_ITEM_RANDO, RI_GS_TOKEN_OCEAN }, + }, + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Stray Fairies", + .columns = 5, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_CLOCK_TOWN_STRAY_FAIRY }, + { TRACKER_ITEM_RANDO, RI_WOODFALL_STRAY_FAIRY }, + { TRACKER_ITEM_RANDO, RI_SNOWHEAD_STRAY_FAIRY }, + { TRACKER_ITEM_RANDO, RI_GREAT_BAY_STRAY_FAIRY }, + { TRACKER_ITEM_RANDO, RI_STONE_TOWER_STRAY_FAIRY }, + }, + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Dungeon", + .columns = 4, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_WOODFALL_BOSS_KEY }, + { TRACKER_ITEM_RANDO, RI_SNOWHEAD_BOSS_KEY }, + { TRACKER_ITEM_RANDO, RI_GREAT_BAY_BOSS_KEY }, + { TRACKER_ITEM_RANDO, RI_STONE_TOWER_BOSS_KEY }, + { TRACKER_ITEM_RANDO, RI_WOODFALL_SMALL_KEY }, + { TRACKER_ITEM_RANDO, RI_SNOWHEAD_SMALL_KEY }, + { TRACKER_ITEM_RANDO, RI_GREAT_BAY_SMALL_KEY }, + { TRACKER_ITEM_RANDO, RI_STONE_TOWER_SMALL_KEY }, + }, + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Frogs", + .columns = 4, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_RANDO, RI_FROG_BLUE, RI_FROG_WHITE), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Ocarina Buttons", + .columns = 5, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_RANDO, RI_OCARINA_BUTTON_A, RI_OCARINA_BUTTON_C_UP), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ .name = "Boss Souls", + .columns = 5, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_SOUL_BOSS_ODOLWA }, + { TRACKER_ITEM_RANDO, RI_SOUL_BOSS_GOHT }, + { TRACKER_ITEM_RANDO, RI_SOUL_BOSS_GYORG }, + { TRACKER_ITEM_RANDO, RI_SOUL_BOSS_TWINMOLD }, + { TRACKER_ITEM_RANDO, RI_SOUL_BOSS_MAJORA }, + } }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Enemy Souls", + .columns = 6, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_RANDO, RI_SOUL_ENEMY_ALIEN, RI_SOUL_ENEMY_WOLFOS), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ .name = "Owl Statues", + .columns = 5, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_OWL_CLOCK_TOWN_SOUTH }, + { TRACKER_ITEM_RANDO, RI_OWL_MILK_ROAD }, + { TRACKER_ITEM_RANDO, RI_OWL_SOUTHERN_SWAMP }, + { TRACKER_ITEM_RANDO, RI_OWL_WOODFALL }, + { TRACKER_ITEM_RANDO, RI_OWL_MOUNTAIN_VILLAGE }, + { TRACKER_ITEM_RANDO, RI_OWL_SNOWHEAD }, + { TRACKER_ITEM_RANDO, RI_OWL_GREAT_BAY_COAST }, + { TRACKER_ITEM_RANDO, RI_OWL_ZORA_CAPE }, + { TRACKER_ITEM_RANDO, RI_OWL_IKANA_CANYON }, + { TRACKER_ITEM_RANDO, RI_OWL_STONE_TOWER }, + } }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Time", + .columns = 6, + .scale = 1.0f, + .items = GetItemsFromRange(TRACKER_ITEM_RANDO, RI_TIME_DAY_1, RI_TIME_NIGHT_3), + }); + + itemTrackerGroupsAvailable.push_back(TrackerGroup{ + .name = "Misc", + .columns = 6, + .scale = 1.0f, + .items = { + { TRACKER_ITEM_RANDO, RI_TRIFORCE_PIECE }, + { TRACKER_ITEM_RANDO, RI_ABILITY_SWIM }, + }, + }); } void ApplyDefaultItemPreset() { - BenGui::mItemTrackerWindow->namedItemWindows.clear(); + itemTrackerGroups.clear(); - for (int key = 0; key < defaultItemLists.size(); key++) { - if (listOrder[key] == "Rando") { - continue; + std::set<std::string> defaultGroups = { + "Inventory", "Masks", "Songs", "Quest", "Tokens", "Stray Fairies", "Dungeon", + }; + + for (auto& group : itemTrackerGroupsAvailable) { + if (defaultGroups.count(group.name)) { + itemTrackerGroups.push_back(group); } - ApplyDefaultItemGroup(listOrder[key]); } } void SaveItemTrackerLayout() { - std::vector<std::vector<TrackerItemListObject>*> trackerWindows = { - &BenGui::mItemTrackerWindow->namedItemWindows, - &BenGui::mItemTrackerWindow->randoItemWindows, - }; - - auto itemTrackerConfig = nlohmann::json::object(); - - uint16_t windowType = 0; - - for (auto* window : trackerWindows) { - if (window->empty()) { - windowType++; - continue; - } - - std::string windowName = windowType == TRACKER_MAIN ? "Main" : "Rando"; - nlohmann::json trackerSaveObject = nlohmann::json::object(); - - for (auto& object : *window) { - trackerSaveObject[object.windowName] = nlohmann::json::object(); - trackerSaveObject[object.windowName]["Name"] = object.windowName; - trackerSaveObject[object.windowName]["Columns"] = object.columnLength; - trackerSaveObject[object.windowName]["Scale"] = object.windowScale; - trackerSaveObject[object.windowName]["Opacity"] = object.windowOpacity; - trackerSaveObject[object.windowName]["ItemList"] = nlohmann::json::array(); - - for (auto& item : object.itemList) { - trackerSaveObject[object.windowName]["ItemList"].push_back(item); - } - } - itemTrackerConfig[windowName] = trackerSaveObject; - windowType++; + auto itemTrackerLayout = nlohmann::json::array(); + for (auto& group : itemTrackerGroups) { + auto groupJson = nlohmann::json::object({ + { "name", group.name }, + { "columns", group.columns }, + { "scale", group.scale }, + { "items", group.items }, + }); + itemTrackerLayout.push_back(groupJson); } - - Ship::Context::GetInstance()->GetConfig()->SetBlock("ItemTrackerLayout", itemTrackerConfig); + Ship::Context::GetInstance()->GetConfig()->SetBlock("CVars.ItemTrackerLayout", itemTrackerLayout); Ship::Context::GetInstance()->GetConfig()->Save(); } -void LoadItemTrackerLayout() { +void LoadItemTrackerConfig() { auto allConfig = Ship::Context::GetInstance()->GetConfig()->GetNestedJson(); - if (allConfig.find("ItemTrackerLayout") == allConfig.end()) { - return; - } - auto& itemTrackerConfig = allConfig["ItemTrackerLayout"]; - - for (auto& [windowKey, windowData] : itemTrackerConfig.items()) { - std::vector<TrackerItemListObject>* window = windowKey == "Main" - ? &BenGui::mItemTrackerWindow->namedItemWindows - : &BenGui::mItemTrackerWindow->randoItemWindows; - for (auto& [dataKey, dataInfo] : windowData.items()) { - TrackerItemListObject windowObj = { .windowName = dataInfo["Name"].get<std::string>(), - .columnLength = dataInfo["Columns"], - .windowScale = dataInfo["Scale"], - .windowOpacity = dataInfo["Opacity"], - .itemList = dataInfo["ItemList"] }; - window->push_back(windowObj); - } - } -} + // Verify that the config has CVars.ItemTrackerLayout and its an array + if (allConfig.find("CVars") != allConfig.end() && allConfig["CVars"].is_object() && + allConfig["CVars"].find("ItemTrackerLayout") != allConfig["CVars"].end() && + allConfig["CVars"]["ItemTrackerLayout"].is_array()) { + + itemTrackerGroups.clear(); + auto& itemTrackerLayout = allConfig["CVars"]["ItemTrackerLayout"]; + for (auto& group : itemTrackerLayout) { + std::vector<std::pair<TrackerItemType, u32>> items; + + // Manually parse items array + if (group.contains("items") && group["items"].is_array()) { + for (auto& item : group["items"]) { + if (item.is_array() && item.size() >= 2) { + items.push_back({ static_cast<TrackerItemType>(item[0].get<int>()), item[1].get<u32>() }); + } + } + } -void DrawTrackerSaveLoadOptions() { - if (UIWidgets::Button("Save Layout", { .color = WIDGET_COLOR })) { - SaveItemTrackerLayout(); - } - if (UIWidgets::Button("Load Layout", { .color = WIDGET_COLOR })) { - BenGui::mItemTrackerWindow->namedItemWindows.clear(); - BenGui::mItemTrackerWindow->randoItemWindows.clear(); - LoadItemTrackerLayout(); + itemTrackerGroups.push_back(TrackerGroup{ + .name = group.value("name", std::string("")), + .columns = group.value("columns", (u8)6), + .scale = group.value("scale", 1.0f), + .items = items, + }); + } } } void DrawTrackerOptions() { - int32_t windowIndex = 0; - ImGui::SeparatorText("Presets"); - if (UIWidgets::Button("Default Preset", { .color = WIDGET_COLOR })) { - ApplyDefaultItemPreset(); - } - UIWidgets::Tooltip("Places all Vanilla items in the tracker."); - - ImGui::SeparatorText("Custom Windows"); - if (ImGui::BeginTable("OptionsList", 2)) { + if (ImGui::BeginTable("OptionsTable", 2)) { ImGui::TableNextColumn(); + if (CVarGetInteger("gWindows.ItemTracker", 0)) { + UIWidgets::WindowButton("Disable Item Tracker", "gWindows.ItemTracker", BenGui::mItemTrackerWindow, + { .size = UIWidgets::Sizes::Inline, .color = WIDGET_COLOR }); + } else { + UIWidgets::WindowButton("Enable Item Tracker", "gWindows.ItemTracker", BenGui::mItemTrackerWindow, + { .size = UIWidgets::Sizes::Inline, .color = WIDGET_COLOR }); + } + UIWidgets::CVarCombobox("Window Type", "gSettings.ItemTracker.WindowType", windowTypes, { .alignment = UIWidgets::ComponentAlignment::Right, .labelPosition = UIWidgets::LabelPosition::Near, .color = WIDGET_COLOR }); - ImGui::TableNextColumn(); UIWidgets::CVarCheckbox("Split Window Groups", "gSettings.ItemTracker.WindowGroup"); - UIWidgets::CVarCheckbox("Show Item Counts", "gSettings.ItemTracker.ItemCounts"); - ImGui::EndTable(); - } - UIWidgets::InputString("Window Name", &trackerInputName, - { - .labelPosition = UIWidgets::LabelPosition::None, - .color = WIDGET_COLOR, - .placeholder = "Enter new window name", - }); - if (ImGui::BeginTable("WindowCreation", 2)) { + UIWidgets::CVarCheckbox("Show Item Counts", "gSettings.ItemTracker.ItemCounts", + UIWidgets::CheckboxOptions().DefaultValue(true)); ImGui::TableNextColumn(); - if (UIWidgets::Button("Create Vanilla Window", { .color = WIDGET_COLOR })) { - BenGui::mItemTrackerWindow->namedItemWindows.push_back(CreateTrackerObject()); - trackerInputName.clear(); + + if (UIWidgets::Button("Restore Default Groups", { .color = UIWidgets::Colors::Gray })) { + ApplyDefaultItemPreset(); + SaveItemTrackerLayout(); } - ImGui::TableNextColumn(); - if (UIWidgets::Button("Create Rando Window", { .color = WIDGET_COLOR })) { - BenGui::mItemTrackerWindow->randoItemWindows.push_back(CreateTrackerObject()); + + if (!CVarGetInteger("gSettings.ItemTracker.WindowGroup", 0)) { + UIWidgets::CVarSliderFloat("Scale", "gSettings.ItemTracker.Scale", + UIWidgets::FloatSliderOptions() + .Min(0.2f) + .Max(3.0f) + .DefaultValue(1.0f) + .LabelPosition(UIWidgets::LabelPosition::None) + .Format("Scale: %.1f") + .Step(0.5f) + .Color(WIDGET_COLOR)); + } + + UIWidgets::CVarSliderFloat("Opacity", "gSettings.ItemTracker.Opacity", + UIWidgets::FloatSliderOptions() + .Min(0.0f) + .Max(1.0f) + .DefaultValue(0.5f) + .Format("Opacity: %.1f") + .LabelPosition(UIWidgets::LabelPosition::None) + .Step(0.1f) + .Color(WIDGET_COLOR)); + static std::string trackerInputName; + UIWidgets::InputString("##Group Name", &trackerInputName, + UIWidgets::InputOptions() + .LabelPosition(UIWidgets::LabelPosition::None) + .Color(WIDGET_COLOR) + .PlaceholderText("Enter new group name") + .Size({ ImGui::GetContentRegionAvail().x - 45.0f, 0 })); + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_PLUS, { .color = UIWidgets::Colors::Green })) { + itemTrackerGroups.push_back(TrackerGroup{ + .name = trackerInputName.c_str(), + .columns = 6, + .scale = 1.0f, + }); + SaveItemTrackerLayout(); trackerInputName.clear(); } + ImGui::EndTable(); } - ImGui::SeparatorText("Window Options"); - for (auto& window : BenGui::mItemTrackerWindow->namedItemWindows) { - DrawTrackerWindowOptions(windowIndex, window); - windowIndex++; - } - for (auto& window : BenGui::mItemTrackerWindow->randoItemWindows) { - DrawTrackerWindowOptions(windowIndex, window); - windowIndex++; - } } -void DrawTrackerCustomizationOptions() { - if (ImGui::BeginChild("Item Lists")) { - for (int key = 0; key < defaultItemLists.size(); key++) { - ImGui::PushID(key); - std::tuple<int16_t, int16_t, int16_t> list = defaultItemLists.at(listOrder[key]); - ImGui::SeparatorText(listOrder[key].c_str()); - ImGui::SameLine(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize("Set All").x * 1.5f)); - if (UIWidgets::Button("Set All", { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Green })) { - ApplyDefaultItemGroup(listOrder[key]); +void DrawTrackerAvailableGroups() { + if (ImGui::BeginChild("Available Groups")) { + int groupIndex = 0; + for (auto& group : itemTrackerGroupsAvailable) { + ImGui::PushID(groupIndex); + ImGui::SeparatorText(group.name.c_str()); + ImGui::SameLine(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize("aaa").x * 1.5f)); + if (UIWidgets::Button(ICON_FA_PLUS, { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Green })) { + itemTrackerGroups.push_back(group); + SaveItemTrackerLayout(); } - DrawItemList(listOrder[key], std::get<2>(list)); + DrawItemTrackerGroupPreview(group, true, groupIndex); ImGui::PopID(); + groupIndex++; } - for (int rkey = 0; rkey < randoItemLists.size(); rkey++) { - ImGui::PushID(rkey + RI_MAX); - std::tuple<int16_t, int16_t, int16_t> list = randoItemLists.at(randoListOrder[rkey]); - ImGui::SeparatorText(randoListOrder[rkey].c_str()); - ImGui::SameLine(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize("Set All").x * 1.5f)); - if (UIWidgets::Button("Set All", { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Green })) { - ApplyRandoItemGroup(randoListOrder[rkey]); + } + ImGui::EndChild(); +} + +void DrawTrackerActiveGroups() { + if (ImGui::BeginChild("Active Groups")) { + int groupIndex = 0; + for (auto& group : itemTrackerGroups) { + ImGui::PushID(groupIndex); + ImGui::SeparatorText(group.name.c_str()); + + ImGui::SameLine(ImGui::GetContentRegionMax().x - (ImGui::CalcTextSize("aaaaaaaaa").x * 1.5f)); + if (UIWidgets::Button(ICON_FA_CHEVRON_UP, { .size = ImVec2(0, 0), .color = WIDGET_COLOR })) { + if (groupIndex > 0) { + std::swap(itemTrackerGroups[groupIndex], itemTrackerGroups[groupIndex - 1]); + SaveItemTrackerLayout(); + } + } + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_CHEVRON_DOWN, { .size = ImVec2(0, 0), .color = WIDGET_COLOR })) { + if (groupIndex < static_cast<int>(itemTrackerGroups.size()) - 1) { + std::swap(itemTrackerGroups[groupIndex], itemTrackerGroups[groupIndex + 1]); + SaveItemTrackerLayout(); + } + } + ImGui::SameLine(); + if (UIWidgets::Button(ICON_FA_ELLIPSIS_H, { .size = ImVec2(0, 0), .color = UIWidgets::Colors::Gray })) { + ImGui::OpenPopup("GroupOptions"); } - DrawItemList(randoListOrder[rkey], std::get<2>(list)); + ImGui::SetNextWindowSize(ImVec2(400, 0), ImGuiCond_Always); + ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMin().x - 400, ImGui::GetItemRectMin().y), + ImGuiCond_Always); + if (ImGui::BeginPopup("GroupOptions")) { + if (DrawTrackerWindowOptions(groupIndex, group)) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + DrawItemTrackerGroupPreview(group, false, groupIndex); ImGui::PopID(); + groupIndex++; } } ImGui::EndChild(); @@ -617,42 +604,15 @@ void DrawTrackerCustomizationOptions() { void ItemTrackerSettingsWindow::DrawElement() { ImGui::SetNextWindowSize(ImVec2(733, 472), ImGuiCond_FirstUseEver); if (ImGui::BeginChild("Item Tracker Settings")) { - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 1.0f, 1.0f, 0.2f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 0.1f)); + DrawTrackerOptions(); - if (CVarGetInteger("gWindows.ItemTracker", 0)) { - UIWidgets::WindowButton("Hide Item Tracker", "gWindows.ItemTracker", BenGui::mItemTrackerWindow, - { .size = UIWidgets::Sizes::Inline, .color = WIDGET_COLOR }); - } else { - UIWidgets::WindowButton("Show Item Tracker", "gWindows.ItemTracker", BenGui::mItemTrackerWindow, - { .size = UIWidgets::Sizes::Inline, .color = WIDGET_COLOR }); - } - UIWidgets::Separator(); UIWidgets::PushStyleTabs(WIDGET_COLOR); if (ImGui::BeginTable("TrackerTabs", 2)) { ImGui::TableNextColumn(); if (ImGui::BeginChild("TrackerChild")) { if (ImGui::BeginTabBar("TrackerTabs")) { - if (ImGui::BeginTabItem("Customization")) { - if (ImGui::BeginChild("CustomizationChild")) { - DrawTrackerCustomizationOptions(); - } - ImGui::EndChild(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Options")) { - if (ImGui::BeginChild("OptionsChild")) { - DrawTrackerOptions(); - } - ImGui::EndChild(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Save/Load")) { - if (ImGui::BeginChild("SaveChild")) { - DrawTrackerSaveLoadOptions(); - } - ImGui::EndChild(); + if (ImGui::BeginTabItem("Available Groups")) { + DrawTrackerAvailableGroups(); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -663,8 +623,8 @@ void ItemTrackerSettingsWindow::DrawElement() { ImGui::TableNextColumn(); if (ImGui::BeginChild("WindowChild")) { if (ImGui::BeginTabBar("WindowTab")) { - if (ImGui::BeginTabItem("Window Layouts")) { - DrawPreviewPane(); + if (ImGui::BeginTabItem("Active Groups")) { + DrawTrackerActiveGroups(); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -674,11 +634,12 @@ void ItemTrackerSettingsWindow::DrawElement() { ImGui::EndTable(); } UIWidgets::PopStyleTabs(); - - ImGui::PopStyleColor(3); } ImGui::EndChild(); } void ItemTrackerSettingsWindow::InitElement() { + LoadAvailableWindows(); + ApplyDefaultItemPreset(); + LoadItemTrackerConfig(); } diff --git a/mm/2s2h/Rando/ConvertItem.cpp b/mm/2s2h/Rando/ConvertItem.cpp index e8815a58f..fd3232a66 100644 --- a/mm/2s2h/Rando/ConvertItem.cpp +++ b/mm/2s2h/Rando/ConvertItem.cpp @@ -307,11 +307,6 @@ bool Rando::IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId) case RI_STONE_TOWER_STRAY_FAIRY: case RI_GS_TOKEN_SWAMP: case RI_GS_TOKEN_OCEAN: - case RI_FROG_BLUE: - case RI_FROG_CYAN: - case RI_FROG_PINK: - case RI_FROG_WHITE: - case RI_ABILITY_SWIM: case RI_TRIFORCE_PIECE: if (hasObtainedCheck) { return false; @@ -448,6 +443,8 @@ bool Rando::IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId) // These items are technically fine to receive again because they don't do anything, but we'll convert them to // ensure it's clear to the player something didn't go wrong. // Quest Items + case RI_BOMBERS_NOTEBOOK: + return !CHECK_QUEST_ITEM(QUEST_BOMBERS_NOTEBOOK); case RI_REMAINS_ODOLWA: return !CHECK_QUEST_ITEM(QUEST_REMAINS_ODOLWA); case RI_REMAINS_GOHT: @@ -547,6 +544,16 @@ bool Rando::IsItemObtainable(RandoItemId randoItemId, RandoCheckId randoCheckId) case RI_SOUL_ENEMY_WIZROBE: case RI_SOUL_ENEMY_WOLFOS: return !Flags_GetRandoInf(SOUL_RI_TO_RANDO_INF(randoItemId)); + case RI_ABILITY_SWIM: + return !Flags_GetRandoInf(RANDO_INF_OBTAINED_SWIM); + case RI_FROG_BLUE: + return !CHECK_WEEKEVENTREG(WEEKEVENTREG_33_01); + case RI_FROG_CYAN: + return !CHECK_WEEKEVENTREG(WEEKEVENTREG_32_40); + case RI_FROG_PINK: + return !CHECK_WEEKEVENTREG(WEEKEVENTREG_32_80); + case RI_FROG_WHITE: + return !CHECK_WEEKEVENTREG(WEEKEVENTREG_33_02); case RI_TIME_DAY_1: case RI_TIME_NIGHT_1: case RI_TIME_DAY_2: diff --git a/mm/2s2h/Rando/StaticData/Items.cpp b/mm/2s2h/Rando/StaticData/Items.cpp index 946578558..adb53840f 100644 --- a/mm/2s2h/Rando/StaticData/Items.cpp +++ b/mm/2s2h/Rando/StaticData/Items.cpp @@ -308,6 +308,15 @@ RandoItemId GetItemIdFromName(const char* name) { return RI_UNKNOWN; } +RandoItemId GetItemIdFromVanillaItemId(u32 itemId) { + for (auto& [randoItemId, randoStaticItem] : Items) { + if (randoStaticItem.itemId == itemId) { + return randoItemId; + } + } + return RI_UNKNOWN; +} + // This exists because of nintendo being nintendo u8 GetIconForZMessage(RandoItemId randoItemId) { switch (randoItemId) { @@ -392,10 +401,14 @@ const char* GetIconTexturePath(RandoItemId randoItemId) { case RI_PROGRESSIVE_MAGIC: return (const char*)gItemIcons[ITEM_MAGIC_JAR_SMALL]; case RI_SOUL_BOSS_GOHT: + return (const char*)gItemIcons[ITEM_REMAINS_GOHT]; case RI_SOUL_BOSS_GYORG: - case RI_SOUL_BOSS_MAJORA: + return (const char*)gItemIcons[ITEM_REMAINS_GYORG]; case RI_SOUL_BOSS_ODOLWA: + return (const char*)gItemIcons[ITEM_REMAINS_ODOLWA]; case RI_SOUL_BOSS_TWINMOLD: + return (const char*)gItemIcons[ITEM_REMAINS_TWINMOLD]; + case RI_SOUL_BOSS_MAJORA: case RI_SOUL_ENEMY_ALIEN: case RI_SOUL_ENEMY_ARMOS: case RI_SOUL_ENEMY_BAD_BAT: @@ -489,6 +502,8 @@ const char* GetIconTexturePath(RandoItemId randoItemId) { return (const char*)gThreeDayClockMoonHourTex; case RI_TIME_PROGRESSIVE: return (const char*)gThreeDayClockSunHourTex; + case RI_ABILITY_SWIM: + return (const char*)gItemIcons[ITEM_MASK_ZORA]; default: break; } diff --git a/mm/2s2h/Rando/StaticData/StaticData.h b/mm/2s2h/Rando/StaticData/StaticData.h index 81ed12b52..6e3641cdc 100644 --- a/mm/2s2h/Rando/StaticData/StaticData.h +++ b/mm/2s2h/Rando/StaticData/StaticData.h @@ -50,6 +50,7 @@ extern std::map<StartingItemCategory, std::vector<RandoItemId>> StartingItemsMap extern std::map<RandoItemId, u8> MaxStartingItemsMap; RandoItemId GetItemIdFromName(const char* name); +RandoItemId GetItemIdFromVanillaItemId(u32 itemId); u8 GetIconForZMessage(RandoItemId itemId); const char* GetIconTexturePath(RandoItemId itemId); bool ShouldShowGetItemCutscene(RandoItemId itemId); diff --git a/mm/2s2h/ShipUtils.cpp b/mm/2s2h/ShipUtils.cpp index 2e8e70fce..fce0a3828 100644 --- a/mm/2s2h/ShipUtils.cpp +++ b/mm/2s2h/ShipUtils.cpp @@ -127,105 +127,6 @@ std::array<const char*, 11> digitList = { gCounterDigit0Tex, gCounterDigit1Tex, gCounterDigit4Tex, gCounterDigit5Tex, gCounterDigit6Tex, gCounterDigit7Tex, gCounterDigit8Tex, gCounterDigit9Tex, gCounterColonTex }; -const char* fairyIconTextures[] = { gDungeonStrayFairyWoodfallIconTex, gDungeonStrayFairySnowheadIconTex, - gDungeonStrayFairyGreatBayIconTex, gDungeonStrayFairyStoneTowerIconTex }; - -std::vector<std::pair<int16_t, std::string>> itemIdToItemNameMap = { - // Inventory - { ITEM_OCARINA_OF_TIME, "Ocarina of Time" }, - { ITEM_BOW, "Bow" }, - { ITEM_ARROW_FIRE, "Fire Arrow" }, - { ITEM_ARROW_ICE, "Ice Arrow" }, - { ITEM_ARROW_LIGHT, "Light Arrow" }, - { ITEM_MOONS_TEAR, "Moon's Tear" }, - { ITEM_BOMB, "Bomb" }, - { ITEM_BOMBCHU, "Bombchu" }, - { ITEM_DEKU_STICK, "Deku Stick" }, - { ITEM_DEKU_NUT, "Deku Nut" }, - { ITEM_MAGIC_BEANS, "Magic Bean" }, - { ITEM_ROOM_KEY, "Room Key" }, - { ITEM_POWDER_KEG, "Powder Keg" }, - { ITEM_PICTOGRAPH_BOX, "Pictograph" }, - { ITEM_LENS_OF_TRUTH, "Lens of Truth" }, - { ITEM_HOOKSHOT, "Hookshot" }, - { ITEM_SWORD_GREAT_FAIRY, "Great Fairy Sword" }, - { ITEM_LETTER_TO_KAFEI, "Letter to Kafei" }, - { ITEM_BOTTLE, "Empty Bottle" }, - - // Masks - { ITEM_MASK_POSTMAN, "Postman's Hat" }, - { ITEM_MASK_ALL_NIGHT, "All-Night Mask" }, - { ITEM_MASK_BLAST, "Blast Mask" }, - { ITEM_MASK_STONE, "Stone Mask" }, - { ITEM_MASK_GREAT_FAIRY, "Great Fairy's Mask" }, - { ITEM_MASK_DEKU, "Deku Mask" }, - { ITEM_MASK_KEATON, "Keaton Mask" }, - { ITEM_MASK_BREMEN, "Bremen Mask" }, - { ITEM_MASK_BUNNY, "Bunny Hood" }, - { ITEM_MASK_DON_GERO, "Don Gero's Mask" }, - { ITEM_MASK_SCENTS, "Mask of Scents" }, - { ITEM_MASK_GORON, "Goron Mask" }, - { ITEM_MASK_ROMANI, "Romani's Mask" }, - { ITEM_MASK_CIRCUS_LEADER, "Circus Leader's Mask" }, - { ITEM_MASK_KAFEIS_MASK, "Kafei's Mask" }, - { ITEM_MASK_COUPLE, "Couple's Mask" }, - { ITEM_MASK_TRUTH, "Mask of Truth" }, - { ITEM_MASK_ZORA, "Zora Mask" }, - { ITEM_MASK_KAMARO, "Kamaro's Mask" }, - { ITEM_MASK_GIBDO, "Gibdo Mask" }, - { ITEM_MASK_GARO, "Garo's Mask" }, - { ITEM_MASK_CAPTAIN, "Captain's Hat" }, - { ITEM_MASK_GIANT, "Giant's Mask" }, - { ITEM_MASK_FIERCE_DEITY, "Fierce Deity's Mask" }, - - // Songs - { ITEM_SONG_TIME, "Song of Time" }, - { ITEM_SONG_HEALING, "Song of Healing" }, - { ITEM_SONG_EPONA, "Epona's Song" }, - { ITEM_SONG_SOARING, "Song of Soaring" }, - { ITEM_SONG_STORMS, "Song of Storms" }, - { ITEM_SONG_SONATA, "Sonata of Awakening" }, - { ITEM_SONG_LULLABY, "Goron Lullaby" }, - { ITEM_SONG_NOVA, "New Wave Bossa Nova" }, - { ITEM_SONG_ELEGY, "Elegy of Emptiness" }, - { ITEM_SONG_OATH, "Oath to Order" }, - - // Quest - { ITEM_REMAINS_ODOLWA, "Odolwa's Remains" }, - { ITEM_REMAINS_GOHT, "Goht's Remains" }, - { ITEM_REMAINS_GYORG, "Gyorg's Remains" }, - { ITEM_REMAINS_TWINMOLD, "Twinmold's Remains" }, - { ITEM_SWORD_KOKIRI, "Kokiri Sword" }, - { ITEM_SHIELD_HERO, "Hero's Shield" }, - { ITEM_WALLET_ADULT, "Adult Wallet" }, - { ITEM_BOMBERS_NOTEBOOK, "Bombers' Notebook" }, - - // Upgrade Items - { ITEM_SWORD_RAZOR, "Razor Sword" }, - { ITEM_SWORD_GILDED, "Gilded Sword" }, - { ITEM_SHIELD_MIRROR, "Mirror Shield" }, - { ITEM_WALLET_GIANT, "Giant Wallet" }, - - // Trade Items - { ITEM_DEED_LAND, "Land Title Deed" }, - { ITEM_DEED_SWAMP, "Swamp Title Deed" }, - { ITEM_DEED_MOUNTAIN, "Mountain Title Deed" }, - { ITEM_DEED_OCEAN, "Ocean Title Deed" }, - { ITEM_LETTER_MAMA, "Letter to Mama" }, - { ITEM_PENDANT_OF_MEMORIES, "Pendant of Memories" }, -}; - -std::string Ship_GetItemNameById(int16_t itemId) { - std::string itemName = ""; - for (auto& [id, name] : itemIdToItemNameMap) { - if (id == itemId) { - itemName = name; - break; - } - } - return itemName; -}; - std::map<uint32_t, ImVec4> itemColorMap = { { ITEM_SONG_SONATA, ImVec4(0.588f, 1.0f, 0.392f, 1.0f) }, { ITEM_SONG_LULLABY, ImVec4(1.0f, 0.313f, 0.156f, 1.0f) }, @@ -244,12 +145,33 @@ ImVec4 Ship_GetItemColorTint(uint32_t itemId) { } } -std::map<uint32_t, ImVec4> randoItemColorMap = { +std::unordered_map<uint32_t, ImVec4> randoItemColorMap = { { RI_OCARINA_BUTTON_A, ImVec4(0.085f, 0.494f, 0.796f, 1) }, { RI_OCARINA_BUTTON_C_DOWN, ImVec4(0.84f, 0.768f, 0.089f, 1) }, { RI_OCARINA_BUTTON_C_LEFT, ImVec4(0.84f, 0.768f, 0.089f, 1) }, { RI_OCARINA_BUTTON_C_RIGHT, ImVec4(0.84f, 0.768f, 0.089f, 1) }, { RI_OCARINA_BUTTON_C_UP, ImVec4(0.84f, 0.768f, 0.089f, 1) }, + { RI_CLOCK_TOWN_STRAY_FAIRY, ImVec4(1.0f, 0.9f, 0.5f, 0.4f) }, + { RI_WOODFALL_SMALL_KEY, ImVec4(0.9f, 0.33f, 0.56f, 0.4f) }, + { RI_SNOWHEAD_SMALL_KEY, ImVec4(0.1f, 0.54f, 0.16f, 0.4f) }, + { RI_GREAT_BAY_SMALL_KEY, ImVec4(0.61f, 0.04f, 0.86f, 0.4f) }, + { RI_STONE_TOWER_SMALL_KEY, ImVec4(0.58f, 0.65f, 0.15f, 0.4f) }, + { RI_WOODFALL_BOSS_KEY, ImVec4(0.9f, 0.33f, 0.56f, 0.4f) }, + { RI_SNOWHEAD_BOSS_KEY, ImVec4(0.1f, 0.54f, 0.16f, 0.4f) }, + { RI_GREAT_BAY_BOSS_KEY, ImVec4(0.61f, 0.04f, 0.86f, 0.4f) }, + { RI_STONE_TOWER_BOSS_KEY, ImVec4(0.58f, 0.65f, 0.15f, 0.4f) }, + { RI_TIME_DAY_1, ImVec4(1.0f, 0.9f, 0.3f, 1.0f) }, + { RI_TIME_DAY_2, ImVec4(1.0f, 0.9f, 0.3f, 1.0f) }, + { RI_TIME_DAY_3, ImVec4(1.0f, 0.9f, 0.3f, 1.0f) }, + { RI_TIME_NIGHT_1, ImVec4(0.5f, 0.7f, 1.0f, 1.0f) }, + { RI_TIME_NIGHT_2, ImVec4(0.5f, 0.7f, 1.0f, 1.0f) }, + { RI_TIME_NIGHT_3, ImVec4(0.5f, 0.7f, 1.0f, 1.0f) }, + { RI_SONG_SONATA, ImVec4(0.588f, 1.0f, 0.392f, 1.0f) }, + { RI_SONG_LULLABY, ImVec4(1.0f, 0.313f, 0.156f, 1.0f) }, + { RI_SONG_NOVA, ImVec4(0.392f, 0.588f, 1.0f, 1.0f) }, + { RI_SONG_ELEGY, ImVec4(1.0f, 0.627f, 0.0f, 1.0f) }, + { RI_SONG_OATH, ImVec4(1.0f, 0.392f, 1.0f, 1.0f) }, + { RI_SONG_LULLABY_INTRO, ImVec4(1.0f, 0.313f, 0.156f, 1.0f) }, }; ImVec4 Ship_GetRandoItemColorTint(uint32_t randoItemId) { @@ -261,41 +183,6 @@ ImVec4 Ship_GetRandoItemColorTint(uint32_t randoItemId) { } } -std::map<QuestItem, ItemId> questIdToItemMap = { - { QUEST_REMAINS_ODOLWA, ITEM_REMAINS_ODOLWA }, - { QUEST_REMAINS_GOHT, ITEM_REMAINS_GOHT }, - { QUEST_REMAINS_GYORG, ITEM_REMAINS_GYORG }, - { QUEST_REMAINS_TWINMOLD, ITEM_REMAINS_TWINMOLD }, - { QUEST_SONG_SONATA, ITEM_SONG_SONATA }, - { QUEST_SONG_LULLABY, ITEM_SONG_LULLABY }, - { QUEST_SONG_BOSSA_NOVA, ITEM_SONG_NOVA }, - { QUEST_SONG_ELEGY, ITEM_SONG_ELEGY }, - { QUEST_SONG_OATH, ITEM_SONG_OATH }, - { QUEST_SONG_SARIA, ITEM_SONG_SARIA }, - { QUEST_SONG_TIME, ITEM_SONG_TIME }, - { QUEST_SONG_HEALING, ITEM_SONG_HEALING }, - { QUEST_SONG_EPONA, ITEM_SONG_EPONA }, - { QUEST_SONG_SOARING, ITEM_SONG_SOARING }, - { QUEST_SONG_STORMS, ITEM_SONG_STORMS }, - { QUEST_SONG_SUN, ITEM_SONG_SUN }, - { QUEST_BOMBERS_NOTEBOOK, ITEM_BOMBERS_NOTEBOOK }, -}; - -uint32_t Ship_ConvertQuestIdToItem(uint32_t itemId) { - auto findQuest = questIdToItemMap.find((QuestItem)itemId); - if (findQuest != questIdToItemMap.end()) { - return findQuest->second; - } -} - -uint32_t Ship_ConvertItemIdToQuest(uint32_t itemId) { - for (auto& quest : questIdToItemMap) { - if (quest.second == itemId) { - return quest.first; - } - } -} - extern "C" const char* Ship_GetSceneName(s16 sceneId) { if (sceneNames.contains(sceneId)) { return sceneNames[sceneId]; diff --git a/mm/2s2h/ShipUtils.h b/mm/2s2h/ShipUtils.h index 24b9eaa5a..4d1f5b1d1 100644 --- a/mm/2s2h/ShipUtils.h +++ b/mm/2s2h/ShipUtils.h @@ -24,15 +24,9 @@ std::string convertEnumToReadableName(const std::string& input); std::string Ship_RemoveSpecialCharacters(const std::string& str); extern u16 sOwlWarpEntrancesForMods[]; extern std::array<const char*, 11> digitList; -extern const char* fairyIconTextures[]; extern std::string Ship_FormatTimeDisplay(uint32_t value); -extern std::vector<std::pair<int16_t, std::string>> itemIdToItemNameMap; -extern std::string Ship_GetItemNameById(int16_t itemId); -extern std::map<uint32_t, ImVec4> itemColorMap; extern ImVec4 Ship_GetItemColorTint(uint32_t itemId); extern ImVec4 Ship_GetRandoItemColorTint(uint32_t randoItemId); -uint32_t Ship_ConvertQuestIdToItem(uint32_t itemId); -uint32_t Ship_ConvertItemIdToQuest(uint32_t itemId); extern uint32_t Ship_Hash(std::string str); extern std::string GetActorDescription(u16 actorNum); extern std::string GetActorDebugName(u16 actorNum); |
