Check Tracker Re-update (#4322)

* Update check status in the check tracker to the new system. Status and Skipped are now stored in ItemLocation, though still saved separately in the trackerData section.

* Fix shop checks not showing prices when identified.

* Patch fix for check status bleed.
Some cleanup of unused code.

* Small tracker optimizations.

* Fix check hiding.

* Bit more cleanup.

* Unhide the filter and make it work again...

* Fix area totals tracking.
Fix skipped status saving.

* Merge conflict cleanup.
This commit is contained in:
Malkierian 2024-09-17 11:32:52 -07:00 committed by GitHub
parent 9c11718341
commit 4148d59c48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 192 additions and 262 deletions

View File

@ -68,6 +68,7 @@ typedef enum { // Pre-existing IDs for save sections in base code
SECTION_ID_STATS,
SECTION_ID_ENTRANCES,
SECTION_ID_SCENES,
SECTION_ID_TRACKER_DATA,
SECTION_ID_MAX
} SaveFuncIDs;
@ -279,7 +280,6 @@ typedef struct {
/* */ u8 pendingIceTrapCount;
/* */ SohStats sohStats;
/* */ FaroresWindData backupFW;
/* */ RandomizerCheckTrackerData checkTrackerData[RC_MAX];
// #endregion
// #region SOH [Randomizer]
// Upstream TODO: Move these to their own struct or name to more obviously specific to Randomizer

View File

@ -306,7 +306,9 @@ void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
auto loc = Rando::Context::GetInstance()->GetItemLocation(randomizerQueuedCheck);
if (randomizerQueuedItemEntry.modIndex == receivedItemEntry.modIndex && randomizerQueuedItemEntry.itemId == receivedItemEntry.itemId) {
SPDLOG_INFO("Item received mod {} item {} from RC {}", receivedItemEntry.modIndex, receivedItemEntry.itemId, static_cast<uint32_t>(randomizerQueuedCheck));
loc->MarkAsObtained();
loc->SetCheckStatus(RCSHOW_COLLECTED);
CheckTracker::RecalculateAllAreaTotals();
SaveManager::Instance->SaveSection(gSaveContext.fileNum, SECTION_ID_TRACKER_DATA, true);
randomizerQueuedCheck = RC_UNKNOWN_CHECK;
randomizerQueuedItemEntry = GET_ITEM_NONE;
}
@ -662,7 +664,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void
*should = false;
} else {
*should = true;
Rando::Context::GetInstance()->GetItemLocation(RC_TOT_MASTER_SWORD)->MarkAsObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_TOT_MASTER_SWORD)->SetCheckStatus(RCSHOW_COLLECTED);
CheckTracker::RecalculateAllAreaTotals();
}
break;
case VB_ITEM00_DESPAWN: {
@ -1179,25 +1182,26 @@ void RandomizerOnSceneInitHandler(int16_t sceneNum) {
// probably need to do something different when we implement shuffle
if (sceneNum == SCENE_TREASURE_BOX_SHOP) {
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_1);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_1)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_1)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_2);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_2)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_2)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_3);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_3)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_3)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_4);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_4)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_4)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_ITEM_5);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_5)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_ITEM_5)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_1);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_1)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_1)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_2);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_2)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_2)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_3);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_3)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_3)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_4);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_4)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_4)->SetCheckStatus(RCSHOW_UNCHECKED);
Flags_UnsetRandomizerInf(RAND_INF_MARKET_TREASURE_CHEST_GAME_KEY_5);
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_5)->MarkAsNotObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_MARKET_TREASURE_CHEST_GAME_KEY_5)->SetCheckStatus(RCSHOW_UNCHECKED);
CheckTracker::RecalculateAllAreaTotals();
}
// LACs & Prelude checks

View File

