diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.cpp b/soh/soh/Enhancements/randomizer/3drando/fill.cpp index 0f8f0ccbf2..afa2efb03c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.cpp @@ -304,13 +304,12 @@ bool IsBombchus(RandomizerGet item, bool includeShops = false) { bool IsBeatableWithout(RandomizerCheck excludedCheck, bool replaceItem, RandomizerGet ignore = RG_NONE) { // RANDOTODO make excludedCheck an ItemLocation - auto ctx = Rando::Context::GetInstance(); - RandomizerGet copy = ctx->GetItemLocation(excludedCheck)->GetPlacedRandomizerGet(); // Copy out item - ctx->GetItemLocation(excludedCheck)->SetPlacedItem(RG_NONE); // Write in empty item + Rando::ItemLocation* location = Rando::Context::GetInstance()->GetItemLocation(excludedCheck); + location->DelayItem(); logic->Reset(); bool result = CheckBeatable(ignore); if (replaceItem) { - ctx->GetItemLocation(excludedCheck)->SetPlacedItem(copy); // Immediately put item back + location->SaveDelayedItem(); // Immediately put item back } return result; } @@ -338,6 +337,10 @@ void AddToPlaythrough(LocationAccess& locPair, GetAccessibleLocationsStruct& gal RandomizerCheck loc = locPair.GetLocation(); Rando::ItemLocation* location = ctx->GetItemLocation(loc); RandomizerGet locItem = location->GetPlacedRandomizerGet(); + // Reached after wincon, so never part of playthrough. Pare first + if (ctx->playthroughBeatable && location->GetPlacedItem().IsAdvancement()) { + gals.lateLocations.push_back(loc); + } // Item is an advancement item, figure out if it should be added to this sphere if (!ctx->playthroughBeatable && location->GetPlacedItem().IsAdvancement()) { ItemType type = location->GetPlacedItem().GetItemType(); @@ -378,6 +381,12 @@ void AddToPlaythrough(LocationAccess& locPair, GetAccessibleLocationsStruct& gal gals.itemSphere.clear(); gals.itemSphere.push_back(loc); ctx->playthroughBeatable = true; + // Items found in this sphere aren't collected before wincon + for (Rando::ItemLocation* pending : gals.newItemLocations) { + if (pending != location && pending->GetPlacedItem().IsAdvancement()) { + gals.lateLocations.push_back(pending->GetRandomizerCheck()); + } + } } } } @@ -565,7 +574,7 @@ std::vector ReachabilitySearch(const std::vector GeneratePlaythrough() { auto ctx = Rando::Context::GetInstance(); ctx->playthroughBeatable = false; logic->Reset(); @@ -589,6 +598,7 @@ void GeneratePlaythrough() { ctx->GetEntranceShuffler()->playthroughEntrances.push_back(gals.entranceSphere); } } while (gals.logicUpdated); + return gals.lateLocations; } // return if the seed is currently beatable or not @@ -710,17 +720,21 @@ void SetAreas() { // Remove unnecessary items from playthrough by removing their location, and checking if game is still beatable // To reduce searches, some preprocessing is done in playthrough generation to avoid adding obviously unnecessary items -static void PareDownPlaythrough() { +static void PareDownPlaythrough(const std::vector& lateLocations) { auto ctx = Rando::Context::GetInstance(); std::vector toAddBackItem; + // Hide items only reachable after wincon. Otherwise they can stand in for a removed playthrough item + for (RandomizerCheck loc : lateLocations) { + ctx->GetItemLocation(loc)->DelayItem(); + toAddBackItem.push_back(loc); + } // Start at sphere before Ganon's and count down for (int32_t i = static_cast(ctx->playthroughLocations.size()) - 2; i >= 0; i--) { // Check each item location in sphere - std::vector erasableIndices; std::vector sphere = ctx->playthroughLocations.at(i); for (int32_t j = static_cast(sphere.size()) - 1; j >= 0; j--) { RandomizerCheck loc = sphere.at(j); - RandomizerGet locGet = ctx->GetItemLocation(loc)->GetPlacedRandomizerGet(); // Copy out item + RandomizerGet locGet = ctx->GetItemLocation(loc)->GetPlacedRandomizerGet(); RandomizerGet ignore = RG_NONE; if (locGet == RG_GOLD_SKULLTULA_TOKEN || IsBombchus(locGet, true) || @@ -731,10 +745,9 @@ static void PareDownPlaythrough() { // Playthrough is still beatable without this item, therefore it can be removed from playthrough section. if (IsBeatableWithout(loc, false, ignore)) { ctx->playthroughLocations[i].erase(ctx->playthroughLocations[i].begin() + j); - ctx->GetItemLocation(loc)->SetDelayedItem(locGet); // Game is still beatable, don't add back until later - toAddBackItem.push_back(loc); + toAddBackItem.push_back(loc); // Game is still beatable, don't add back until later } else { - ctx->GetItemLocation(loc)->SetPlacedItem(locGet); // Immediately put item back so game is beatable again + ctx->GetItemLocation(loc)->SaveDelayedItem(); // Immediately put item back so game is beatable again } } } @@ -1459,13 +1472,13 @@ int Fill() { StopPerformanceTimer(PT_REMAINING_ITEMS); StartPerformanceTimer(PT_PLAYTHROUGH_GENERATION); - GeneratePlaythrough(); + std::vector lateLocations = GeneratePlaythrough(); StopPerformanceTimer(PT_PLAYTHROUGH_GENERATION); // Successful placement, produced beatable result if (ctx->playthroughBeatable && !placementFailure) { SPDLOG_INFO("Calculating Playthrough..."); StartPerformanceTimer(PT_PARE_DOWN_PLAYTHROUGH); - PareDownPlaythrough(); + PareDownPlaythrough(lateLocations); StopPerformanceTimer(PT_PARE_DOWN_PLAYTHROUGH); StartPerformanceTimer(PT_WOTH); diff --git a/soh/soh/Enhancements/randomizer/3drando/fill.hpp b/soh/soh/Enhancements/randomizer/3drando/fill.hpp index 0c972bb427..055f909e21 100644 --- a/soh/soh/Enhancements/randomizer/3drando/fill.hpp +++ b/soh/soh/Enhancements/randomizer/3drando/fill.hpp @@ -13,6 +13,7 @@ struct GetAccessibleLocationsStruct { int gsCount; int maxGsCount; std::vector buyIgnores; + std::vector lateLocations; // Variables for search std::vector newItemLocations; @@ -68,7 +69,7 @@ std::vector ReachabilitySearch(const std::vector GeneratePlaythrough(); bool CheckBeatable(RandomizerGet ignore = RG_NONE); diff --git a/soh/soh/Enhancements/randomizer/item_location.cpp b/soh/soh/Enhancements/randomizer/item_location.cpp index 1bfe380d01..cfdf535c3a 100644 --- a/soh/soh/Enhancements/randomizer/item_location.cpp +++ b/soh/soh/Enhancements/randomizer/item_location.cpp @@ -48,8 +48,9 @@ void ItemLocation::SetPlacedItem(const RandomizerGet item) { SetPrice(StaticData::RetrieveItem(placedItem).GetPrice()); } -void ItemLocation::SetDelayedItem(const RandomizerGet item) { - delayedItem = item; +void ItemLocation::DelayItem() { + delayedItem = placedItem; + placedItem = RG_NONE; } void ItemLocation::SaveDelayedItem() { diff --git a/soh/soh/Enhancements/randomizer/item_location.h b/soh/soh/Enhancements/randomizer/item_location.h index d96ab16adc..8064462ba5 100644 --- a/soh/soh/Enhancements/randomizer/item_location.h +++ b/soh/soh/Enhancements/randomizer/item_location.h @@ -18,7 +18,7 @@ class ItemLocation { RandomizerGet GetPlacedRandomizerGet() const; void SetPlacedItem(RandomizerGet item); RandomizerGet& RefPlacedItem(); - void SetDelayedItem(RandomizerGet item); + void DelayItem(); RandomizerRegion GetParentRegionKey() const; void SetParentRegion(RandomizerRegion region); std::set GetAreas() const;