Available Randomizer Checks (#5091)

* Calculate accessible checks and show them in the check tracker.

* Added added DoorUnlocked checks to the Fire Temple doors.

This is needed when calculating Accessible checks while playing and using small keys. Otherwise checks will appear unaccessible because Keys have been used.

* Changed RecalculateAccessibleChecks to use the logic ReachabilitySearch.

* Reverted DoorUnlocked changes.

* Added recalculate_accessible_checks debug console command.

* Skip CanBuy check when calculating accessible checks, it spoils that some items can't be bought with the current wallet.

* Set RAND_INF_ZELDAS_LETTER.

* Don't add locations to the pool when calculating accessible checks.

* Save and Load randomizer trick options.

* Get the number of used small keys.

* Added check if bean was planted.

* Show number of Accessible checks in each Area.

* Accounted for missing RG_POCKET_CUCCO in logic HasItem and ApplyItemEffect.

* Show accessible checks via an icon.

* Check shop price against current wallet.

* Recalculate Accessible Checks in CheckTrackerLoadGame instead of LoadFile.

* RecalculateAccessibleChecks on save.

* Don't unset Zelda's Letter.

* Added Only Show Available Checks checkbox.

* Rename Accessible Checks to Available Checks.

* Added option to Enable Available Checks.

* Remove debug console to recalculate available checks, this can now be done by toggling Enable Available Checks in the settings.

* Remove extra requiredTrial.

* Default "Hide unshuffled shop item checks" unchecked.

* Updated GetSmallKeyCount to return int8_t.

* Default AmmoDrop to true until the setting is implemented.

* Fixed Sleeping Waterfall timesaver.

* Updated Only Show Available Checks to let Show Hidden Items reveal hidden available checks.

* Revert "Don't unset Zelda's Letter."

This reverts commit 4b5903940f.

* Set RAND_INF_ZELDAS_LETTER.

* Revert "Fixed Sleeping Waterfall timesaver."

This reverts commit 9b62fd5297.

* Added isMagicAcquired to Logic::HasItem.

* GetDungeonSmallKeyDoors return static emptyVector.

* Comment out AmmoCanDrop in ResetLogic.

* Added location_access CanBuy todo.

* Reverted Get and Set SmallKeyCount to use uint8_t and handled -1 keys.

* Added todo for MQ Water GetUsedSmallKeyCount.
This commit is contained in:
xxAtrain223 2025-03-31 00:18:42 -05:00 committed by GitHub
commit 35d17b8b0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 432 additions and 36 deletions

View file

@ -9,7 +9,10 @@
#include "soh/SohGui/UIWidgets.hpp"
#include "soh/SohGui/SohGui.hpp"
#include "dungeon.h"
#include "entrance.h"
#include "location_access.h"
#include "3drando/fill.hpp"
#include "soh/Enhancements/debugger/performanceTimer.h"
#include <string>
#include <vector>
@ -84,7 +87,7 @@ bool fishsanityAgeSplit;
bool initialized;
bool doAreaScroll;
bool previousShowHidden = false;
bool hideShopUnshuffledChecks = true;
bool hideShopUnshuffledChecks = false;
bool alwaysShowGS = false;
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
@ -132,8 +135,10 @@ bool areasFullyChecked[RCAREA_INVALID];
u32 areasSpoiled = 0;
bool showVOrMQ;
s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)"
s8 areaChecksAvailable[RCAREA_INVALID];
s8 areaCheckTotals[RCAREA_INVALID];
uint16_t totalChecks = 0;
uint16_t totalChecksAvailable = 0;
uint16_t totalChecksGotten = 0;
bool optCollapseAll; // A bool that will collapse all checks once
bool optExpandAll; // A bool that will expand all checks once
@ -166,6 +171,8 @@ bool hideCollected = false;
bool showHidden = true;
bool mystery = false;
bool showLogicTooltip = false;
bool enableAvailableChecks = false;
bool onlyShowAvailable = false;
SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) {
switch (area) {
@ -235,10 +242,12 @@ void TrySetAreas() {
void CalculateTotals() {
totalChecks = 0;
totalChecksAvailable = 0;
totalChecksGotten = 0;
for (uint8_t i = 0; i < RCAREA_INVALID; i++) {
totalChecks += areaCheckTotals[i];
totalChecksAvailable += areaChecksAvailable[i];
totalChecksGotten += areaChecksGotten[i];
}
}
@ -251,17 +260,43 @@ uint16_t GetTotalChecksGotten() {
return totalChecksGotten;
}
bool IsCheckHidden(RandomizerCheck rc) {
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckStatus status = itemLocation->GetCheckStatus();
bool available = itemLocation->IsAvailable();
bool skipped = itemLocation->GetIsSkipped();
bool obtained = itemLocation->HasObtained();
bool seen = status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED;
bool scummed = status == RCSHOW_SCUMMED;
bool unchecked = status == RCSHOW_UNCHECKED;
return !showHidden && (
(skipped && hideSkipped) ||
(seen && hideSeen) ||
(scummed && hideScummed) ||
(unchecked && hideUnchecked)
);
}
void RecalculateAreaTotals(RandomizerCheckArea rcArea) {
areaChecksGotten[rcArea] = 0;
areaChecksAvailable[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
for (auto rc : checksByArea.at(rcArea)) {
if (!IsVisibleInCheckTracker(rc)) {
continue;
}
areaCheckTotals[rcArea]++;
if (OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped() || OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->HasObtained()) {
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (itemLoc->GetIsSkipped() || itemLoc->HasObtained()) {
areaChecksGotten[rcArea]++;
}
if (itemLoc->IsAvailable() && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++;
}
}
CalculateTotals();
}
@ -308,6 +343,7 @@ void SetCheckCollected(RandomizerCheck rc) {
if (IsVisibleInCheckTracker(rc)) {
if (!OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped()) {
areaChecksGotten[loc->GetArea()]++;
areaChecksAvailable[loc->GetArea()]--;
} else {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
}
@ -424,10 +460,12 @@ void ClearAreaChecksAndTotals() {
for (auto& [rcArea, vec] : checksByArea) {
vec.clear();
areaChecksGotten[rcArea] = 0;
areaChecksAvailable[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
totalChecks = 0;
totalChecksGotten = 0;
totalChecksAvailable = 0;
}
void SetShopSeen(uint32_t sceneNum, bool prices) {
@ -469,6 +507,9 @@ void CheckTrackerLoadGame(int32_t fileNum) {
if (loc->GetCheckStatus() == RCSHOW_SAVED || loc->GetIsSkipped()) {
areaChecksGotten[entry2->GetArea()]++;
}
if (loc->IsAvailable()) {
areaChecksAvailable[entry2->GetArea()]++;
}
}
if (areaChecksGotten[entry2->GetArea()] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2->GetArea()) ||
@ -524,6 +565,7 @@ void CheckTrackerLoadGame(int32_t fileNum) {
UpdateAllOrdering();
UpdateInventoryChecks();
UpdateFilters();
RecalculateAvailableChecks();
}
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
@ -539,6 +581,7 @@ void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
if (status == RCSHOW_SEEN) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(slot)->SetCheckStatus(RCSHOW_IDENTIFIED);
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
RecalculateAvailableChecks();
}
}
@ -812,6 +855,9 @@ void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) {
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveTrackerData(saveContext, sectionID, fullSave);
if (fullSave) {
RecalculateAvailableChecks();
}
}
void LoadFile() {
@ -882,6 +928,8 @@ void CheckTrackerWindow::DrawElement() {
showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0);
mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0);
showLogicTooltip = CVarGetInteger(CVAR_TRACKER_CHECK("ShowLogic"), 0);
enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0);
onlyShowAvailable = CVarGetInteger(CVAR_TRACKER_CHECK("OnlyShowAvailable"), 0);
hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0);
alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0);
@ -932,9 +980,21 @@ void CheckTrackerWindow::DrawElement() {
ImGui::TableNextRow(0, headerHeight);
ImGui::TableNextColumn();
UIWidgets::CVarCheckbox(
"Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, items will show hidden checks by default when updated to this state." }})
.Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox(
"Show Hidden Items", CVAR_TRACKER_CHECK("ShowHidden"), UIWidgets::CheckboxOptions({{.tooltip = "When active, items will show hidden checks by default when updated to this state." }})
.Color(THEME_COLOR))) {
doAreaScroll = true;
showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0);
RecalculateAllAreaTotals();
}
if (enableAvailableChecks) {
if (UIWidgets::CVarCheckbox(
"Only Show Available Checks", CVAR_TRACKER_CHECK("OnlyShowAvailable"), UIWidgets::CheckboxOptions({{ .tooltip = "When active, unavailable checks will be hidden." }})
.Color(THEME_COLOR))) {
doAreaScroll = true;
RecalculateAllAreaTotals();
}
}
UIWidgets::PaddedSeparator();
if (UIWidgets::Button("Expand All", UIWidgets::ButtonOptions().Color(THEME_COLOR).Size(UIWidgets::Sizes::Inline))) {
optCollapseAll = false;
@ -960,7 +1020,13 @@ void CheckTrackerWindow::DrawElement() {
ImGui::Separator();
ImGui::Text("Total Checks: %d / %d", totalChecksGotten, totalChecks);
std::ostringstream totalChecksSS;
totalChecksSS << "Total Checks: ";
if (enableAvailableChecks) {
totalChecksSS << totalChecksAvailable << " Available / ";
}
totalChecksSS << totalChecksGotten << " Checked / " << totalChecks << " Total";
ImGui::Text(totalChecksSS.str().c_str());
UIWidgets::PaddedSeparator();
@ -1012,7 +1078,8 @@ void CheckTrackerWindow::DrawElement() {
doAreaScroll = true;
}
if ((shouldHideFilteredAreas && filterAreasHidden[rcArea]) ||
(!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked)))
(!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked))) ||
(enableAvailableChecks && onlyShowAvailable && areaChecksAvailable[rcArea] == 0)
) {
doDraw = false;
} else {
@ -1051,14 +1118,27 @@ void CheckTrackerWindow::DrawElement() {
isThisAreaSpoiled = IsAreaSpoiled(rcArea) || mqSpoilers;
if (isThisAreaSpoiled) {
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ())
ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
else
ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
} else {
ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]);
std::ostringstream areaTotalsSS;
std::ostringstream areaTotalsTooltipSS;
areaTotalsSS << "(";
if (enableAvailableChecks) {
areaTotalsSS << static_cast<uint16_t>(areaChecksAvailable[rcArea]) << " / ";
areaTotalsTooltipSS << "Available / ";
}
areaTotalsSS << static_cast<uint16_t>(areaChecksGotten[rcArea]) << " / " << static_cast<uint16_t>(areaCheckTotals[rcArea]) << ")";
areaTotalsTooltipSS << "Checked / Total";
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ()) {
areaTotalsSS << " - MQ";
} else {
areaTotalsSS << " - Vanilla";
}
}
ImGui::Text(areaTotalsSS.str().c_str());
UIWidgets::Tooltip(areaTotalsTooltipSS.str().c_str());
} else {
ImGui::Text("???");
}
@ -1561,6 +1641,12 @@ void DrawLocation(RandomizerCheck rc) {
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckStatus status = itemLoc->GetCheckStatus();
bool skipped = itemLoc->GetIsSkipped();
bool available = itemLoc->IsAvailable();
if (enableAvailableChecks && onlyShowAvailable && !available) {
return;
}
if (status == RCSHOW_COLLECTED) {
if (!showHidden && hideCollected) {
return;
@ -1637,10 +1723,18 @@ void DrawLocation(RandomizerCheck rc) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
areaChecksGotten[loc->GetArea()]--;
totalChecksGotten--;
if (available) {
areaChecksAvailable[loc->GetArea()]++;
totalChecksAvailable++;
}
} else {
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(true);
areaChecksGotten[loc->GetArea()]++;
totalChecksGotten++;
if (available) {
areaChecksAvailable[loc->GetArea()]--;
totalChecksAvailable--;
}
}
UpdateOrdering(loc->GetArea());
UpdateInventoryChecks();
@ -1654,7 +1748,19 @@ void DrawLocation(RandomizerCheck rc) {
ImGui::SameLine();
//Draw
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f));
ImVec4 styleColor(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f);
if (enableAvailableChecks) {
if (itemLoc->HasObtained()) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 0, 0, 0));
} else {
ImGui::PushStyleColor(ImGuiCol_Text, styleColor);
}
ImGui::Text("%s", available ? ICON_FA_UNLOCK : ICON_FA_LOCK);
ImGui::PopStyleColor();
ImGui::SameLine();
}
ImGui::PushStyleColor(ImGuiCol_Text, styleColor);
ImGui::Text("%s", txt.c_str());
ImGui::PopStyleColor();
@ -1726,7 +1832,7 @@ void DrawLocation(RandomizerCheck rc) {
if (conditionStr != "true") {
UIWidgets::Tooltip(conditionStr.c_str());
}
return;
break;
}
}
}
@ -1804,6 +1910,55 @@ void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName,
UIWidgets::PopStyleCombobox();
}
void RecalculateAvailableChecks() {
if (!enableAvailableChecks) {
return;
}
ResetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
StartPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
std::vector<RandomizerCheck> targetLocations;
targetLocations.reserve(RR_MAX);
for (auto& location : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = location.GetRandomizerCheck();
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
itemLocation->SetAvailable(false);
if (!itemLocation->HasObtained()) {
targetLocations.emplace_back(rc);
}
}
std::vector<RandomizerCheck> availableChecks = ReachabilitySearch(targetLocations, RG_NONE, true);
for (auto& rc : availableChecks) {
const auto& location = Rando::StaticData::GetLocation(rc);
const auto& itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (location->GetRCType() == RCTYPE_SHOP && itemLocation->GetCheckStatus() == RCSHOW_IDENTIFIED) {
if (CanBuyAnother(rc)) {
itemLocation->SetAvailable(true);
}
} else {
itemLocation->SetAvailable(true);
}
}
totalChecksAvailable = 0;
for (auto& [rcArea, vec] : checksByArea) {
areaChecksAvailable[rcArea] = 0;
for (auto& rc : vec) {
Rando::ItemLocation* itemLocation = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
if (itemLocation->IsAvailable() && IsVisibleInCheckTracker(rc) && !IsCheckHidden(rc)) {
areaChecksAvailable[rcArea]++;
}
}
totalChecksAvailable += areaChecksAvailable[rcArea];
}
StopPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS);
SPDLOG_INFO("Recalculate Available Checks Time: {}ms", GetPerformanceTimer(PT_RECALCULATE_AVAILABLE_CHECKS).count());
}
void CheckTrackerWindow::Draw() {
if (!IsVisible()) {
return;
@ -1858,7 +2013,7 @@ void CheckTrackerSettingsWindow::DrawElement() {
.Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.").Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox("Hide unshuffled shop item checks", CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.").Color(THEME_COLOR))) {
hideShopUnshuffledChecks = !hideShopUnshuffledChecks;
hideShopUnshuffledChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideUnshuffledShopChecks"), 0);
UpdateFilters();
}
if (UIWidgets::CVarCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"),
@ -1868,6 +2023,11 @@ void CheckTrackerSettingsWindow::DrawElement() {
}
UIWidgets::CVarCheckbox("Show Logic", CVAR_TRACKER_CHECK("ShowLogic"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will show a check's logic when hovering over it.").Color(THEME_COLOR));
if (UIWidgets::CVarCheckbox("Enable Available Checks", CVAR_TRACKER_CHECK("EnableAvailableChecks"),
UIWidgets::CheckboxOptions().Tooltip("If enabled, will show the checks that are available to be collected with your current progress.").Color(THEME_COLOR))) {
enableAvailableChecks = CVarGetInteger(CVAR_TRACKER_CHECK("EnableAvailableChecks"), 0);
RecalculateAvailableChecks();
}
// Filtering settings
UIWidgets::PaddedSeparator();