Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gui/desktop/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ void MainWindow::apply_config_settings()
APPLY_SPINBOX_SETTING(config, ui, damage_multiplier, float(2.0f), float(MAXIMUM_DAMAGE_MULTIPLIER));

auto& num_required_dungeons = config.settings.num_required_dungeons;
// Race mode dungeons must be between 1 and 6 if race mode is enabled
// Required dungeons must be between 1 and 6 if race mode is enabled
if (config.settings.progression_dungeons == ProgressionDungeons::RaceMode)
{
num_required_dungeons = std::clamp(num_required_dungeons, uint8_t(1), uint8_t(MAXIMUM_NUM_DUNGEONS));
Expand Down Expand Up @@ -771,7 +771,7 @@ DEFINE_STATE_CHANGE_FUNCTION(progression_combat_secret_caves)
void MainWindow::on_progression_dungeons_currentTextChanged(const QString &arg1)
{
config.settings.progression_dungeons = nameToProgressionDungeons(arg1.toStdString());
// Grey out the race mode dungeons combobox if race mode/standard is not selected
// Grey out the required dungeons combobox if dungeons are disabled
if (config.settings.progression_dungeons == ProgressionDungeons::RaceMode)
{
ui->num_required_dungeons->setEnabled(true);
Expand Down
4 changes: 2 additions & 2 deletions logic/Dungeon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ struct Dungeon {
Item map = Item();
Item compass = Item();
std::list<Location*> locations = {};
Location* raceModeLocation = nullptr;
Location* bossLocation = nullptr;
Area* startingArea = nullptr;
Entrance* startingEntrance = nullptr;
std::list<std::string> islands = {};
std::string name = "";
bool isRequiredDungeon = false;
bool hasNaturalRaceModeLocation = false;
bool hasNaturalBossLocation = false;
std::string windWarpExitStage = "";
uint8_t windWarpExitRoom = 0;
uint8_t windWarpExitSpawn = 0;
Expand Down
21 changes: 8 additions & 13 deletions logic/EntranceShuffle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,25 +306,24 @@ static EntranceShuffleError validateWorld(WorldPool& worlds, const Entrance* ent

for (auto& world : worlds)
{
// Ensure that all race mode bosses are assigned to a single island
// Ensure that all required bosses are assigned to a single island
auto& settings = world.getSettings();
if (settings.progression_dungeons != ProgressionDungeons::Disabled && settings.num_required_dungeons > 0)
{
std::unordered_set<std::string> raceModeIslands = {};
for (auto loc : world.raceModeLocations)
for (auto loc : world.bossLocations)
{
auto bossIslands = loc->accessPoints.front()->area->findIslands();
const auto& bossIslands = loc->accessPoints.front()->area->findIslands();

if (bossIslands.size() > 1)
{
#ifdef ENABLE_DEBUG
LOG_TO_DEBUG("Error: More than 1 island leading to race mode boss room " + loc->accessPoints.front()->area->name);
LOG_TO_DEBUG("Error: More than 1 island leading to required boss room " + loc->accessPoints.front()->area->name);
for (auto& island : bossIslands)
{
LOG_TO_DEBUG("\t" + island);
}
#endif
return EntranceShuffleError::AMBIGUOUS_RACE_MODE_ISLAND;
return EntranceShuffleError::AMBIGUOUS_BOSS_ISLAND;
}
}
}
Expand Down Expand Up @@ -952,7 +951,7 @@ EntranceShuffleError randomizeEntrances(WorldPool& worlds)
}
}

// Now set the islands the race mode dungeons are in
// Now set the islands the dungeons are in
for (auto& [name, dungeon] : world.dungeons)
{
dungeon.islands = dungeon.startingArea->findIslands();
Expand Down Expand Up @@ -982,12 +981,8 @@ const std::string errorToName(EntranceShuffleError err)
return "NO_MORE_VALID_ENTRANCES";
case EntranceShuffleError::ALL_LOCATIONS_NOT_REACHABLE:
return "ALL_LOCATIONS_NOT_REACHABLE";
case EntranceShuffleError::AMBIGUOUS_RACE_MODE_ISLAND:
return "AMBIGUOUS_RACE_MODE_ISLAND";
case EntranceShuffleError::AMBIGUOUS_RACE_MODE_DUNGEON:
return "AMBIGUOUS_RACE_MODE_DUNGEON";
case EntranceShuffleError::NO_RACE_MODE_ISLAND:
return "NO_RACE_MODE_ISLAND";
case EntranceShuffleError::AMBIGUOUS_BOSS_ISLAND:
return "AMBIGUOUS_BOSS_ISLAND";
case EntranceShuffleError::NOT_ENOUGH_SPHERE_ZERO_LOCATIONS:
return "NOT_ENOUGH_SPHERE_ZERO_LOCATIONS";
case EntranceShuffleError::ATTEMPTED_SELF_CONNECTION:
Expand Down
4 changes: 1 addition & 3 deletions logic/EntranceShuffle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ enum struct EntranceShuffleError
RAN_OUT_OF_RETRIES,
NO_MORE_VALID_ENTRANCES,
ALL_LOCATIONS_NOT_REACHABLE,
AMBIGUOUS_RACE_MODE_ISLAND,
AMBIGUOUS_RACE_MODE_DUNGEON,
NO_RACE_MODE_ISLAND,
AMBIGUOUS_BOSS_ISLAND,
NOT_ENOUGH_SPHERE_ZERO_LOCATIONS,
ATTEMPTED_SELF_CONNECTION,
FAILED_TO_DISCONNECT_TARGET,
Expand Down
48 changes: 24 additions & 24 deletions logic/Fill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,7 @@ void placeVanillaItems(WorldPool& worlds)
// Determine which items are major items. A major item is any item required
// for access to any progression location and/or game beatability. This function
// is called multiple times during the fill algorithm as the items required for
// beatability may change depending on which items are placed in race mode locations
// and/or plandomized
// beatability may change depending on which items are placed in boss locations and/or plandomized
void determineMajorItems(WorldPool& worlds, ItemPool& itemPool, LocationPool& allLocations)
{
LOG_TO_DEBUG("Determining Major Items");
Expand Down Expand Up @@ -405,7 +404,7 @@ static FillError randomizeOwnDungeon(WorldPool& worlds, ItemPool& itemPool)
{
// Filter to only the dungeons locations which are progression locations
// If dungeons are not progression locations, or if race mode is on
// and this isn't a race mode dungeon, then take all locations in
// and this isn't a required dungeon, then take all locations in
// the dungeon since none of them are progression anyway
auto worldLocations = world.getLocations();
auto dungeonLocations = filterFromPool(worldLocations, [&dungeon = dungeon, &settings = settings](const Location* loc){
Expand Down Expand Up @@ -586,66 +585,67 @@ static FillError handleDungeonItems(WorldPool& worlds, ItemPool& itemPool)
return FillError::NONE;
}

static void generateRaceModeItems(const LocationPool& raceModeLocations, ItemPool& raceModeItems, ItemPool& itemsToChooseFrom, ItemPool& mainItemPool)
static void generateBossItems(const LocationPool& bossLocations, ItemPool& chosenItems, ItemPool& itemsToChooseFrom, ItemPool& mainItemPool)
{
shufflePool(itemsToChooseFrom);
while (!itemsToChooseFrom.empty() && raceModeItems.size() < raceModeLocations.size())
while (!itemsToChooseFrom.empty() && chosenItems.size() < bossLocations.size())
{
raceModeItems.push_back(popRandomElement(itemsToChooseFrom));
chosenItems.push_back(popRandomElement(itemsToChooseFrom));
}
// Add back any unused elements
addElementsToPool(mainItemPool, itemsToChooseFrom);
}

// Place progression items in specific locations at the end of dungeons to require the player
// to beat those dungeons.
static FillError placeRaceModeItems(WorldPool& worlds, ItemPool& itemPool, LocationPool& allLocations)
static FillError placeBossItems(WorldPool& worlds, ItemPool& itemPool, LocationPool& allLocations)
{
LocationPool raceModeLocations;
ItemPool raceModeItems;
LocationPool bossLocations;
ItemPool bossItems;
for (auto& world : worlds)
{
if(world.getSettings().required_boss_items) {
for (auto& [name, dungeon] : world.dungeons)
{
if (dungeon.isRequiredDungeon)
{
auto raceModeLocation = dungeon.raceModeLocation;
auto bossLocation = dungeon.bossLocation;
// If this location already has an item placed at it, then skip it
if (raceModeLocation->currentItem.getGameItemId() != GameItem::INVALID)
if (bossLocation->currentItem.getGameItemId() != GameItem::INVALID)
{
continue;
}
raceModeLocations.push_back(raceModeLocation);
bossLocations.push_back(bossLocation);
}
}
}
}

// Build up the list of boss items with major items.
auto majorItems = filterAndEraseFromPool(itemPool, [](const Item& item){return item.isMajorItem();});
generateRaceModeItems(raceModeLocations, raceModeItems, majorItems, itemPool);
generateBossItems(bossLocations, bossItems, majorItems, itemPool);

// logItemPool("raceModeItems", raceModeItems);
logItemPool("Boss Items", bossItems);

if (raceModeItems.size() < raceModeLocations.size())
if (bossItems.size() < bossLocations.size())
{
Utility::platformLog("WARNING: Not enough major items to place at race mode locations.");
Utility::platformLog("WARNING: Not enough major items to place at boss locations.");
}

// Then place the items in the race mode locations
// Then place the items in the boss locations
FillError err;
FILL_ERROR_CHECK(assumedFill(worlds, raceModeItems, itemPool, raceModeLocations));
FILL_ERROR_CHECK(assumedFill(worlds, bossItems, itemPool, bossLocations));

// Set race mode locations which had items placed at them as having expected items
for (auto raceModeLoc : raceModeLocations)
// Set boss locations which had items placed at them as having expected items
for (auto loc : bossLocations)
{
raceModeLoc->hasExpectedItem = true;
loc->hasExpectedItem = true;
}

// Recalculate major items since new items may now be required depending on
// what items were placed at race mode locations
// what items were placed at boss locations
determineMajorItems(worlds, itemPool, allLocations);

return FillError::NONE;
}

Expand Down Expand Up @@ -734,10 +734,10 @@ FillError fill(WorldPool& worlds)

determineMajorItems(worlds, itemPool, allLocations);
FILL_ERROR_CHECK(placeNonProgressLocationPlandomizerItems(worlds, itemPool));
// Handle dungeon items and race mode dungeons first if necessary. Generally
// Handle dungeon and/or boss items first if necessary. Generally
// we need to place items that go into more restrictive location pools first before
// we can place other items.
FILL_ERROR_CHECK(placeRaceModeItems(worlds, itemPool, allLocations));
FILL_ERROR_CHECK(placeBossItems(worlds, itemPool, allLocations));
FILL_ERROR_CHECK(handleDungeonItems(worlds, itemPool));

// Recalculate major items again since new items may now be required depending on
Expand Down
4 changes: 2 additions & 2 deletions logic/Generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ int generateWorlds(WorldPool& worlds, std::vector<Settings>& settingsVector)

// Now that all entrances have been randomized, we can flatten the world's
// logic requirements for some useful operations later on
// Determine race mode dungeons after entrance randomizer to ensure we pick
// Determine required dungeons after entrance randomizer to ensure we pick
// dungeons which can be properly reached depending on any entrance rando settings
for (auto& world : worlds)
{
world.flattenLogicRequirements();
WORLD_LOADING_ERROR_CHECK(world.setDungeonLocations(worlds));
WORLD_LOADING_ERROR_CHECK(world.determineRaceModeDungeons(worlds));
WORLD_LOADING_ERROR_CHECK(world.determineRequiredDungeons(worlds));
}

// Retry the main fill algorithm a couple times incase it completely fails.
Expand Down
12 changes: 6 additions & 6 deletions logic/Hints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ static HintError calculatePossiblePathLocations(WorldPool& worlds)
world.goalLocations.push_back(world.locationTable["Ganon's Tower - Defeat Ganondorf"].get());
for (auto& [name, dungeon] : world.dungeons)
{
// Race mode locations are also goal locations
// Boss locations are also goal locations
if (dungeon.isRequiredDungeon)
{
world.goalLocations.push_back(dungeon.raceModeLocation);
world.goalLocations.push_back(dungeon.bossLocation);
}
}

Expand Down Expand Up @@ -311,7 +311,7 @@ static HintError generatePathHintLocations(World& world, std::list<Hint>& hints)
for (auto& goalLocation : world.goalLocations)
{
shufflePool(goalLocation->pathLocations);
// Initially we want to pull path hints from race mode dungeons before pulling from Ganondorf
// Initially we want to pull path hints from required dungeons before pulling from Ganondorf
if (goalLocation->getName() != "Ganon's Tower - Defeat Ganondorf")
{
goalLocations.push_back(goalLocation);
Expand All @@ -321,15 +321,15 @@ static HintError generatePathHintLocations(World& world, std::list<Hint>& hints)
bool addedGanonPathLocation = false;
for (uint8_t i = 0; i < world.getSettings().path_hints; i++)
{
// Try to get at least one hint for each race mode dungeon first
// Try to get at least one hint for each required dungeon first
Location* goalLocation = nullptr;
if (i < goalLocations.size())
{
goalLocation = goalLocations[i];
}
else
{
// Once we've pulled from all race mode dungeons, then add Ganondorf to the list
// Once we've pulled from all required dungeons, then add Ganondorf to the list
// and choose randomly
if (i == goalLocations.size() && !addedGanonPathLocation)
{
Expand Down Expand Up @@ -650,7 +650,7 @@ static HintError generateLocationHintLocations(World& world, std::list<Hint>& hi
{
if (location->progression &&
!location->hasBeenHinted &&
!location->isRaceModeLocation &&
!location->isBossLocation &&
location->hintPriority == "Sometimes" &&
!(world.getSettings().ho_ho_triforce_hints && location->currentItem.isTriforceShard()))
{
Expand Down
2 changes: 1 addition & 1 deletion logic/Location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ bool Location::currentItemCanBeBarren() const
// if it's a required race mode location
bool Location::isBarrenAsChainLocation() const
{
return !progression || (currentItem.canBeInBarrenRegion() && !isRequiredRaceModeLocation);
return !progression || (currentItem.canBeInBarrenRegion() && !isRequiredBossLocation);
}

std::u16string Location::generateImportanceText(const std::string& language)
Expand Down
10 changes: 5 additions & 5 deletions logic/Location.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ class Location
std::unordered_map<std::string, std::string> names = {};
std::unordered_set<LocationCategory> categories;
bool progression;
bool isRaceModeLocation;
bool isRequiredRaceModeLocation;
bool isBossLocation;
bool isRequiredBossLocation;
uint8_t stageId = 0;
bool plandomized;
bool hasBeenHinted;
Expand Down Expand Up @@ -100,7 +100,7 @@ class Location
// Message Label if this is a hint location
std::string messageLabel;

// goal names if this is a race mode location (one for each language)
// goal names if this is a boss location (one for each language)
std::unordered_map<std::string, std::string> goalNames = {};

// Tracker properties
Expand All @@ -112,8 +112,8 @@ class Location
names({}),
categories({LocationCategory::INVALID}),
progression(false),
isRaceModeLocation(false),
isRequiredRaceModeLocation(false),
isBossLocation(false),
isRequiredBossLocation(false),
stageId(0),
plandomized(false),
hasBeenHinted(false),
Expand Down
Loading
Loading