mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-10-13 07:25:54 -04:00
Fix playthrough paring to not rely on checks past wincon (#7275)
Implement DelayItem to bypass SetPlacedItem setting price, which when restored would leave behind price of RG_NONE
This commit is contained in:
@@ -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<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerChec
|
||||
}
|
||||
|
||||
// Create the playthrough for the seed
|
||||
void GeneratePlaythrough() {
|
||||
std::vector<RandomizerCheck> 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<RandomizerCheck>& lateLocations) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
std::vector<RandomizerCheck> 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<int32_t>(ctx->playthroughLocations.size()) - 2; i >= 0; i--) {
|
||||
// Check each item location in sphere
|
||||
std::vector<int> erasableIndices;
|
||||
std::vector<RandomizerCheck> sphere = ctx->playthroughLocations.at(i);
|
||||
for (int32_t j = static_cast<int32_t>(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<RandomizerCheck> 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);
|
||||
|
||||
@@ -13,6 +13,7 @@ struct GetAccessibleLocationsStruct {
|
||||
int gsCount;
|
||||
int maxGsCount;
|
||||
std::vector<LogicVal> buyIgnores;
|
||||
std::vector<RandomizerCheck> lateLocations;
|
||||
|
||||
// Variables for search
|
||||
std::vector<Rando::ItemLocation*> newItemLocations;
|
||||
@@ -68,7 +69,7 @@ std::vector<RandomizerCheck> ReachabilitySearch(const std::vector<RandomizerChec
|
||||
RandomizerRegion startingRegion = RR_ROOT,
|
||||
RandoAgeTime startingAgeTime = RAT_NONE);
|
||||
|
||||
void GeneratePlaythrough();
|
||||
std::vector<RandomizerCheck> GeneratePlaythrough();
|
||||
|
||||
bool CheckBeatable(RandomizerGet ignore = RG_NONE);
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<RandomizerArea> GetAreas() const;
|
||||
|
||||
Reference in New Issue
Block a user