diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index b9ca97a9b..7c1f99a15 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -103,6 +103,23 @@ std::map DungeonRCAreasBySceneID = { {SCENE_INSIDE_GANONS_CASTLE, RCAREA_GANONS_CASTLE}, }; +// Dungeon entrances with obvious visual differences between MQ and vanilla qualifying as spoiling on sight +std::vector spoilingEntrances = { + 0x0000, // ENTR_DEKU_TREE_0 + 0x0467, // ENTR_DODONGOS_CAVERN_1 + 0x0028, // ENTR_JABU_JABU_0 + 0x0407, // ENTR_JABU_JABU_1 + 0x0169, // ENTR_FOREST_TEMPLE_0 + 0x0165, // ENTR_FIRE_TEMPLE_0 + 0x0175, // ENTR_FIRE_TEMPLE_1 + 0x0423, // ENTR_WATER_TEMPLE_1 + 0x0082, // ENTR_SPIRIT_TEMPLE_0 + 0x02B2, // ENTR_SHADOW_TEMPLE_1 + 0x0088, // ENTR_ICE_CAVERN_0 + 0x0008, // ENTR_GERUDO_TRAINING_GROUNDS_0 + 0x0467 // ENTR_INSIDE_GANONS_CASTLE_0 +}; + std::map> checksByArea; bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; @@ -263,6 +280,10 @@ void SetCheckCollected(RandomizerCheck rc) { } SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); + if (!IsAreaSpoiled(rcObj.rcArea)) { + SetAreaSpoiled(rcObj.rcArea); + } + doAreaScroll = true; UpdateOrdering(rcObj.rcArea); UpdateInventoryChecks(); @@ -467,9 +488,14 @@ void CheckTrackerLoadGame(int32_t fileNum) { areaChecksGotten[realRcObj.rcArea]++; } } - - if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) { - areasSpoiled |= (1 << realRcObj.rcArea); + } + for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) { + if (!IsAreaSpoiled(static_cast(i)) && (RandomizerCheckObjects::AreaIsOverworld(static_cast(i)) || !IS_RANDO || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE || + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION || + (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER && + OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12))) { + SetAreaSpoiled(static_cast(i)); } } if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) { @@ -539,6 +565,9 @@ void CheckTrackerTransition(uint32_t sceneNum) { SetShopSeen(sceneNum, false); break; } + if (!IsAreaSpoiled(currentArea) && (RandomizerCheckObjects::AreaIsOverworld(currentArea) || std::find(spoilingEntrances.begin(), spoilingEntrances.end(), gPlayState->nextEntranceIndex) != spoilingEntrances.end())) { + SetAreaSpoiled(currentArea); + } } void CheckTrackerFrame() { @@ -756,6 +785,7 @@ void CheckTrackerFlagSet(int16_t flagType, int32_t flag) { void InitTrackerData(bool isDebug) { TrySetAreas(); + areasSpoiled = 0; for (auto& [rc, rco] : RandomizerCheckObjects::GetAllRCObjects()) { if (rc != RC_UNKNOWN_CHECK && rc != RC_MAX) { DefaultCheckData(rc); @@ -783,6 +813,7 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) { SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem); }); }); + SaveManager::Instance->SaveData("areasSpoiled", areasSpoiled); } void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { @@ -798,6 +829,7 @@ void LoadFile() { SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem); }); }); + SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled); } void Teardown() { @@ -809,6 +841,15 @@ void Teardown() { lastLocationChecked = RC_UNKNOWN_CHECK; } +bool IsAreaSpoiled(RandomizerCheckArea rcArea) { + return areasSpoiled & (1 << rcArea); +} + +void SetAreaSpoiled(RandomizerCheckArea rcArea) { + areasSpoiled |= (1 << rcArea); + SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); +} + void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) { auto area = RandomizerCheckObjects::GetAllRCObjects().find(static_cast(check))->second.rcArea; if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) || @@ -827,10 +868,6 @@ void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) { void CheckTrackerWindow::DrawElement() { ImGui::SetNextWindowSize(ImVec2(400, 540), ImGuiCond_FirstUseEver); - if (!initialized && (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)) { - return; - } - if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { if (CVarGetInteger("gCheckTrackerShowOnlyPaused", 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) { return; @@ -851,7 +888,7 @@ void CheckTrackerWindow::DrawElement() { BeginFloatWindows("Check Tracker", mIsVisible, ImGuiWindowFlags_NoScrollbar); - if (!GameInteractor::IsSaveLoaded()) { + if (!GameInteractor::IsSaveLoaded() || !initialized) { ImGui::Text("Waiting for file load..."); //TODO Language EndFloatWindows(); return; @@ -862,8 +899,6 @@ void CheckTrackerWindow::DrawElement() { sceneId = (SceneID)gPlayState->sceneNum; } - areasSpoiled |= (1 << currentArea); - //Quick Options #ifdef __WIIU__ float headerHeight = 40.0f; @@ -925,7 +960,6 @@ void CheckTrackerWindow::DrawElement() { Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string stemp; - s32 areaMask = 1; for (auto& [rcArea, objs] : checksByArea) { RandomizerCheckArea thisArea = currentArea; @@ -978,11 +1012,7 @@ void CheckTrackerWindow::DrawElement() { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f)); - isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO || - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE || - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION || - (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER && - OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12); + isThisAreaSpoiled = IsAreaSpoiled(rcArea) || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0); if (isThisAreaSpoiled) { if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { @@ -1015,7 +1045,6 @@ void CheckTrackerWindow::DrawElement() { ImGui::TreePop(); } } - areaMask <<= 1; } ImGui::EndTable(); //Checks Lead-out @@ -1219,10 +1248,10 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) { } void UpdateInventoryChecks() { - //For all the areas with compasses, if you have one, spoil the area + //For all the areas with maps, if you have one, spoil the area for (auto [scene, area] : DungeonRCAreasBySceneID) { if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, scene)) { - areasSpoiled |= (1 << area); + SetAreaSpoiled(area); } } } @@ -1232,9 +1261,6 @@ void UpdateAreaFullyChecked(RandomizerCheckArea area) { void UpdateAreas(RandomizerCheckArea area) { areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size(); - if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) { - areasSpoiled |= (1 << area); - } } void UpdateAllOrdering() { diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h index a8928cca3..85a4f1d9e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -50,6 +50,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj); void InitTrackerData(bool isDebug); RandomizerCheckArea GetCheckArea(); void UpdateCheck(uint32_t, RandomizerCheckTrackerData); +bool IsAreaSpoiled(RandomizerCheckArea rcArea); +void SetAreaSpoiled(RandomizerCheckArea rcArea); } // namespace CheckTracker diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index f63b37b62..44671b5c6 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -9,6 +9,7 @@ #include #include #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "randomizer_check_tracker.h" #include extern "C" { @@ -290,15 +291,15 @@ bool IsValidSaveFile() { } bool HasSong(ItemTrackerItem item) { - return (1 << item.id) & gSaveContext.inventory.questItems; + return GameInteractor::IsSaveLoaded() ? ((1 << item.id) & gSaveContext.inventory.questItems) : false; } bool HasQuestItem(ItemTrackerItem item) { - return (item.data & gSaveContext.inventory.questItems) != 0; + return GameInteractor::IsSaveLoaded() ? (item.data & gSaveContext.inventory.questItems) : false; } bool HasEquipment(ItemTrackerItem item) { - return (item.data & gSaveContext.inventory.equipment) != 0; + return GameInteractor::IsSaveLoaded() ? (item.data & gSaveContext.inventory.equipment) : false; } ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { @@ -406,8 +407,12 @@ ItemTrackerNumbers GetItemCurrentAndMax(ItemTrackerItem item) { #define IM_COL_GREEN IM_COL32(0, 255, 0, 255) #define IM_COL_GRAY IM_COL32(155, 155, 155, 255) #define IM_COL_PURPLE IM_COL32(180, 90, 200, 255) +#define IM_COL_LIGHT_YELLOW IM_COL32(255, 255, 130, 255) -void DrawItemCount(ItemTrackerItem item) { +void DrawItemCount(ItemTrackerItem item, bool hideMax) { + if (!GameInteractor::IsSaveLoaded()) { + return; + } int iconSize = CVarGetInteger("gItemTrackerIconSize", 36); ItemTrackerNumbers currentAndMax = GetItemCurrentAndMax(item); ImVec2 p = ImGui::GetCursorScreenPos(); @@ -416,7 +421,7 @@ void DrawItemCount(ItemTrackerItem item) { if (item.id == ITEM_KEY_SMALL && IsValidSaveFile()) { std::string currentString = ""; - std::string maxString = std::to_string(currentAndMax.maxCapacity); + std::string maxString = hideMax ? "???" : std::to_string(currentAndMax.maxCapacity); ImU32 currentColor = IM_COL_WHITE; ImU32 maxColor = IM_COL_GREEN; // "Collected / Max", "Current / Collected / Max", "Current / Max" @@ -548,7 +553,7 @@ void DrawQuest(ItemTrackerItem item) { ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); if (item.id == QUEST_SKULL_TOKEN) { - DrawItemCount(item); + DrawItemCount(item, false); } ImGui::EndGroup(); @@ -558,7 +563,7 @@ void DrawQuest(ItemTrackerItem item) { void DrawItem(ItemTrackerItem item) { - uint32_t actualItemId = INV_CONTENT(item.id); + uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? INV_CONTENT(item.id) : ITEM_NONE; int iconSize = CVarGetInteger("gItemTrackerIconSize", 36); bool hasItem = actualItemId != ITEM_NONE; std::string itemName = ""; @@ -617,7 +622,7 @@ void DrawItem(ItemTrackerItem item) { ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasItem && IsValidSaveFile() ? item.name : item.nameFaded), ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); - DrawItemCount(item); + DrawItemCount(item, false); ImGui::EndGroup(); if (itemName == "") { @@ -628,7 +633,7 @@ void DrawItem(ItemTrackerItem item) { } void DrawBottle(ItemTrackerItem item) { - uint32_t actualItemId = gSaveContext.inventory.items[SLOT(item.id) + item.data]; + uint32_t actualItemId = GameInteractor::IsSaveLoaded() ? (gSaveContext.inventory.items[SLOT(item.id) + item.data]) : false; bool hasItem = actualItemId != ITEM_NONE; if (GameInteractor::IsSaveLoaded() && (hasItem && item.id != actualItemId && actualItemTrackerItemMap.find(actualItemId) != actualItemTrackerItemMap.end())) { @@ -647,8 +652,8 @@ void DrawDungeonItem(ItemTrackerItem item) { ImU32 dungeonColor = IM_COL_WHITE; uint32_t bitMask = 1 << (item.id - ITEM_KEY_BOSS); // Bitset starts at ITEM_KEY_BOSS == 0. the rest are sequential int iconSize = CVarGetInteger("gItemTrackerIconSize", 36); - bool hasItem = (bitMask & gSaveContext.inventory.dungeonItems[item.data]) != 0; - bool hasSmallKey = (gSaveContext.inventory.dungeonKeys[item.data]) >= 0; + bool hasItem = GameInteractor::IsSaveLoaded() ? (bitMask & gSaveContext.inventory.dungeonItems[item.data]) : false; + bool hasSmallKey = GameInteractor::IsSaveLoaded() ? ((gSaveContext.inventory.dungeonKeys[item.data]) >= 0) : false; ImGui::BeginGroup(); if (itemId == ITEM_KEY_SMALL) { ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(hasSmallKey && IsValidSaveFile() ? item.name : item.nameFaded), @@ -659,16 +664,18 @@ void DrawDungeonItem(ItemTrackerItem item) { ImVec2(iconSize, iconSize), ImVec2(0, 0), ImVec2(1, 1)); } - if (ResourceMgr_IsSceneMasterQuest(item.data) && (CHECK_DUNGEON_ITEM(DUNGEON_MAP, item.data) || item.data == SCENE_GERUDO_TRAINING_GROUND || item.data == SCENE_INSIDE_GANONS_CASTLE)) { - dungeonColor = IM_COL_PURPLE; + if (CheckTracker::IsAreaSpoiled(RandomizerCheckObjects::GetRCAreaBySceneID(static_cast(item.data))) && GameInteractor::IsSaveLoaded()) { + dungeonColor = (ResourceMgr_IsSceneMasterQuest(item.data) ? IM_COL_PURPLE : IM_COL_LIGHT_YELLOW); } if (itemId == ITEM_KEY_SMALL) { - DrawItemCount(item); + DrawItemCount(item, !CheckTracker::IsAreaSpoiled(RandomizerCheckObjects::GetRCAreaBySceneID(static_cast(item.data)))); ImVec2 p = ImGui::GetCursorScreenPos(); + // offset puts the text at the correct level. for some reason, if the save is loaded, the margin is 3 pixels higher only for small keys, so we use 16 then. Otherwise, 13 is where everything else is + int offset = GameInteractor::IsSaveLoaded() ? 16 : 13; std::string dungeonName = itemTrackerDungeonShortNames[item.data]; - ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + 16))); + ImGui::SetCursorScreenPos(ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(dungeonName.c_str()).x / 2), p.y - (iconSize + offset))); ImGui::PushStyleColor(ImGuiCol_Text, dungeonColor); ImGui::Text("%s", dungeonName.c_str()); ImGui::PopStyleColor(); @@ -725,13 +732,15 @@ void DrawNotes(bool resizeable = false) { } }; ImVec2 size = resizeable ? ImVec2(-FLT_MIN, ImGui::GetContentRegionAvail().y) : ImVec2(((iconSize + iconSpacing) * 6) - 8, 200); - if (ItemTrackerNotes::TrackerNotesInputTextMultiline("##ItemTrackerNotes", &itemTrackerNotes, size, ImGuiInputTextFlags_AllowTabInput)) { - notesNeedSave = true; - notesIdleFrames = 0; - } - if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) { - notesNeedSave = false; - SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true); + if (GameInteractor::IsSaveLoaded()) { + if (ItemTrackerNotes::TrackerNotesInputTextMultiline("##ItemTrackerNotes", &itemTrackerNotes, size, ImGuiInputTextFlags_AllowTabInput)) { + notesNeedSave = true; + notesIdleFrames = 0; + } + if ((ImGui::IsItemDeactivatedAfterEdit() || (notesNeedSave && notesIdleFrames > notesMaxIdleFrames)) && IsValidSaveFile()) { + notesNeedSave = false; + SaveManager::Instance->SaveSection(gSaveContext.fileNum, itemTrackerSectionId, true); + } } ImGui::EndGroup(); }