Skip to content

Commit 58b3aae

Browse files
fix: Repeat items on tokens (#125)
* Update fill.cpp - Add variable used when filling SSH & OSH with non repeatable items (NRI) - Add check to stop warning about too many items and not enough locations for SSH & OSH NRI - Add check to stop placement from attempting to continue after all locations were filled in SSH & OSH NRI situation - Changes SSH & OSH NRI Fill from FastFill to AssumedFill to prevent softlocks and item placement issues * Update fill.cpp fixed spaces to appease phlex * Update fill.cpp Adjust all AssumedFIll calls to have locationshintable be true * Update Z3DR --------- Co-authored-by: Tacoman369 <90735287+Tacoman369@users.noreply.github.com>
1 parent 67e3fee commit 58b3aae

2 files changed

Lines changed: 129 additions & 56 deletions

File tree

Z3DR

source/fill.cpp

Lines changed: 128 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ using namespace Logic;
2424
using namespace Settings;
2525

2626
static bool placementFailure = false;
27+
static bool NoRepeatOnTokens = false;
2728

2829
static void RemoveStartingItemsFromPool() {
2930
for (ItemKey startingItem : StartingInventory) {
@@ -377,23 +378,30 @@ static void FastFill(std::vector<ItemKey> items, std::vector<LocationKey> locati
377378
static void AssumedFill(const std::vector<ItemKey>& items, const std::vector<LocationKey>& allowedLocations, bool setLocationsAsHintable = false) {
378379

379380
if (items.size() > allowedLocations.size()) {
380-
printf("\x1b[2;2HERROR: MORE ITEMS THAN LOCATIONS IN GIVEN LISTS");
381-
PlacementLog_Msg("Items:\n");
382-
for (const ItemKey item : items) {
383-
PlacementLog_Msg("\t");
384-
PlacementLog_Msg(ItemTable(item).GetName().GetEnglish());
385-
PlacementLog_Msg("\n");
381+
//If Tokensanity is on and RepeatableItemsOnTokens is off
382+
//Don't display this message
383+
if (NoRepeatOnTokens) {
384+
//do nothing here to not display the message
386385
}
387-
PlacementLog_Msg("\nAllowed Locations:\n");
388-
for (const LocationKey loc : allowedLocations) {
389-
PlacementLog_Msg("\t");
390-
PlacementLog_Msg(Location(loc)->GetName());
391-
PlacementLog_Msg("\n");
386+
else {
387+
printf("\x1b[2;2HERROR: MORE ITEMS THAN LOCATIONS IN GIVEN LISTS");
388+
PlacementLog_Msg("Items:\n");
389+
for (const ItemKey item : items) {
390+
PlacementLog_Msg("\t");
391+
PlacementLog_Msg(ItemTable(item).GetName().GetEnglish());
392+
PlacementLog_Msg("\n");
393+
}
394+
PlacementLog_Msg("\nAllowed Locations:\n");
395+
for (const LocationKey loc : allowedLocations) {
396+
PlacementLog_Msg("\t");
397+
PlacementLog_Msg(Location(loc)->GetName());
398+
PlacementLog_Msg("\n");
399+
}
400+
PlacementLog_Write();
401+
placementFailure = true;
402+
return;
392403
}
393-
PlacementLog_Write();
394-
placementFailure = true;
395-
return;
396-
}
404+
}
397405

398406
//If No Logic fast fill everything
399407
if (Settings::Logic.Is(LogicSetting::LOGIC_NONE)) {
@@ -427,9 +435,9 @@ static void AssumedFill(const std::vector<ItemKey>& items, const std::vector<Loc
427435
//{
428436
// PlacementLog_Msg(" " + ItemTable(items).GetName().GetEnglish() + "," );
429437
//}
430-
431438
//shuffle the order of items to place
432439
Shuffle(itemsToPlace);
440+
433441
while (!itemsToPlace.empty()) {
434442
ItemKey item = std::move(itemsToPlace.back());
435443
ItemTable(item).SetAsPlaythrough();
@@ -472,6 +480,17 @@ static void AssumedFill(const std::vector<ItemKey>& items, const std::vector<Loc
472480
//retry if there are no more locations to place items
473481
if (accessibleLocations.empty()) {
474482

483+
//If Tokensanity is on and RepeatableItemsOnTokens is off
484+
//the item pool sent is much larger than the location pool
485+
//in this case we just place 30 of the items and then move on
486+
if (NoRepeatOnTokens) {
487+
//put back the last item we picked up
488+
itemsToPlace.push_back(item);
489+
//then put the unused items back into the main pool and stop
490+
AddElementsToPool(ItemPool, itemsToPlace);
491+
break;
492+
}
493+
475494
PlacementLog_Msg("\nCANNOT PLACE ");
476495
PlacementLog_Msg(ItemTable(item).GetName().GetEnglish());
477496
PlacementLog_Msg(". TRYING AGAIN...\n");
@@ -492,37 +511,73 @@ static void AssumedFill(const std::vector<ItemKey>& items, const std::vector<Loc
492511
break;
493512
}
494513

495-
//place the item within one of the allowed locations accounting for if this item needs to be able to be obtained more than once and if location allows that
496-
//the only situation we don't want is a non repeatable location with a reusable item
497514
LocationKey selectedLocation = RandomElement(accessibleLocations);
498-
if ( !(Location(selectedLocation)->IsRepeatable()) && ItemTable(item).IsReusable() ){
499-
//unsuccessfulPlacement = true;
500-
CitraPrint("Attemting to place repeatable item in non repeatable spot in AssumedFill");
501-
PlacementLog_Msg("\n Attempted to place " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
515+
516+
//If Tokensanity is on and RepeatableItemsOnTokens is off
517+
//Only place non repeatable items
518+
if (NoRepeatOnTokens) {
519+
//If the item is repeatable put it back and try again
520+
if (ItemTable(item).IsReusable() ) {
521+
CitraPrint("Attempting to place Repeatable Item in SSH/OSH Location");
522+
PlacementLog_Msg("\n Attempted to place Repeatable Item in SSH/OSH Loaction");
502523
itemsToPlace.push_back(item);
503524
}
504-
else {
505-
PlaceItemInLocation(selectedLocation, item);
506-
//PlacementLog_Msg("Placed " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
507-
//CitraPrint("Placed " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
508-
attemptedLocations.push_back(selectedLocation);
509-
510-
//This tells us the location went through the randomization algorithm
511-
//to distinguish it from locations which did not or that the player already
512-
//knows
513-
if (setLocationsAsHintable) {
514-
Location(selectedLocation)->SetAsHintable();
525+
else {
526+
//Else place it and keep going
527+
PlaceItemInLocation(selectedLocation, item);
528+
attemptedLocations.push_back(selectedLocation);
529+
//This tells us the location went through the randomization algorithm
530+
//to distinguish it from locations which did not or that the player already
531+
//knows
532+
if (setLocationsAsHintable) {
533+
Location(selectedLocation)->SetAsHintable();
534+
}
535+
536+
//If ALR is off, then we check beatability after placing the item.
537+
//If the game is beatable, then we can stop placing items with logic.
538+
if (!LocationsReachable) {
539+
playthroughBeatable = false;
540+
Logic::LogicReset();
541+
GetAccessibleLocations(allLocations, SearchMode::CheckBeatable);
542+
if (playthroughBeatable) {
543+
FastFill(itemsToPlace, GetAllEmptyLocations(), true);
544+
return;
545+
}
546+
}
515547
}
516-
517-
//If ALR is off, then we check beatability after placing the item.
518-
//If the game is beatable, then we can stop placing items with logic.
519-
if (!LocationsReachable) {
520-
playthroughBeatable = false;
521-
Logic::LogicReset();
522-
GetAccessibleLocations(allLocations, SearchMode::CheckBeatable);
523-
if (playthroughBeatable) {
524-
FastFill(itemsToPlace, GetAllEmptyLocations(), true);
525-
return;
548+
}
549+
else {
550+
//place the item within one of the allowed locations accounting for if this item needs to be able to be obtained more than once and if location allows that
551+
//the only situation we don't want is a non repeatable location with a reusable item
552+
if ( !(Location(selectedLocation)->IsRepeatable()) && ItemTable(item).IsReusable() ){
553+
//unsuccessfulPlacement = true;
554+
CitraPrint("Attemting to place repeatable item in non repeatable spot in AssumedFill");
555+
PlacementLog_Msg("\n Attempted to place " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
556+
itemsToPlace.push_back(item);
557+
}
558+
else {
559+
PlaceItemInLocation(selectedLocation, item);
560+
//PlacementLog_Msg("Placed " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
561+
//CitraPrint("Placed " + ItemTable(item).GetName().GetEnglish() + " at " + Location(selectedLocation)->GetName());
562+
attemptedLocations.push_back(selectedLocation);
563+
564+
//This tells us the location went through the randomization algorithm
565+
//to distinguish it from locations which did not or that the player already
566+
//knows
567+
if (setLocationsAsHintable) {
568+
Location(selectedLocation)->SetAsHintable();
569+
}
570+
571+
//If ALR is off, then we check beatability after placing the item.
572+
//If the game is beatable, then we can stop placing items with logic.
573+
if (!LocationsReachable) {
574+
playthroughBeatable = false;
575+
Logic::LogicReset();
576+
GetAccessibleLocations(allLocations, SearchMode::CheckBeatable);
577+
if (playthroughBeatable) {
578+
FastFill(itemsToPlace, GetAllEmptyLocations(), true);
579+
return;
580+
}
526581
}
527582
}
528583
}
@@ -547,7 +602,7 @@ std::vector<ItemKey> rewards = FilterAndEraseFromPool(ItemPool, [](const ItemKey
547602
}
548603
}
549604
else if (ShuffleRewards.Is((u8)RewardShuffleSetting::REWARDSHUFFLE_ANYWHERE)){
550-
AssumedFill(rewards, allLocations);
605+
AssumedFill(rewards, allLocations, true);
551606
}
552607
}
553608

@@ -601,12 +656,12 @@ static void RandomizeOwnDungeon(const Dungeon::DungeonInfo* dungeon) {
601656
}
602657

603658
//randomize boss key and small keys together for even distribution
604-
AssumedFill(dungeonItems, dungeonLocations);
659+
AssumedFill(dungeonItems, dungeonLocations, true);
605660

606661
//randomize map and compass separately since they're not progressive
607662
if (MapsAndCompasses.Is((u8)MapsAndCompassesSetting::MAPSANDCOMPASSES_OWN_DUNGEON) && dungeon->GetMap() != NONE && dungeon->GetCompass() != NONE) {
608663
auto dungeonMapAndCompass = FilterAndEraseFromPool(ItemPool, [dungeon](const ItemKey i) { return i == dungeon->GetMap() || i == dungeon->GetCompass();});
609-
AssumedFill(dungeonMapAndCompass, dungeonLocations);
664+
AssumedFill(dungeonMapAndCompass, dungeonLocations, true);
610665
}
611666
}
612667

@@ -817,20 +872,38 @@ int Fill() {
817872
}
818873
//Then if repeatable items on tokens is off -- fill token spots with nonrepeatable items
819874
if (Tokensanity && !RepeatableItemsOnTokens){
820-
//Get all nonrepeatable items
821-
std::vector<ItemKey> remainingNonRepeatItemPool = FilterAndEraseFromPool(ItemPool, [](const ItemKey i) {return ItemTable(i).IsReusable() == false;});
875+
//Set Variable to not mess with fill algorithm
876+
NoRepeatOnTokens = true;
877+
//Get SSH locations
822878
std::vector<LocationKey> SwampSkullLocations = FilterFromPool(allLocations, [](const LocationKey loc) {return Location(loc)->IsCategory(Category::cSwampSkulltula);});
823-
//CitraPrint("Starting Assumed Fill on Swamp Locations");
824-
//fill skulltula spots with them
825-
FastFill(remainingNonRepeatItemPool, SwampSkullLocations);
879+
//Get NonRepeatItems
880+
std::vector<ItemKey> NonRepeatItems = FilterAndEraseFromPool(ItemPool, [](const ItemKey i) {return !ItemTable(i).IsReusable();});
881+
//fill SSH spots with nonrepeatableitems
882+
//CitraPrint("Items in NonRepeatItems:\n");
883+
//PlacementLog_Msg("Items in NonRepeatItems:\n");
884+
//for (ItemKey item : NonRepeatItems) {
885+
// CitraPrint(ItemTable(item).GetName().GetEnglish() + "\n");
886+
// PlacementLog_Msg(ItemTable(item).GetName().GetEnglish() + "\n");
887+
// }
888+
AssumedFill(NonRepeatItems, SwampSkullLocations, true);
826889

890+
//Get OSH locations
827891
std::vector<LocationKey> OceanSkullLocations = FilterFromPool(allLocations, [](const LocationKey loc1) {return Location(loc1)->IsCategory(Category::cOceanSkulltula);});
828-
//CitraPrint("Starting Assumed Fill on Ocean Locations");
829-
FastFill(remainingNonRepeatItemPool, OceanSkullLocations);
892+
//Get NonRepeatItems
893+
std::vector<ItemKey> NonRepeatItems2 = FilterAndEraseFromPool(ItemPool, [](const ItemKey i) {return !ItemTable(i).IsReusable();});
894+
//CitraPrint("Items in NonRepeatItems2:\n");
895+
//PlacementLog_Msg("Items in NonRepeatItems2:\n");
896+
//for (ItemKey item : NonRepeatItems2) {
897+
// CitraPrint(ItemTable(item).GetName().GetEnglish() + "\n");
898+
// PlacementLog_Msg(ItemTable(item).GetName().GetEnglish() + "\n");
899+
// }
900+
//fill OSH spots with NonRepeatableItems
901+
AssumedFill(NonRepeatItems2, OceanSkullLocations, true);
902+
//Set Variable back to false to go back to normal filling
903+
NoRepeatOnTokens = false;
830904

831-
//CitraPrint("Adding RemainingNonRepeatItems back to main ItemPool");
832-
AddElementsToPool(ItemPool, remainingNonRepeatItemPool);
833905
//Then Place Anju & Kafei Items in spots accessable on Day 1, this should prevent situations where you cant get an item in time for its use
906+
//Do this before continuing as the pool of accessable locations is smaller after filling all skulltula locations
834907
std::vector<LocationKey> day1Locations = FilterFromPool(allLocations, [](const LocationKey loc) {return Location(loc)->IsCategory(Category::cDayOne);});
835908
std::vector<ItemKey> anjukafeiitems = FilterAndEraseFromPool(ItemPool, [](const ItemKey i) {return ItemTable(i).GetItemType() == ITEMTYPE_QUEST;});
836909
AssumedFill(anjukafeiitems, day1Locations,true);

0 commit comments

Comments
 (0)