@ -102,15 +102,23 @@ void ItemLocation::SetCustomPrice(const uint16_t price_) {
}
bool ItemLocation::HasObtained() const {
return obtained;
return status == RCSHOW_COLLECTED || status == RCSHOW_SAVED;
}
void ItemLocation::MarkAsObtained() {
obtained = true;
void ItemLocation::SetCheckStatus(RandomizerCheckStatus status_) {
status = status_;
}
void ItemLocation::MarkAsNotObtained() {
obtained = false;
RandomizerCheckStatus ItemLocation::GetCheckStatus() {
return status;
}
void ItemLocation::SetIsSkipped(bool isSkipped_) {
isSkipped = isSkipped_;
}
bool ItemLocation::GetIsSkipped() {
return isSkipped;
}
bool ItemLocation::IsHintable() const {
@ -213,6 +221,7 @@ void ItemLocation::ResetVariables() {
wothCandidate = false;
barrenCandidate = false;
area = RA_NONE;
obtained = false;
status = RCSHOW_UNCHECKED;
isSkipped = false;
}
}

View File

@ -32,8 +32,10 @@ class ItemLocation {
bool HasCustomPrice() const;
void SetCustomPrice(uint16_t price_);
bool HasObtained() const;
void MarkAsObtained();
void MarkAsNotObtained();
void SetCheckStatus(RandomizerCheckStatus status_);
RandomizerCheckStatus GetCheckStatus();
void SetIsSkipped(bool isSkipped_);
bool GetIsSkipped();
bool IsHintable() const;
void SetAsHintable();
bool IsAHintAccessible() const;
@ -70,6 +72,7 @@ class ItemLocation {
bool visibleInImGui = false;
bool wothCandidate = false;
bool barrenCandidate = false;
bool obtained = false;
RandomizerCheckStatus status = RCSHOW_UNCHECKED;
bool isSkipped = false;
};
} // namespace Rando

View File

@ -56,8 +56,6 @@ std::set<RandomizerCheck> spoilerExcludedLocations;
std::set<RandomizerTrick> enabledTricks;
std::set<RandomizerTrick> enabledGlitches;
std::set<std::map<RandomizerCheck, RandomizerCheckTrackerData>> checkTrackerStates;
u8 generated;
char* seedString;

View File

@ -27,16 +27,6 @@ typedef struct {
uint8_t id;
} Sprite;
// Check tracker check visibility categories
typedef enum {
RCSHOW_UNCHECKED,
RCSHOW_SEEN,
RCSHOW_IDENTIFIED,
RCSHOW_SCUMMED,
RCSHOW_COLLECTED,
RCSHOW_SAVED,
} RandomizerCheckStatus;
typedef enum {
HINT_TYPE_HINT_KEY,
HINT_TYPE_AREA,
@ -315,6 +305,16 @@ typedef enum {
RCAREA_INVALID
} RandomizerCheckArea;
// Check tracker check visibility categories
typedef enum {
RCSHOW_UNCHECKED,
RCSHOW_SEEN,
RCSHOW_IDENTIFIED,
RCSHOW_SCUMMED,
RCSHOW_COLLECTED,
RCSHOW_SAVED,
} RandomizerCheckStatus;
typedef enum {
RR_NONE,
RR_ROOT,
@ -3886,13 +3886,6 @@ typedef enum {
RSK_MAX
} RandomizerSettingKey;
typedef struct {
RandomizerCheckStatus status;
uint16_t skipped;
int16_t price;
uint16_t hintItem;
} RandomizerCheckTrackerData;
//Generic Settings (any binary option can use this)
// off/on
typedef enum {

View File

@ -36,21 +36,6 @@ extern std::vector<ItemTrackerItem> equipmentItems;
using json = nlohmann::json;
void to_json(json& j, const RandomizerCheckTrackerData& rctd) {
j = json {
{ "status", rctd.status == RCSHOW_COLLECTED ? RCSHOW_SAVED : rctd.status },
{ "skipped", rctd.skipped },
{ "price", rctd.price },
{ "hintItem", rctd.hintItem }};
}
void from_json(const json& j, RandomizerCheckTrackerData& rctd) {
j.at("status").get_to(rctd.status);
j.at("skipped").get_to(rctd.skipped);
j.at("hintItem").get_to(rctd.hintItem);
j.at("price").get_to(rctd.price);
}
namespace CheckTracker {
// settings
@ -149,13 +134,12 @@ OSContPad* trackerButtonsPressed;
std::unordered_map<RandomizerCheck, std::string> checkNameOverrides;
bool ShouldShowCheck(RandomizerCheck rc);
bool ShouldHideArea(RandomizerCheckArea rcArea);
bool UpdateFilters();
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0);
bool CompareChecks(RandomizerCheck, RandomizerCheck);
bool CheckByArea(RandomizerCheckArea);
void DrawLocation(RandomizerCheck);
void EndFloatWindows();
bool HasItemBeenCollected(RandomizerCheck);
void LoadSettings();
void RainbowTick();
void UpdateAreas(RandomizerCheckArea area);
@ -219,39 +203,35 @@ Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green
std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L,
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
static ImGuiTextFilter checkSearch;
void DefaultCheckData(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
gSaveContext.checkTrackerData[rc].skipped = 0;
gSaveContext.checkTrackerData[rc].hintItem = RC_UNKNOWN_CHECK;
}
std::array<bool, RCAREA_INVALID> filterAreasHidden = { 0 };
std::array<bool, RC_MAX> filterChecksHidden = { 0 };
void SongFromImpa() {
if (IS_RANDO) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_CHILD_ZELDA) == RO_GENERIC_ON && IS_RANDO) {
if (gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status != RCSHOW_SAVED) {
gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status = RCSHOW_SAVED;
}
//if (gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status != RCSHOW_SAVED) {
// gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status = RCSHOW_SAVED;
//}
}
}
}
void GiftFromSages() {
if (!IS_RANDO) {
DefaultCheckData(RC_GIFT_FROM_SAGES);
//DefaultCheckData(RC_GIFT_FROM_SAGES);
}
}
std::vector<RandomizerCheck> checks;
// Function for adding Link's Pocket check
void LinksPocket() {
if (IS_RANDO) {
/*if (IS_RANDO) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_DUNGEON_REWARDS) == RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
DefaultCheckData(RC_LINKS_POCKET);
gSaveContext.checkTrackerData[RC_LINKS_POCKET].status = RCSHOW_SAVED;
}
}
}*/
}
void TrySetAreas() {
@ -280,37 +260,38 @@ uint16_t GetTotalChecksGotten() {
return totalChecksGotten;
}
void RecalculateAreaTotals() {
for (auto [rcArea, checks] : checksByArea) {
if (rcArea == RCAREA_INVALID) {
return;
}
void RecalculateAreaTotals(RandomizerCheckArea rcArea) {
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
for (auto rc : checks) {
for (auto rc : checksByArea.at(rcArea)) {
if (!IsVisibleInCheckTracker(rc)) {
continue;
}
areaCheckTotals[rcArea]++;
if (gSaveContext.checkTrackerData[rc].skipped || gSaveContext.checkTrackerData[rc].status == RCSHOW_COLLECTED
|| gSaveContext.checkTrackerData[rc].status == RCSHOW_SAVED) {
if (OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped() || OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->HasObtained()) {
areaChecksGotten[rcArea]++;
}
}
CalculateTotals();
}
totalChecks = 0;
totalChecksGotten = 0;
void RecalculateAllAreaTotals() {
for (auto& [rcArea, checks] : checksByArea) {
if (rcArea == RCAREA_INVALID) {
return;
}
RecalculateAreaTotals(rcArea);
}
}
void SetCheckCollected(RandomizerCheck rc) {
gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED;
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetCheckStatus(RCSHOW_COLLECTED);
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
if (IsVisibleInCheckTracker(rc)) {
if (!gSaveContext.checkTrackerData[rc].skipped) {
if (!OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetIsSkipped()) {
areaChecksGotten[loc->GetArea()]++;
} else {
gSaveContext.checkTrackerData[rc].skipped = false;
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
}
}
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
@ -427,6 +408,8 @@ void ClearAreaChecksAndTotals() {
areaChecksGotten[rcArea] = 0;
areaCheckTotals[rcArea] = 0;
}
totalChecks = 0;
totalChecksGotten = 0;
}
void SetShopSeen(uint32_t sceneNum, bool prices) {
@ -439,8 +422,8 @@ void SetShopSeen(uint32_t sceneNum, bool prices) {
}
bool statusChanged = false;
for (int i = start; i < start + 8; i++) {
if (gSaveContext.checkTrackerData[i].status == RCSHOW_UNCHECKED) {
gSaveContext.checkTrackerData[i].status = RCSHOW_SEEN;
if (OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->GetCheckStatus() == RCSHOW_UNCHECKED) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->SetCheckStatus(RCSHOW_SEEN);
statusChanged = true;
}
}
@ -449,73 +432,23 @@ void SetShopSeen(uint32_t sceneNum, bool prices) {
}
}
bool HasItemBeenCollected(RandomizerCheck rc) {
if (gPlayState == nullptr) {
return false;
}
Rando::Location* x = Rando::StaticData::GetLocation(rc);
Rando::SpoilerCollectionCheck check = x->GetCollectionCheck();
auto flag = check.flag;
auto scene = check.scene;
auto type = check.type;
switch (type) {
case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED:
return true;
case SpoilerCollectionCheckType::SPOILER_CHK_CHEST:
return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.chest & (1 << flag)) ||
gSaveContext.sceneFlags[scene].chest & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE:
return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.collect & (1 << flag)) ||
gSaveContext.sceneFlags[scene].collect & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM:
case SpoilerCollectionCheckType::SPOILER_CHK_FISH:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD:
return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc));
case SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF:
return gSaveContext.eventChkInf[flag / 16] & (0x01 << flag % 16);
case SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA:
return GET_GS_FLAGS(scene) & flag;
case SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE:
return gSaveContext.infTable[scene] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF:
return gSaveContext.itemGetInf[flag / 16] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_NONE:
return false;
case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER:
// Gravedigger has a fix in place that means one of two save locations. Check both.
return (gSaveContext.itemGetInf[1] & 0x1000) || // vanilla flag
((IS_RANDO || CVarGetInteger(CVAR_ENHANCEMENT("GravediggingTourFix"), 0)) &&
gSaveContext.sceneFlags[scene].collect & (1 << flag) || (gPlayState->actorCtx.flags.collect & (1 << flag))); // rando/fix flag
default:
return false;
}
return false;
}
void CheckTrackerLoadGame(int32_t fileNum) {
LoadSettings();
TrySetAreas();
for (auto& entry : Rando::StaticData::GetLocationTable()) {
RandomizerCheck rc = entry.GetRandomizerCheck();
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET ||
!Rando::StaticData::GetLocation(rc) != RC_UNKNOWN_CHECK) {
continue;
}
Rando::Location* entry2;
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
entry2 = Rando::StaticData::GetLocation(rc);
} else {
entry2 = Rando::StaticData::GetLocation(rc);
}
Rando::Location* entry2 = Rando::StaticData::GetLocation(rc);
Rando::ItemLocation* loc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
checksByArea.find(entry2->GetArea())->second.push_back(entry2->GetRandomizerCheck());
if (IsVisibleInCheckTracker(entry2->GetRandomizerCheck())) {
areaCheckTotals[entry2->GetArea()]++;
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
if (loc->GetCheckStatus() == RCSHOW_SAVED || loc->GetIsSkipped()) {
areaChecksGotten[entry2->GetArea()]++;
}
}
@ -570,6 +503,7 @@ void CheckTrackerLoadGame(int32_t fileNum) {
initialized = true;
UpdateAllOrdering();
UpdateInventoryChecks();
UpdateFilters();
}
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
@ -581,10 +515,9 @@ void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && gPlayState->sceneNum == SCENE_BAZAAR) {
slot = RC_KAK_BAZAAR_ITEM_1 + cursorSlot;
}
auto status = gSaveContext.checkTrackerData[slot].status;
auto status = OTRGlobals::Instance->gRandoContext->GetItemLocation(slot)->GetCheckStatus();
if (status == RCSHOW_SEEN) {
gSaveContext.checkTrackerData[slot].status = RCSHOW_IDENTIFIED;
gSaveContext.checkTrackerData[slot].price = basePrice;
OTRGlobals::Instance->gRandoContext->GetItemLocation(slot)->SetCheckStatus(RCSHOW_IDENTIFIED);
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
}
}
@ -617,8 +550,7 @@ void CheckTrackerFrame() {
return;
}
// TODO: Move to OnAmmoChange hook once it gets added.
if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
if (!OTRGlobals::Instance->gRandoContext->GetItemLocation(RC_ZR_MAGIC_BEAN_SALESMAN)->HasObtained()) {
if (BEANS_BOUGHT >= 10) {
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
}
@ -827,33 +759,43 @@ void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
void InitTrackerData(bool isDebug) {
TrySetAreas();
areasSpoiled = 0;
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if (loc.GetRandomizerCheck() != RC_UNKNOWN_CHECK && loc.GetRandomizerCheck() != RC_MAX) {
DefaultCheckData(loc.GetRandomizerCheck());
}
}
UpdateAllOrdering();
}
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
if (gameSave) {
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
UpdateAllOrdering();
UpdateInventoryChecks();
} else {
saveContext->checkTrackerData[i].status = RCSHOW_SCUMMED;
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave) {
bool updateOrdering = false;
std::vector<RandomizerCheck> checkCount;
for (int i = RC_UNKNOWN_CHECK; i < RC_MAX; i++) {
if (OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->GetCheckStatus() != RCSHOW_UNCHECKED ||
OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->GetIsSkipped())
checkCount.push_back(static_cast<RandomizerCheck>(i));
}
SaveManager::Instance->SaveArray("checkStatus", checkCount.size(), [&](size_t i) {
RandomizerCheck check = checkCount.at(i);
RandomizerCheckStatus savedStatus = OTRGlobals::Instance->gRandoContext->GetItemLocation(check)->GetCheckStatus();
bool isSkipped = OTRGlobals::Instance->gRandoContext->GetItemLocation(check)->GetIsSkipped();
if (savedStatus == RCSHOW_COLLECTED) {
if (fullSave) {
OTRGlobals::Instance->gRandoContext->GetItemLocation(check)->SetCheckStatus(RCSHOW_SAVED);
savedStatus = RCSHOW_SAVED;
updateOrdering = true;
}
else {
savedStatus = RCSHOW_SCUMMED;
}
}
if (savedStatus != RCSHOW_UNCHECKED || isSkipped) {
SaveManager::Instance->SaveStruct("", [&]() {
SaveManager::Instance->SaveData("status", saveContext->checkTrackerData[i].status);
SaveManager::Instance->SaveData("skipped", saveContext->checkTrackerData[i].skipped);
SaveManager::Instance->SaveData("price", saveContext->checkTrackerData[i].price);
SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem);
SaveManager::Instance->SaveData("randomizerCheck", check);
SaveManager::Instance->SaveData("status", savedStatus);
SaveManager::Instance->SaveData("skipped", isSkipped);
});
}
});
SaveManager::Instance->SaveData("areasSpoiled", areasSpoiled);
if (updateOrdering) {
UpdateAllOrdering();
UpdateAllAreas();
}
}
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
@ -861,15 +803,21 @@ void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
}
void LoadFile() {
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
SaveManager::Instance->LoadArray("checkStatus", RC_MAX, [](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
SaveManager::Instance->LoadData("skipped", gSaveContext.checkTrackerData[i].skipped);
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
RandomizerCheckStatus status;
bool skipped;
RandomizerCheck rc;
SaveManager::Instance->LoadData("randomizerCheck", rc, RC_UNKNOWN_CHECK);
SaveManager::Instance->LoadData("status", status, RCSHOW_UNCHECKED);
SaveManager::Instance->LoadData("skipped", skipped, false);
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetCheckStatus(status);
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(skipped);
});
});
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled);
SaveManager::Instance->LoadData("areasSpoiled", areasSpoiled, (uint32_t)0);
UpdateAllOrdering();
UpdateAllAreas();
}
void Teardown() {
@ -877,6 +825,8 @@ void Teardown() {
ClearAreaChecksAndTotals();
checksByArea.clear();
areasSpoiled = 0;
filterAreasHidden = { 0 };
filterChecksHidden = { 0 };
lastLocationChecked = RC_UNKNOWN_CHECK;
}
@ -890,30 +840,6 @@ void SetAreaSpoiled(RandomizerCheckArea rcArea) {
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
}
void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) {
auto area = Rando::StaticData::GetLocation(static_cast<RandomizerCheck>(check))->GetArea();
if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) ||
((gSaveContext.checkTrackerData[check].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[check].status != RCSHOW_SAVED) &&
(data.status == RCSHOW_COLLECTED || data.status == RCSHOW_SAVED))) {
areaChecksGotten[area]++;
} else if ((gSaveContext.checkTrackerData[check].skipped && !data.skipped) ||
((gSaveContext.checkTrackerData[check].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[check].status == RCSHOW_SAVED) &&
(data.status != RCSHOW_COLLECTED && data.status != RCSHOW_SAVED))) {
areaChecksGotten[area]--;
}
gSaveContext.checkTrackerData[check] = data;
UpdateOrdering(area);
}
void CheckTrackerWindow::Draw() {
if (!IsVisible()) {
return;
}
DrawElement();
// Sync up the IsVisible flag if it was changed by ImGui
SyncVisibilityConsoleVariable();
}
void CheckTrackerWindow::DrawElement() {
if (CVarGetInteger(CVAR_TRACKER_CHECK("WindowType"), TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) {
if (CVarGetInteger(CVAR_TRACKER_CHECK("ShowOnlyPaused"), 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) {
@ -985,7 +911,9 @@ void CheckTrackerWindow::DrawElement() {
doAreaScroll = true;
}
UIWidgets::Tooltip("Clear the search field");
checkSearch.Draw();
if (checkSearch.Draw()) {
UpdateFilters();
}
UIWidgets::PaddedSeparator();
@ -1039,7 +967,7 @@ void CheckTrackerWindow::DrawElement() {
previousShowHidden = showHidden;
doAreaScroll = true;
}
if ((shouldHideFilteredAreas && ShouldHideArea(rcArea)) ||
if ((shouldHideFilteredAreas && filterAreasHidden[rcArea]) ||
(!showHidden && ((hideComplete && thisAreaFullyChecked) || (hideIncomplete && !thisAreaFullyChecked)))
) {
doDraw = false;
@ -1099,7 +1027,7 @@ void CheckTrackerWindow::DrawElement() {
doAreaScroll = false;
}
for (auto rc : checks) {
if (doDraw && isThisAreaSpoiled && ShouldShowCheck(rc)) {
if (doDraw && isThisAreaSpoiled && !filterChecksHidden[rc]) {
DrawLocation(rc);
}
}
@ -1119,13 +1047,16 @@ void CheckTrackerWindow::DrawElement() {
}
}
bool ShouldHideArea(RandomizerCheckArea rcArea) {
if (checkSearch.Filters.Size == 0 || checkSearch.PassFilter(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) {
return false;
}
for (auto check : checksByArea[rcArea]) {
bool UpdateFilters() {
for (auto& [rcArea, checks] : checksByArea) {
filterAreasHidden[rcArea] = !checkSearch.PassFilter(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str());
for (auto check : checks) {
if (ShouldShowCheck(check)) {
return false;
filterAreasHidden[rcArea] = false;
filterChecksHidden[check] = false;
} else {
filterChecksHidden[check] = true;
}
}
}
@ -1136,8 +1067,9 @@ bool ShouldShowCheck(RandomizerCheck check) {
return (
IsVisibleInCheckTracker(check) &&
(checkSearch.Filters.Size == 0 ||
checkSearch.PassFilter(RandomizerCheckObjects::GetRCAreaName(Rando::StaticData::GetLocation(check)->GetArea()).c_str()) ||
checkSearch.PassFilter(Rando::StaticData::GetLocation(check)->GetShortName().c_str()))
checkSearch.PassFilter((Rando::StaticData::GetLocation(check)->GetShortName() + " " +
Rando::StaticData::GetLocation(check)->GetName() + " " +
RandomizerCheckObjects::GetRCAreaName(Rando::StaticData::GetLocation(check)->GetArea())).c_str()))
);
}
@ -1368,6 +1300,13 @@ void UpdateInventoryChecks() {
void UpdateAreaFullyChecked(RandomizerCheckArea area) {
}
void UpdateAllAreas() {
// Sort the entire thing
for (int i = 0; i < RCAREA_INVALID; i++) {
UpdateAreas(static_cast<RandomizerCheckArea>(i));
}
}
void UpdateAreas(RandomizerCheckArea area) {
areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size();
}
@ -1384,7 +1323,7 @@ void UpdateOrdering(RandomizerCheckArea rcArea) {
if(checksByArea.contains(rcArea)) {
std::sort(checksByArea.find(rcArea)->second.begin(), checksByArea.find(rcArea)->second.end(), CompareChecks);
}
RecalculateAllAreaTotals();
CalculateTotals();
}
@ -1393,14 +1332,14 @@ bool IsEoDCheck(RandomizerCheckType type) {
}
bool CompareChecks(RandomizerCheck i, RandomizerCheck j) {
RandomizerCheckTrackerData iShow = gSaveContext.checkTrackerData[i];
RandomizerCheckTrackerData jShow = gSaveContext.checkTrackerData[j];
Rando::Location* x = Rando::StaticData::GetLocation(i);
Rando::Location* y = Rando::StaticData::GetLocation(j);
bool iCollected = iShow.status == RCSHOW_COLLECTED || iShow.status == RCSHOW_SAVED;
bool iSaved = iShow.status == RCSHOW_SAVED;
bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED;
bool jSaved = jShow.status == RCSHOW_SAVED;
auto itemI = OTRGlobals::Instance->gRandoContext->GetItemLocation(i);
auto itemJ = OTRGlobals::Instance->gRandoContext->GetItemLocation(j);
bool iCollected = itemI->HasObtained();
bool iSaved = itemI->GetCheckStatus() == RCSHOW_SAVED;
bool jCollected = itemJ->HasObtained();
bool jSaved = itemJ->GetCheckStatus() == RCSHOW_SAVED;
if (!iCollected && jCollected) {
return true;
@ -1414,9 +1353,9 @@ bool CompareChecks(RandomizerCheck i, RandomizerCheck j) {
return false;
}
if (!iShow.skipped && jShow.skipped) {
if (!itemI->GetIsSkipped() && itemJ->GetIsSkipped()) {
return true;
} else if (iShow.skipped && !jShow.skipped) {
} else if (itemI->GetIsSkipped() && !itemJ->GetIsSkipped()) {
return false;
}
@ -1446,14 +1385,8 @@ void DrawLocation(RandomizerCheck rc) {
bool showHidden = CVarGetInteger(CVAR_TRACKER_CHECK("ShowHidden"), 0);
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckTrackerData checkData = gSaveContext.checkTrackerData[rc];
RandomizerCheckStatus status = checkData.status;
if (itemLoc->HasObtained()) {
status = RCSHOW_COLLECTED;
}
bool skipped = checkData.skipped;
RandomizerCheckStatus status = itemLoc->GetCheckStatus();
bool skipped = itemLoc->GetIsSkipped();
if (status == RCSHOW_COLLECTED) {
if (!showHidden && CVarGetInteger(CVAR_TRACKER_CHECK("Collected.Hide"), 0)) {
return;
@ -1525,11 +1458,13 @@ void DrawLocation(RandomizerCheck rc) {
if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) {
if (UIWidgets::StateButton(std::to_string(rc).c_str(), skipped ? ICON_FA_PLUS : ICON_FA_TIMES)) {
if (skipped) {
gSaveContext.checkTrackerData[rc].skipped = false;
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(false);
areaChecksGotten[loc->GetArea()]--;
totalChecksGotten--;
} else {
gSaveContext.checkTrackerData[rc].skipped = true;
OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->SetIsSkipped(true);
areaChecksGotten[loc->GetArea()]++;
totalChecksGotten++;
}
UpdateOrdering(loc->GetArea());
UpdateInventoryChecks();
@ -1550,9 +1485,7 @@ void DrawLocation(RandomizerCheck rc) {
bool mystery = CVarGetInteger(CVAR_RANDOMIZER_ENHANCEMENT("MysteriousShuffle"), 0) && itemLoc->IsAddedToPool();
if (checkData.hintItem != 0) {
// TODO hints
} else if (status != RCSHOW_UNCHECKED) {
if (status != RCSHOW_UNCHECKED) {
switch (status) {
case RCSHOW_SAVED:
case RCSHOW_COLLECTED:
@ -1581,8 +1514,8 @@ void DrawLocation(RandomizerCheck rc) {
} else if (!mystery) {
txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language);
}
if (!IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) {
txt += fmt::format(" - {}", gSaveContext.checkTrackerData[rc].price);
if (IsVisibleInCheckTracker(rc) && status == RCSHOW_IDENTIFIED && !mystery) {
txt += fmt::format(" - {}", OTRGlobals::Instance->gRandoContext->GetItemLocation(rc)->GetPrice());
}
} else {
if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) {
@ -1608,8 +1541,7 @@ void DrawLocation(RandomizerCheck rc) {
}
if (CVarGetInteger("gCheckTrackerOptionShowLogic", 0)) {
std::vector<LocationAccess> locationsInRegion = areaTable[itemLoc->GetParentRegionKey()].locations;
for (auto& locationInRegion : locationsInRegion) {
for (auto& locationInRegion : areaTable[itemLoc->GetParentRegionKey()].locations) {
if (locationInRegion.GetLocation() == rc) {
std::string conditionStr = locationInRegion.GetConditionStr();
if (conditionStr != "true") {
@ -1742,12 +1674,12 @@ void CheckTrackerSettingsWindow::DrawElement() {
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", CVAR_TRACKER_CHECK("HideRightShopChecks"), false, "", UIWidgets::CheckboxGraphics::Cross, true)) {
hideShopRightChecks = !hideShopRightChecks;
RecalculateAreaTotals();
RecalculateAllAreaTotals();
}
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), false, "")) {
alwaysShowGS = !alwaysShowGS;
RecalculateAreaTotals();
RecalculateAllAreaTotals();
}
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");
UIWidgets::EnhancementCheckbox("Show Logic", "gCheckTrackerOptionShowLogic");
@ -1796,7 +1728,7 @@ void CheckTrackerWindow::InitElement() {
Color_Saved_Extra = CVarGetColor(CVAR_TRACKER_CHECK("Saved.ExtraColor"), Color_Saved_Extra_Default);
SaveManager::Instance->AddInitFunction(InitTrackerData);
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, SECTION_PARENT_NONE);
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
@ -1811,8 +1743,6 @@ void CheckTrackerWindow::InitElement() {
hideShopRightChecks = CVarGetInteger(CVAR_TRACKER_CHECK("HideRightShopChecks"), 1);
alwaysShowGS = CVarGetInteger(CVAR_TRACKER_CHECK("AlwaysShowGSLocs"), 0);
//LocationTable_Init();
}
} // namespace CheckTracker

View File

@ -21,7 +21,6 @@ class CheckTrackerSettingsWindow : public Ship::GuiWindow {
class CheckTrackerWindow : public Ship::GuiWindow {
public:
using GuiWindow::GuiWindow;
void Draw() override;
~CheckTrackerWindow() {};
protected:
@ -44,20 +43,19 @@ class CheckTrackerWindow : public Ship::GuiWindow {
//repeat...
#define INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(idx) (0x8000 >> (7 - (idx % 8) + ((idx % 16) / 8) * 8))
void DefaultCheckData(RandomizerCheck rc);
void Teardown();
void UpdateAllOrdering();
bool IsVisibleInCheckTracker(RandomizerCheck rc);
bool IsCheckShuffled(RandomizerCheck rc);
void InitTrackerData(bool isDebug);
RandomizerCheckArea GetCheckArea();
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
uint16_t GetTotalChecks();
uint16_t GetTotalChecksGotten();
bool IsAreaSpoiled(RandomizerCheckArea rcArea);
void SetAreaSpoiled(RandomizerCheckArea rcArea);
void UpdateInventoryChecks();
void UpdateAreas(RandomizerCheckArea area);
void UpdateAllOrdering();
void UpdateAllAreas();
void RecalculateAllAreaTotals();
} // namespace CheckTracker
void to_json(nlohmann::json & j, const RandomizerCheckTrackerData& rctd);
void from_json(const nlohmann::json& j, RandomizerCheckTrackerData& rctd);

View File

@ -97,7 +97,7 @@ void GiveLinksPocketItem() {
if (Randomizer_GetSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, (GetItemID)RG_NONE);
StartingItemGive(getItemEntry);
Rando::Context::GetInstance()->GetItemLocation(RC_LINKS_POCKET)->MarkAsObtained();
Rando::Context::GetInstance()->GetItemLocation(RC_LINKS_POCKET)->SetCheckStatus(RCSHOW_SAVED);
// If we re-add the above, we'll get the item on save creation, now it's given on first load
Flags_SetRandomizerInf(RAND_INF_LINKS_POCKET);
}

View File

@ -111,6 +111,7 @@ SaveManager::SaveManager() {
coreSectionIDsByName["sohStats"] = SECTION_ID_STATS;
coreSectionIDsByName["entrances"] = SECTION_ID_ENTRANCES;
coreSectionIDsByName["scenes"] = SECTION_ID_SCENES;
coreSectionIDsByName["trackerData"] = SECTION_ID_TRACKER_DATA;
AddLoadFunction("base", 1, LoadBaseVersion1);
AddLoadFunction("base", 2, LoadBaseVersion2);
AddLoadFunction("base", 3, LoadBaseVersion3);
@ -404,13 +405,6 @@ void SaveManager::LoadRandomizerVersion3() {
// all ItemLocations is 0 anyway.
randoContext->GetItemLocation(i)->SetCustomPrice(price);
}
uint16_t obtained = 0;
SaveManager::Instance->LoadData("obtained", obtained, (uint16_t)0);
if (obtained) {
randoContext->GetItemLocation(i)->MarkAsObtained();
} else {
randoContext->GetItemLocation(i)->MarkAsNotObtained();
}
});
});
@ -496,7 +490,6 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
if (randoContext->GetItemLocation(i)->HasCustomPrice()) {
SaveManager::Instance->SaveData("price", randoContext->GetItemLocation(i)->GetPrice());
}
SaveManager::Instance->SaveData("obtained", randoContext->GetItemLocation(i)->HasObtained());
});
});
@ -666,8 +659,8 @@ void SaveManager::Init() {
if (std::filesystem::exists(GetFileName(fileNum))) {
LoadFile(fileNum);
saveBlock = nlohmann::json::object();
OTRGlobals::Instance->gRandoContext->ClearItemLocations();
}
}
}

View File

@ -163,6 +163,8 @@ class SaveManager {
static void LoadRandomizerVersion1();
static void LoadRandomizerVersion2();
static void LoadRandomizerVersion3();
static void LoadTrackerData();
static void SaveTrackerData(SaveContext* saveContext, int sectionID, bool fullSave);
static void SaveRandomizer(SaveContext* saveContext, int sectionID, bool fullSave);
static void LoadBaseVersion1();