Improvement: Additional Spoiler Hint Info (#2727)

* First attempt at tracking hinted locations. Not looking promising for more than a hint tracker.

* Spoiler log now generates with human-readable hinted area, type, and item for gossip stone hints.

* Hints now only output location if their hint text specifically states the check. Any overworld or dungeon region hint no longer does.

* Expanded gossip stone hints to include item, hinted location and area, and `RandomizerGet` names. Currently only English names.

Part of this required changing `HintType` into a `typedef enum` instead of an `enum class` to allow usage in a .c filespace, and consolidating types from `randomizer_check_objects.h` to `randomizerTypes.h`.

* Beginning of alter rewards information.

* Step 2 of altar reward info.

* Altar reward info (location and area) now writes to and reads from the spoiler log. Added a few entries to `SpoilerfileAreaNameToEnum` to accommodate area name variants from region hint names.

* Streamlined altar data (only needed location name for `RandomizerCheck` translation. `RandomizerCheck` provides access to `RandomizerCheckArea` via `RandomizerCheckObject`).

Added Light Arrows, Greg, and Dampe's Hookshot hint locations to write and read of the spoiler.

* Fix attempts to lookup values in `hintTypeNames` as array, now uses map's `find()`.
This commit is contained in:
Malkierian 2023-06-02 09:10:25 -07:00 committed by GitHub
parent f976003563
commit c9bcd64beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 356 additions and 188 deletions

View File

@ -155,6 +155,10 @@ typedef struct {
typedef struct { typedef struct {
RandomizerCheck check; RandomizerCheck check;
RandomizerCheck hintedCheck;
RandomizerGet rGet;
RandomizerCheckArea area;
HintType type;
char hintText[200]; char hintText[200];
} HintLocationRando; } HintLocationRando;
@ -298,6 +302,7 @@ typedef struct {
/* */ EntranceOverride entranceOverrides[ENTRANCE_OVERRIDES_MAX_COUNT]; /* */ EntranceOverride entranceOverrides[ENTRANCE_OVERRIDES_MAX_COUNT];
/* */ char childAltarText[250]; /* */ char childAltarText[250];
/* */ char adultAltarText[750]; /* */ char adultAltarText[750];
/* */ RandomizerCheck rewardCheck[9];
/* */ char ganonHintText[150]; /* */ char ganonHintText[150];
/* */ char gregHintText[250]; /* */ char gregHintText[250];
/* */ char ganonText[250]; /* */ char ganonText[250];
@ -308,6 +313,9 @@ typedef struct {
/* */ char warpRequiemText[100]; /* */ char warpRequiemText[100];
/* */ char warpNocturneText[100]; /* */ char warpNocturneText[100];
/* */ char warpPreludeText[100]; /* */ char warpPreludeText[100];
/* */ RandomizerCheck ganonHintCheck;
/* */ RandomizerCheck gregCheck;
/* */ RandomizerCheck dampeCheck;
/* */ u8 seedIcons[5]; /* */ u8 seedIcons[5];
/* */ u16 randomizerInf[9]; /* */ u16 randomizerInf[9];
/* */ u16 adultTradeItems; /* */ u16 adultTradeItems;

View File

@ -20,6 +20,22 @@ using namespace Logic;
using namespace Settings; using namespace Settings;
using namespace Trial; using namespace Trial;
std::unordered_map<HintType, std::string> hintTypeNames = {
{ HINT_TYPE_TRIAL, "Trial" },
{ HINT_TYPE_ALWAYS, "Always" },
{ HINT_TYPE_WOTH, "WotH" },
{ HINT_TYPE_BARREN, "Barren" },
{ HINT_TYPE_ENTRANCE, "Entrance" },
{ HINT_TYPE_SOMETIMES, "Sometimes" },
{ HINT_TYPE_RANDOM, "Random"},
{ HINT_TYPE_ITEM, "Item" },
{ HINT_TYPE_SONG, "Song" },
{ HINT_TYPE_OVERWORLD, "Overworld" },
{ HINT_TYPE_DUNGEON, "Dungeon" },
{ HINT_TYPE_JUNK, "Junk" },
{ HINT_TYPE_NAMED_ITEM, "NamedItem" },
};
constexpr std::array<HintSetting, 4> hintSettingTable{{ constexpr std::array<HintSetting, 4> hintSettingTable{{
// Useless hints // Useless hints
{ {
@ -27,19 +43,19 @@ constexpr std::array<HintSetting, 4> hintSettingTable{{
.dungeonsBarrenLimit = 1, .dungeonsBarrenLimit = 1,
.namedItemsRequired = false, .namedItemsRequired = false,
.distTable = {{ .distTable = {{
{.type = HintType::Trial, .order = 1, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_TRIAL, .order = 1, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Always, .order = 2, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_ALWAYS, .order = 2, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Woth, .order = 3, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_WOTH, .order = 3, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Barren, .order = 4, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_BARREN, .order = 4, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Entrance, .order = 5, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_ENTRANCE, .order = 5, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Sometimes, .order = 6, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_SOMETIMES, .order = 6, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Random, .order = 7, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_RANDOM, .order = 7, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Item, .order = 8, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_ITEM, .order = 8, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Song, .order = 9, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_SONG, .order = 9, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Overworld, .order = 10, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_OVERWORLD, .order = 10, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Dungeon, .order = 11, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_DUNGEON, .order = 11, .weight = 0, .fixed = 0, .copies = 0},
{.type = HintType::Junk, .order = 12, .weight = 99, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_JUNK, .order = 12, .weight = 99, .fixed = 0, .copies = 0},
{.type = HintType::NamedItem, .order = 13, .weight = 0, .fixed = 0, .copies = 0}, {.type = HINT_TYPE_NAMED_ITEM, .order = 13, .weight = 0, .fixed = 0, .copies = 0},
}}, }},
}, },
@ -49,19 +65,19 @@ constexpr std::array<HintSetting, 4> hintSettingTable{{
.dungeonsBarrenLimit = 1, .dungeonsBarrenLimit = 1,
.namedItemsRequired = true, .namedItemsRequired = true,
.distTable = {{ .distTable = {{
{.type = HintType::Trial, .order = 1, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_TRIAL, .order = 1, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Always, .order = 2, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ALWAYS, .order = 2, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Woth, .order = 3, .weight = 7, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_WOTH, .order = 3, .weight = 7, .fixed = 0, .copies = 1},
{.type = HintType::Barren, .order = 4, .weight = 4, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_BARREN, .order = 4, .weight = 4, .fixed = 0, .copies = 1},
{.type = HintType::Entrance, .order = 5, .weight = 6, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ENTRANCE, .order = 5, .weight = 6, .fixed = 0, .copies = 1},
{.type = HintType::Sometimes, .order = 6, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SOMETIMES, .order = 6, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Random, .order = 7, .weight = 12, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_RANDOM, .order = 7, .weight = 12, .fixed = 0, .copies = 1},
{.type = HintType::Item, .order = 8, .weight = 10, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ITEM, .order = 8, .weight = 10, .fixed = 0, .copies = 1},
{.type = HintType::Song, .order = 9, .weight = 2, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SONG, .order = 9, .weight = 2, .fixed = 0, .copies = 1},
{.type = HintType::Overworld, .order = 10, .weight = 4, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_OVERWORLD, .order = 10, .weight = 4, .fixed = 0, .copies = 1},
{.type = HintType::Dungeon, .order = 11, .weight = 3, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_DUNGEON, .order = 11, .weight = 3, .fixed = 0, .copies = 1},
{.type = HintType::Junk, .order = 12, .weight = 6, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_JUNK, .order = 12, .weight = 6, .fixed = 0, .copies = 1},
{.type = HintType::NamedItem, .order = 13, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_NAMED_ITEM, .order = 13, .weight = 0, .fixed = 0, .copies = 1},
}}, }},
}, },
@ -71,19 +87,19 @@ constexpr std::array<HintSetting, 4> hintSettingTable{{
.dungeonsBarrenLimit = 1, .dungeonsBarrenLimit = 1,
.namedItemsRequired = true, .namedItemsRequired = true,
.distTable = {{ .distTable = {{
{.type = HintType::Trial, .order = 1, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_TRIAL, .order = 1, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Always, .order = 2, .weight = 0, .fixed = 0, .copies = 2}, {.type = HINT_TYPE_ALWAYS, .order = 2, .weight = 0, .fixed = 0, .copies = 2},
{.type = HintType::Woth, .order = 3, .weight = 12, .fixed = 0, .copies = 2}, {.type = HINT_TYPE_WOTH, .order = 3, .weight = 12, .fixed = 0, .copies = 2},
{.type = HintType::Barren, .order = 4, .weight = 12, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_BARREN, .order = 4, .weight = 12, .fixed = 0, .copies = 1},
{.type = HintType::Entrance, .order = 5, .weight = 4, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ENTRANCE, .order = 5, .weight = 4, .fixed = 0, .copies = 1},
{.type = HintType::Sometimes, .order = 6, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SOMETIMES, .order = 6, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Random, .order = 7, .weight = 8, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_RANDOM, .order = 7, .weight = 8, .fixed = 0, .copies = 1},
{.type = HintType::Item, .order = 8, .weight = 8, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ITEM, .order = 8, .weight = 8, .fixed = 0, .copies = 1},
{.type = HintType::Song, .order = 9, .weight = 4, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SONG, .order = 9, .weight = 4, .fixed = 0, .copies = 1},
{.type = HintType::Overworld, .order = 10, .weight = 6, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_OVERWORLD, .order = 10, .weight = 6, .fixed = 0, .copies = 1},
{.type = HintType::Dungeon, .order = 11, .weight = 6, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_DUNGEON, .order = 11, .weight = 6, .fixed = 0, .copies = 1},
{.type = HintType::Junk, .order = 12, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_JUNK, .order = 12, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::NamedItem, .order = 13, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_NAMED_ITEM, .order = 13, .weight = 0, .fixed = 0, .copies = 1},
}}, }},
}, },
@ -93,19 +109,19 @@ constexpr std::array<HintSetting, 4> hintSettingTable{{
.dungeonsBarrenLimit = 40, .dungeonsBarrenLimit = 40,
.namedItemsRequired = true, .namedItemsRequired = true,
.distTable = {{ .distTable = {{
{.type = HintType::Trial, .order = 1, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_TRIAL, .order = 1, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Always, .order = 2, .weight = 0, .fixed = 0, .copies = 2}, {.type = HINT_TYPE_ALWAYS, .order = 2, .weight = 0, .fixed = 0, .copies = 2},
{.type = HintType::Woth, .order = 3, .weight = 15, .fixed = 0, .copies = 2}, {.type = HINT_TYPE_WOTH, .order = 3, .weight = 15, .fixed = 0, .copies = 2},
{.type = HintType::Barren, .order = 4, .weight = 15, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_BARREN, .order = 4, .weight = 15, .fixed = 0, .copies = 1},
{.type = HintType::Entrance, .order = 5, .weight = 10, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ENTRANCE, .order = 5, .weight = 10, .fixed = 0, .copies = 1},
{.type = HintType::Sometimes, .order = 6, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SOMETIMES, .order = 6, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Random, .order = 7, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_RANDOM, .order = 7, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::Item, .order = 8, .weight = 5, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_ITEM, .order = 8, .weight = 5, .fixed = 0, .copies = 1},
{.type = HintType::Song, .order = 9, .weight = 2, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_SONG, .order = 9, .weight = 2, .fixed = 0, .copies = 1},
{.type = HintType::Overworld, .order = 10, .weight = 7, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_OVERWORLD, .order = 10, .weight = 7, .fixed = 0, .copies = 1},
{.type = HintType::Dungeon, .order = 11, .weight = 7, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_DUNGEON, .order = 11, .weight = 7, .fixed = 0, .copies = 1},
{.type = HintType::Junk, .order = 12, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_JUNK, .order = 12, .weight = 0, .fixed = 0, .copies = 1},
{.type = HintType::NamedItem, .order = 13, .weight = 0, .fixed = 0, .copies = 1}, {.type = HINT_TYPE_NAMED_ITEM, .order = 13, .weight = 0, .fixed = 0, .copies = 1},
}}, }},
}, },
}}; }};
@ -125,6 +141,9 @@ Text warpRequiemText;
Text warpNocturneText; Text warpNocturneText;
Text warpPreludeText; Text warpPreludeText;
std::string ganonHintLoc;
std::string dampeHintLoc;
Text& GetChildAltarText() { Text& GetChildAltarText() {
return childAltarText; return childAltarText;
} }
@ -173,7 +192,15 @@ Text& GetWarpPreludeText() {
return warpPreludeText; return warpPreludeText;
} }
static Area* GetHintRegion(const uint32_t area) { std::string GetGanonHintLoc() {
return ganonHintLoc;
}
std::string GetDampeHintLoc() {
return dampeHintLoc;
}
Area* GetHintRegion(const uint32_t area) {
std::vector<uint32_t> alreadyChecked = {}; std::vector<uint32_t> alreadyChecked = {};
std::vector<uint32_t> spotQueue = {area}; std::vector<uint32_t> spotQueue = {area};
@ -232,10 +259,13 @@ static std::vector<uint32_t> GetAccessibleGossipStones(const uint32_t hintedLoca
return accessibleGossipStones; return accessibleGossipStones;
} }
static void AddHint(Text hint, const uint32_t gossipStone, const std::vector<uint8_t>& colors = {}) { static void AddHint(Text hint, const uint32_t gossipStone, const std::vector<uint8_t>& colors = {}, HintType hintType = HINT_TYPE_ITEM, const uint32_t hintedLocation = NONE) {
//save hints as dummy items for writing to the spoiler log //save hints as dummy items for writing to the spoiler log
NewItem(gossipStone, Item{RG_HINT, hint, ITEMTYPE_EVENT, GI_RUPEE_BLUE_LOSE, false, &noVariable, NONE}); NewItem(gossipStone, Item{RG_HINT, hint, ITEMTYPE_EVENT, GI_RUPEE_BLUE_LOSE, false, &noVariable, NONE});
Location(gossipStone)->SetPlacedItem(gossipStone); Location(gossipStone)->SetPlacedItem(gossipStone);
Location(gossipStone)->SetHintedLocation(hintedLocation);
Location(gossipStone)->SetHintType(hintType);
Location(gossipStone)->SetHintedRegion(GetHintRegion(Location(hintedLocation)->GetParentRegionKey())->GetHint().GetText().GetEnglish());
//create the in game message //create the in game message
// uint32_t messageId = 0x400 + Location(gossipStone)->GetFlag(); // uint32_t messageId = 0x400 + Location(gossipStone)->GetFlag();
@ -280,7 +310,7 @@ static void CreateLocationHint(const std::vector<uint32_t>& possibleHintLocation
SPDLOG_DEBUG(finalHint.english); SPDLOG_DEBUG(finalHint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(finalHint, gossipStone, {QM_GREEN, QM_RED}); AddHint(finalHint, gossipStone, {QM_GREEN, QM_RED}, HINT_TYPE_ITEM, hintedLocation);
} }
static void CreateWothHint(uint8_t* remainingDungeonWothHints) { static void CreateWothHint(uint8_t* remainingDungeonWothHints) {
@ -332,7 +362,7 @@ static void CreateWothHint(uint8_t* remainingDungeonWothHints) {
SPDLOG_DEBUG("\tMessage: "); SPDLOG_DEBUG("\tMessage: ");
SPDLOG_DEBUG(finalWothHint.english); SPDLOG_DEBUG(finalWothHint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(finalWothHint, gossipStone, { QM_LBLUE }); AddHint(finalWothHint, gossipStone, { QM_LBLUE }, HINT_TYPE_WOTH, hintedLocation);
} }
static void CreateBarrenHint(uint8_t* remainingDungeonBarrenHints, std::vector<uint32_t>& barrenLocations) { static void CreateBarrenHint(uint8_t* remainingDungeonBarrenHints, std::vector<uint32_t>& barrenLocations) {
@ -376,7 +406,7 @@ static void CreateBarrenHint(uint8_t* remainingDungeonBarrenHints, std::vector<u
SPDLOG_DEBUG("\tMessage: "); SPDLOG_DEBUG("\tMessage: ");
SPDLOG_DEBUG(finalBarrenHint.english); SPDLOG_DEBUG(finalBarrenHint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(finalBarrenHint, gossipStone, { QM_PINK }); AddHint(finalBarrenHint, gossipStone, { QM_PINK }, HINT_TYPE_BARREN, hintedLocation);
// get rid of all other locations in this same barren region // get rid of all other locations in this same barren region
barrenLocations = FilterFromPool(barrenLocations, [hintedLocation](uint32_t loc) { barrenLocations = FilterFromPool(barrenLocations, [hintedLocation](uint32_t loc) {
@ -422,13 +452,13 @@ static void CreateRandomLocationHint(const bool goodItem = false) {
SPDLOG_DEBUG("\tMessage: "); SPDLOG_DEBUG("\tMessage: ");
SPDLOG_DEBUG(finalHint.english); SPDLOG_DEBUG(finalHint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(finalHint, gossipStone, {QM_GREEN, QM_RED}); AddHint(finalHint, gossipStone, {QM_GREEN, QM_RED}, HINT_TYPE_NAMED_ITEM, hintedLocation);
} else { } else {
Text finalHint = Hint(PREFIX).GetText()+"#"+itemText+"# "+Hint(CAN_BE_FOUND_AT).GetText()+" #"+locationText+"#."; Text finalHint = Hint(PREFIX).GetText()+"#"+itemText+"# "+Hint(CAN_BE_FOUND_AT).GetText()+" #"+locationText+"#.";
SPDLOG_DEBUG("\tMessage: "); SPDLOG_DEBUG("\tMessage: ");
SPDLOG_DEBUG(finalHint.english); SPDLOG_DEBUG(finalHint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(finalHint, gossipStone, {QM_RED, QM_GREEN}); AddHint(finalHint, gossipStone, { QM_RED, QM_GREEN }, HINT_TYPE_NAMED_ITEM, hintedLocation);
} }
} }
@ -452,7 +482,7 @@ static void CreateJunkHint() {
SPDLOG_DEBUG(hint.english); SPDLOG_DEBUG(hint.english);
SPDLOG_DEBUG("\n\n"); SPDLOG_DEBUG("\n\n");
AddHint(hint, gossipStone, {QM_PINK}); AddHint(hint, gossipStone, { QM_PINK }, HINT_TYPE_JUNK);
} }
static std::vector<uint32_t> CalculateBarrenRegions() { static std::vector<uint32_t> CalculateBarrenRegions() {
@ -495,7 +525,7 @@ static void CreateTrialHints() {
//make hint //make hint
auto hint = Hint(PREFIX).GetText() + Hint(SIX_TRIALS).GetText(); auto hint = Hint(PREFIX).GetText() + Hint(SIX_TRIALS).GetText();
AddHint(hint, gossipStone, {QM_PINK}); AddHint(hint, gossipStone, { QM_PINK }, HINT_TYPE_TRIAL);
//zero trials //zero trials
} else if (RandomGanonsTrials && GanonsTrialsCount.Is(0)) { } else if (RandomGanonsTrials && GanonsTrialsCount.Is(0)) {
@ -506,7 +536,7 @@ static void CreateTrialHints() {
//make hint //make hint
auto hint = Hint(PREFIX).GetText() + Hint(ZERO_TRIALS).GetText(); auto hint = Hint(PREFIX).GetText() + Hint(ZERO_TRIALS).GetText();
AddHint(hint, gossipStone, {QM_YELLOW}); AddHint(hint, gossipStone, { QM_YELLOW }, HINT_TYPE_TRIAL);
//4 or 5 required trials //4 or 5 required trials
} else if (GanonsTrialsCount.Is(5) || GanonsTrialsCount.Is(4)) { } else if (GanonsTrialsCount.Is(5) || GanonsTrialsCount.Is(4)) {
@ -524,7 +554,7 @@ static void CreateTrialHints() {
//make hint //make hint
auto hint = Hint(PREFIX).GetText()+"#"+trial->GetName()+"#"+Hint(FOUR_TO_FIVE_TRIALS).GetText(); auto hint = Hint(PREFIX).GetText()+"#"+trial->GetName()+"#"+Hint(FOUR_TO_FIVE_TRIALS).GetText();
AddHint(hint, gossipStone, {QM_YELLOW}); AddHint(hint, gossipStone, { QM_YELLOW }, HINT_TYPE_TRIAL);
} }
//1 to 3 trials //1 to 3 trials
} else if (GanonsTrialsCount.Value<uint8_t>() >= 1 && GanonsTrialsCount.Value<uint8_t>() <= 3) { } else if (GanonsTrialsCount.Value<uint8_t>() >= 1 && GanonsTrialsCount.Value<uint8_t>() <= 3) {
@ -541,7 +571,7 @@ static void CreateTrialHints() {
//make hint //make hint
auto hint = Hint(PREFIX).GetText()+"#"+trial->GetName()+"#"+Hint(ONE_TO_THREE_TRIALS).GetText(); auto hint = Hint(PREFIX).GetText()+"#"+trial->GetName()+"#"+Hint(ONE_TO_THREE_TRIALS).GetText();
AddHint(hint, gossipStone, {QM_PINK}); AddHint(hint, gossipStone, { QM_PINK }, HINT_TYPE_TRIAL);
} }
} }
} }
@ -556,10 +586,13 @@ void CreateGanonText() {
auto lightArrowLocation = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == LIGHT_ARROWS;}); auto lightArrowLocation = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == LIGHT_ARROWS;});
//If there is no light arrow location, it was in the player's inventory at the start //If there is no light arrow location, it was in the player's inventory at the start
auto hint = Hint(LIGHT_ARROW_LOCATION_HINT);
if (lightArrowLocation.empty()) { if (lightArrowLocation.empty()) {
ganonHintText = Hint(LIGHT_ARROW_LOCATION_HINT).GetText()+Hint(YOUR_POCKET).GetText(); ganonHintText = hint.GetText()+Hint(YOUR_POCKET).GetText();
ganonHintLoc = "Link's Pocket";
} else { } else {
ganonHintText = Hint(LIGHT_ARROW_LOCATION_HINT).GetText()+GetHintRegion(Location(lightArrowLocation[0])->GetParentRegionKey())->GetHint().GetText(); ganonHintText = hint.GetText()+GetHintRegion(Location(lightArrowLocation[0])->GetParentRegionKey())->GetHint().GetText();
ganonHintLoc = Location(lightArrowLocation[0])->GetName();
} }
ganonHintText = ganonHintText + "!"; ganonHintText = ganonHintText + "!";
@ -759,6 +792,7 @@ void CreateMerchantsHints() {
void CreateDampesDiaryText() { void CreateDampesDiaryText() {
if (!DampeHintText) { if (!DampeHintText) {
dampesText = Text(); dampesText = Text();
dampeHintLoc = "";
return; return;
} }
@ -778,6 +812,7 @@ void CreateDampesDiaryText() {
}; };
dampesText = temp1 + area + temp2; dampesText = temp1 + area + temp2;
dampeHintLoc = Location(location)->GetName();
} }
void CreateGregRupeeHint() { void CreateGregRupeeHint() {
@ -861,7 +896,7 @@ void CreateAllHints() {
uint8_t remainingDungeonBarrenHints = hintSetting.dungeonsBarrenLimit; uint8_t remainingDungeonBarrenHints = hintSetting.dungeonsBarrenLimit;
// Add 'always' location hints // Add 'always' location hints
if (hintSetting.distTable[static_cast<int>(HintType::Always)].copies > 0) { if (hintSetting.distTable[static_cast<int>(HINT_TYPE_ALWAYS)].copies > 0) {
// Only filter locations that had a random item placed at them (e.g. don't get cow locations if shuffle cows is off) // Only filter locations that had a random item placed at them (e.g. don't get cow locations if shuffle cows is off)
auto alwaysHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){ auto alwaysHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){
return ((Location(loc)->GetHint().GetType() == HintCategory::Always) || return ((Location(loc)->GetHint().GetType() == HintCategory::Always) ||
@ -883,7 +918,7 @@ void CreateAllHints() {
} }
//Add 'trial' location hints //Add 'trial' location hints
if (hintSetting.distTable[static_cast<int>(HintType::Trial)].copies > 0) { if (hintSetting.distTable[static_cast<int>(HINT_TYPE_TRIAL)].copies > 0) {
CreateTrialHints(); CreateTrialHints();
} }
@ -945,22 +980,6 @@ void CreateAllHints() {
} }
} }
std::array<std::string, 13> hintTypeNames = {
"Trial",
"Always",
"WotH",
"Barren",
"Entrance",
"Sometimes",
"Random",
"Item",
"Song",
"Overworld",
"Dungeon",
"Junk",
"NamedItem",
};
//while there are still gossip stones remaining //while there are still gossip stones remaining
while (FilterFromPool(gossipStoneLocations, [](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == NONE;}).size() != 0) { while (FilterFromPool(gossipStoneLocations, [](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == NONE;}).size() != 0) {
//TODO: fixed hint types //TODO: fixed hint types
@ -973,39 +992,39 @@ void CreateAllHints() {
HintType type = RandomElement(remainingHintTypes, true); HintType type = RandomElement(remainingHintTypes, true);
SPDLOG_DEBUG("Attempting to make hint of type: "); SPDLOG_DEBUG("Attempting to make hint of type: ");
SPDLOG_DEBUG(hintTypeNames[static_cast<int>(type)]); SPDLOG_DEBUG(hintTypeNames.find(type)->second);
SPDLOG_DEBUG("\n"); SPDLOG_DEBUG("\n");
//create the appropriate hint for the type //create the appropriate hint for the type
if (type == HintType::Woth) { if (type == HINT_TYPE_WOTH) {
CreateWothHint(&remainingDungeonWothHints); CreateWothHint(&remainingDungeonWothHints);
} else if (type == HintType::Barren) { } else if (type == HINT_TYPE_BARREN) {
CreateBarrenHint(&remainingDungeonBarrenHints, barrenLocations); CreateBarrenHint(&remainingDungeonBarrenHints, barrenLocations);
} else if (type == HintType::Sometimes){ } else if (type == HINT_TYPE_SOMETIMES){
std::vector<uint32_t> sometimesHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->GetHint().GetType() == HintCategory::Sometimes && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());}); std::vector<uint32_t> sometimesHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->GetHint().GetType() == HintCategory::Sometimes && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());});
CreateLocationHint(sometimesHintLocations); CreateLocationHint(sometimesHintLocations);
} else if (type == HintType::Random) { } else if (type == HINT_TYPE_RANDOM) {
CreateRandomLocationHint(); CreateRandomLocationHint();
} else if (type == HintType::Item) { } else if (type == HINT_TYPE_ITEM) {
CreateGoodItemHint(); CreateGoodItemHint();
} else if (type == HintType::Song){ } else if (type == HINT_TYPE_SONG){
std::vector<uint32_t> songHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsCategory(Category::cSong) && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());}); std::vector<uint32_t> songHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsCategory(Category::cSong) && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());});
CreateLocationHint(songHintLocations); CreateLocationHint(songHintLocations);
} else if (type == HintType::Overworld){ } else if (type == HINT_TYPE_OVERWORLD){
std::vector<uint32_t> overworldHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsOverworld() && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());}); std::vector<uint32_t> overworldHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsOverworld() && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());});
CreateLocationHint(overworldHintLocations); CreateLocationHint(overworldHintLocations);
} else if (type == HintType::Dungeon){ } else if (type == HINT_TYPE_DUNGEON){
std::vector<uint32_t> dungeonHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsDungeon() && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());}); std::vector<uint32_t> dungeonHintLocations = FilterFromPool(allLocations, [](const uint32_t loc){return Location(loc)->IsDungeon() && Location(loc)->IsHintable() && !(Location(loc)->IsHintedAt());});
CreateLocationHint(dungeonHintLocations); CreateLocationHint(dungeonHintLocations);
} else if (type == HintType::Junk) { } else if (type == HINT_TYPE_JUNK) {
CreateJunkHint(); CreateJunkHint();
} }
} }

View File

@ -10,23 +10,6 @@
#include "settings.hpp" #include "settings.hpp"
#include <functional> #include <functional>
enum class HintType {
Trial,
Always,
Woth, //Way of the Hero
Barren,
Entrance,
Sometimes,
Random,
Item,
Song,
Overworld,
Dungeon,
Junk,
NamedItem,
MaxCount,
};
struct HintDistributionSetting { struct HintDistributionSetting {
HintType type; HintType type;
uint8_t order; uint8_t order;
@ -36,7 +19,7 @@ struct HintDistributionSetting {
}; };
struct HintSetting { struct HintSetting {
using DistributionTable = std::array<HintDistributionSetting, static_cast<int>(HintType::MaxCount)>; using DistributionTable = std::array<HintDistributionSetting, static_cast<int>(HINT_TYPE_MAX)>;
uint8_t dungeonsWothLimit; uint8_t dungeonsWothLimit;
uint8_t dungeonsBarrenLimit; uint8_t dungeonsBarrenLimit;
@ -239,3 +222,6 @@ Text& GetWarpSerenadeText();
Text& GetWarpRequiemText(); Text& GetWarpRequiemText();
Text& GetWarpNocturneText(); Text& GetWarpNocturneText();
Text& GetWarpPreludeText(); Text& GetWarpPreludeText();
std::string GetDampeHintLoc();
std::string GetGanonHintLoc();

View File

@ -347,6 +347,30 @@ public:
return isHintable; return isHintable;
} }
void SetHintedLocation(uint32_t location) {
hintedLocation = location;
}
uint32_t GetHintedLocation() {
return hintedLocation;
}
void SetHintType(HintType type) {
hintType = type;
}
HintType GetHintType() {
return hintType;
}
void SetHintedRegion (std::string region) {
hintedRegion = region;
}
std::string GetHintedRegion() {
return hintedRegion;
}
void SetAsHintable() { void SetAsHintable() {
isHintable = true; isHintable = true;
} }
@ -465,6 +489,9 @@ private:
std::vector<Category> categories; std::vector<Category> categories;
bool addedToPool = false; bool addedToPool = false;
uint32_t placedItem = NONE; uint32_t placedItem = NONE;
uint32_t hintedLocation = NONE;
HintType hintType;
std::string hintedRegion;
uint32_t delayedItem = NONE; uint32_t delayedItem = NONE;
Option excludedOption = Option::Bool(name, {"Include", "Exclude"}, {"", ""}); Option excludedOption = Option::Bool(name, {"Include", "Exclude"}, {"", ""});
uint16_t price = 0; uint16_t price = 0;

View File

@ -11,6 +11,8 @@
#include "utils.hpp" #include "utils.hpp"
#include "shops.hpp" #include "shops.hpp"
#include "hints.hpp" #include "hints.hpp"
#include "pool_functions.hpp"
#include "soh/Enhancements/randomizer/randomizer_check_objects.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <cstdio> #include <cstdio>
@ -32,6 +34,11 @@
using json = nlohmann::json; using json = nlohmann::json;
json jsonData; json jsonData;
std::map<HintKey, ItemLocation*> hintedLocations;
extern std::unordered_map<HintType, std::string> hintTypeNames;
extern std::array<std::string, 17> hintCategoryNames;
extern Area* GetHintRegion(uint32_t);
namespace { namespace {
std::string placementtxt; std::string placementtxt;
@ -645,6 +652,10 @@ std::string AutoFormatHintTextString(std::string unformattedHintTextString) {
return textStr; return textStr;
} }
ItemLocation* GetItemLocation(uint32_t item) {
return Location(FilterFromPool(allLocations, [item](const uint32_t loc){return Location(loc)->GetPlaceduint32_t() == item;})[0]);
}
// Writes the hints to the spoiler log, if they are enabled. // Writes the hints to the spoiler log, if they are enabled.
static void WriteHints(int language) { static void WriteHints(int language) {
std::string unformattedGanonText; std::string unformattedGanonText;
@ -665,8 +676,8 @@ static void WriteHints(int language) {
jsonData["warpRequiemText"] = GetWarpRequiemText().GetEnglish(); jsonData["warpRequiemText"] = GetWarpRequiemText().GetEnglish();
jsonData["warpNocturneText"] = GetWarpNocturneText().GetEnglish(); jsonData["warpNocturneText"] = GetWarpNocturneText().GetEnglish();
jsonData["warpPreludeText"] = GetWarpPreludeText().GetEnglish(); jsonData["warpPreludeText"] = GetWarpPreludeText().GetEnglish();
jsonData["childAltarText"] = GetChildAltarText().GetEnglish(); jsonData["childAltar"]["hintText"] = GetChildAltarText().GetEnglish();
jsonData["adultAltarText"] = GetAdultAltarText().GetEnglish(); jsonData["adultAltar"]["hintText"] = GetAdultAltarText().GetEnglish();
break; break;
case 2: case 2:
unformattedGanonText = GetGanonText().GetFrench(); unformattedGanonText = GetGanonText().GetFrench();
@ -679,11 +690,36 @@ static void WriteHints(int language) {
jsonData["warpRequiemText"] = GetWarpRequiemText().GetFrench(); jsonData["warpRequiemText"] = GetWarpRequiemText().GetFrench();
jsonData["warpNocturneText"] = GetWarpNocturneText().GetFrench(); jsonData["warpNocturneText"] = GetWarpNocturneText().GetFrench();
jsonData["warpPreludeText"] = GetWarpPreludeText().GetFrench(); jsonData["warpPreludeText"] = GetWarpPreludeText().GetFrench();
jsonData["childAltarText"] = GetChildAltarText().GetFrench(); jsonData["childAltar"]["hintText"] = GetChildAltarText().GetFrench();
jsonData["adultAltarText"] = GetAdultAltarText().GetFrench(); jsonData["adultAltar"]["hintText"] = GetAdultAltarText().GetFrench();
break; break;
} }
ItemLocation* emeraldLoc = GetItemLocation(KOKIRI_EMERALD);
ItemLocation* rubyLoc = GetItemLocation(GORON_RUBY);
ItemLocation* sapphireLoc = GetItemLocation(ZORA_SAPPHIRE);
std::string emeraldArea;
std::string erubyArea;
std::string sapphireArea;
jsonData["childAltar"]["rewards"]["emeraldLoc"] = emeraldLoc->GetName();
jsonData["childAltar"]["rewards"]["rubyLoc"] = rubyLoc->GetName();
jsonData["childAltar"]["rewards"]["sapphireLoc"] = sapphireLoc->GetName();
ItemLocation* forestMedallionLoc = GetItemLocation(FOREST_MEDALLION);
ItemLocation* fireMedallionLoc = GetItemLocation(FIRE_MEDALLION);
ItemLocation* waterMedallionLoc = GetItemLocation(WATER_MEDALLION);
ItemLocation* shadowMedallionLoc = GetItemLocation(SHADOW_MEDALLION);
ItemLocation* spiritMedallionLoc = GetItemLocation(SPIRIT_MEDALLION);
ItemLocation* lightMedallionLoc = GetItemLocation(LIGHT_MEDALLION);
jsonData["adultAltar"]["rewards"]["forestMedallionLoc"] = forestMedallionLoc->GetName();
jsonData["adultAltar"]["rewards"]["fireMedallionLoc"] = fireMedallionLoc->GetName();
jsonData["adultAltar"]["rewards"]["waterMedallionLoc"] = waterMedallionLoc->GetName();
jsonData["adultAltar"]["rewards"]["shadowMedallionLoc"] = shadowMedallionLoc->GetName();
jsonData["adultAltar"]["rewards"]["spiritMedallionLoc"] = spiritMedallionLoc->GetName();
jsonData["adultAltar"]["rewards"]["lightMedallionLoc"] = lightMedallionLoc->GetName();
std::string ganonText = AutoFormatHintTextString(unformattedGanonText); std::string ganonText = AutoFormatHintTextString(unformattedGanonText);
std::string ganonHintText = AutoFormatHintTextString(unformattedGanonHintText); std::string ganonHintText = AutoFormatHintTextString(unformattedGanonHintText);
std::string dampesText = AutoFormatHintTextString(unformattedDampesText); std::string dampesText = AutoFormatHintTextString(unformattedDampesText);
@ -691,8 +727,11 @@ static void WriteHints(int language) {
jsonData["ganonText"] = ganonText; jsonData["ganonText"] = ganonText;
jsonData["ganonHintText"] = ganonHintText; jsonData["ganonHintText"] = ganonHintText;
jsonData["ganonHintLoc"] = GetGanonHintLoc();
jsonData["dampeText"] = dampesText; jsonData["dampeText"] = dampesText;
jsonData["dampeHintLoc"] = GetDampeHintLoc();
jsonData["gregText"] = gregText; jsonData["gregText"] = gregText;
jsonData["gregLoc"] = GetItemLocation(GREG_RUPEE)->GetName();
if (Settings::GossipStoneHints.Is(HINTS_NO_HINTS)) { if (Settings::GossipStoneHints.Is(HINTS_NO_HINTS)) {
return; return;
@ -700,6 +739,7 @@ static void WriteHints(int language) {
for (const uint32_t key : gossipStoneLocations) { for (const uint32_t key : gossipStoneLocations) {
ItemLocation* location = Location(key); ItemLocation* location = Location(key);
ItemLocation* hintedLocation = Location(location->GetHintedLocation());
std::string unformattedHintTextString; std::string unformattedHintTextString;
switch (language) { switch (language) {
case 0: case 0:
@ -711,8 +751,20 @@ static void WriteHints(int language) {
break; break;
} }
HintType hintType = location->GetHintType();
std::string textStr = AutoFormatHintTextString(unformattedHintTextString); std::string textStr = AutoFormatHintTextString(unformattedHintTextString);
jsonData["hints"][location->GetName()] = textStr; jsonData["hints"][location->GetName()]["hint"] = textStr;
jsonData["hints"][location->GetName()]["type"] = hintTypeNames.find(hintType)->second;
if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_NAMED_ITEM) {
jsonData["hints"][location->GetName()]["item"] = hintedLocation->GetPlacedItemName().GetEnglish();
if (hintType != HINT_TYPE_NAMED_ITEM) {
jsonData["hints"][location->GetName()]["location"] = hintedLocation->GetName();
}
}
if (hintType != HINT_TYPE_TRIAL && hintType != HINT_TYPE_JUNK) {
jsonData["hints"][location->GetName()]["area"] = location->GetHintedRegion();
}
} }
} }
@ -747,6 +799,9 @@ static void WriteAllLocations(int language) {
if (location->HasScrubsanityPrice() || location->HasShopsanityPrice()) { if (location->HasScrubsanityPrice() || location->HasShopsanityPrice()) {
jsonData["locations"][location->GetName()]["price"] = location->GetPrice(); jsonData["locations"][location->GetName()]["price"] = location->GetPrice();
} }
if (location->IsHintedAt()) {
hintedLocations.emplace(location->GetHintKey(), location);
}
if (location->GetPlacedItemKey() == ICE_TRAP) { if (location->GetPlacedItemKey() == ICE_TRAP) {
switch (language) { switch (language) {
@ -768,6 +823,16 @@ static void WriteAllLocations(int language) {
} }
} }
//static void WriteHintData(int language) {
// for (auto [hintKey, item_location] : hintedLocations) {
// ItemLocation *hint_location = Location(hintKey);
// jsonData["hints"][hint_location->GetName()] = { { "text", hint_location->GetPlacedItemName().GetEnglish() },
// { "item", item_location->GetPlacedItemName().GetEnglish() },
// { "itemLocation", item_location->GetName() },
// { "locationArea", item_location->GetParentRegionKey() } };
// }
//}
const char* SpoilerLog_Write(int language) { const char* SpoilerLog_Write(int language) {
auto spoilerLog = tinyxml2::XMLDocument(false); auto spoilerLog = tinyxml2::XMLDocument(false);
spoilerLog.InsertEndChild(spoilerLog.NewDeclaration()); spoilerLog.InsertEndChild(spoilerLog.NewDeclaration());
@ -806,6 +871,7 @@ const char* SpoilerLog_Write(int language) {
WriteHints(language); WriteHints(language);
WriteShuffledEntrances(); WriteShuffledEntrances();
WriteAllLocations(language); WriteAllLocations(language);
//WriteHintData(language);
if (!std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("Randomizer"))) { if (!std::filesystem::exists(LUS::Context::GetPathRelativeToAppDirectory("Randomizer"))) {
std::filesystem::create_directory(LUS::Context::GetPathRelativeToAppDirectory("Randomizer")); std::filesystem::create_directory(LUS::Context::GetPathRelativeToAppDirectory("Randomizer"));

View File

@ -31,11 +31,16 @@
extern "C" uint32_t ResourceMgr_IsGameMasterQuest(); extern "C" uint32_t ResourceMgr_IsGameMasterQuest();
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
extern std::map<RandomizerCheckArea, std::string> rcAreaNames;
extern std::unordered_map<HintType, std::string> hintTypeNames;
using json = nlohmann::json; using json = nlohmann::json;
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
std::unordered_map<std::string, RandomizerCheck> SpoilerfileCheckNameToEnum; std::unordered_map<std::string, RandomizerCheck> SpoilerfileCheckNameToEnum;
std::unordered_map<std::string, RandomizerGet> SpoilerfileGetNameToEnum; std::unordered_map<std::string, RandomizerGet> SpoilerfileGetNameToEnum;
std::unordered_map<std::string, RandomizerCheckArea> SpoilerfileAreaNameToEnum;
std::unordered_map<std::string, HintType> SpoilerfileHintTypeNameToEnum;
std::multimap<std::tuple<s16, s16, s32>, RandomizerCheckObject> checkFromActorMultimap; std::multimap<std::tuple<s16, s16, s32>, RandomizerCheckObject> checkFromActorMultimap;
std::set<RandomizerCheck> excludedLocations; std::set<RandomizerCheck> excludedLocations;
@ -123,6 +128,18 @@ Randomizer::Randomizer() {
item.GetName().french, item.GetName().french,
}; };
} }
for (auto area : rcAreaNames) {
SpoilerfileAreaNameToEnum[area.second] = area.first;
}
SpoilerfileAreaNameToEnum["Inside Ganon's Castle"] = RCAREA_GANONS_CASTLE;
SpoilerfileAreaNameToEnum["the Lost Woods"] = RCAREA_LOST_WOODS;
SpoilerfileAreaNameToEnum["the Market"] = RCAREA_MARKET;
SpoilerfileAreaNameToEnum["the Graveyard"] = RCAREA_GRAVEYARD;
SpoilerfileAreaNameToEnum["Haunted Wasteland"] = RCAREA_WASTELAND;
SpoilerfileAreaNameToEnum["outside Ganon's Castle"] = RCAREA_HYRULE_CASTLE;
for (auto [type, name] : hintTypeNames) {
SpoilerfileHintTypeNameToEnum[name] = type;
}
} }
Sprite* Randomizer::GetSeedTexture(uint8_t index) { Sprite* Randomizer::GetSeedTexture(uint8_t index) {
@ -1226,20 +1243,30 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
json spoilerFileJson; json spoilerFileJson;
spoilerFileStream >> spoilerFileJson; spoilerFileStream >> spoilerFileJson;
std::string childAltarJsonText = spoilerFileJson["childAltarText"].get<std::string>(); std::string childAltarJsonText = spoilerFileJson["childAltar"]["hintText"].get<std::string>();
std::string formattedChildAltarText = FormatJsonHintText(childAltarJsonText); std::string formattedChildAltarText = FormatJsonHintText(childAltarJsonText);
strncpy(gSaveContext.childAltarText, formattedChildAltarText.c_str(), sizeof(gSaveContext.childAltarText) - 1); strncpy(gSaveContext.childAltarText, formattedChildAltarText.c_str(), sizeof(gSaveContext.childAltarText) - 1);
gSaveContext.childAltarText[sizeof(gSaveContext.childAltarText) - 1] = 0; gSaveContext.childAltarText[sizeof(gSaveContext.childAltarText) - 1] = 0;
gSaveContext.rewardCheck[0] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["emeraldLoc"]];
gSaveContext.rewardCheck[1] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["rubyLoc"]];
gSaveContext.rewardCheck[2] = SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["sapphireLoc"]];
std::string adultAltarJsonText = spoilerFileJson["adultAltarText"].get<std::string>(); std::string adultAltarJsonText = spoilerFileJson["adultAltar"]["hintText"].get<std::string>();
std::string formattedAdultAltarText = FormatJsonHintText(adultAltarJsonText); std::string formattedAdultAltarText = FormatJsonHintText(adultAltarJsonText);
strncpy(gSaveContext.adultAltarText, formattedAdultAltarText.c_str(), sizeof(gSaveContext.adultAltarText) - 1); strncpy(gSaveContext.adultAltarText, formattedAdultAltarText.c_str(), sizeof(gSaveContext.adultAltarText) - 1);
gSaveContext.adultAltarText[sizeof(gSaveContext.adultAltarText) - 1] = 0; gSaveContext.adultAltarText[sizeof(gSaveContext.adultAltarText) - 1] = 0;
gSaveContext.rewardCheck[3] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["forestMedallionLoc"]];
gSaveContext.rewardCheck[4] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["fireMedallionLoc"]];
gSaveContext.rewardCheck[5] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["waterMedallionLoc"]];
gSaveContext.rewardCheck[6] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["shadowMedallionLoc"]];
gSaveContext.rewardCheck[7] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["spiritMedallionLoc"]];
gSaveContext.rewardCheck[8] = SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["lightMedallionLoc"]];
std::string ganonHintJsonText = spoilerFileJson["ganonHintText"].get<std::string>(); std::string ganonHintJsonText = spoilerFileJson["ganonHintText"].get<std::string>();
std::string formattedGanonHintJsonText = FormatJsonHintText(ganonHintJsonText); std::string formattedGanonHintJsonText = FormatJsonHintText(ganonHintJsonText);
strncpy(gSaveContext.ganonHintText, formattedGanonHintJsonText.c_str(), sizeof(gSaveContext.ganonHintText) - 1); strncpy(gSaveContext.ganonHintText, formattedGanonHintJsonText.c_str(), sizeof(gSaveContext.ganonHintText) - 1);
gSaveContext.ganonHintText[sizeof(gSaveContext.ganonHintText) - 1] = 0; gSaveContext.ganonHintText[sizeof(gSaveContext.ganonHintText) - 1] = 0;
gSaveContext.ganonHintCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["ganonHintLoc"]];
std::string ganonJsonText = spoilerFileJson["ganonText"].get<std::string>(); std::string ganonJsonText = spoilerFileJson["ganonText"].get<std::string>();
std::string formattedGanonJsonText = FormatJsonHintText(ganonJsonText); std::string formattedGanonJsonText = FormatJsonHintText(ganonJsonText);
@ -1250,11 +1277,13 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
std::string formattedDampeJsonText = FormatJsonHintText(dampeJsonText); std::string formattedDampeJsonText = FormatJsonHintText(dampeJsonText);
strncpy(gSaveContext.dampeText, formattedDampeJsonText.c_str(), sizeof(gSaveContext.dampeText) - 1); strncpy(gSaveContext.dampeText, formattedDampeJsonText.c_str(), sizeof(gSaveContext.dampeText) - 1);
gSaveContext.dampeText[sizeof(gSaveContext.dampeText) - 1] = 0; gSaveContext.dampeText[sizeof(gSaveContext.dampeText) - 1] = 0;
gSaveContext.dampeCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["dampeHintLoc"]];
std::string gregJsonText = spoilerFileJson["gregText"].get<std::string>(); std::string gregJsonText = spoilerFileJson["gregText"].get<std::string>();
std::string formattedGregJsonText = FormatJsonHintText(gregJsonText); std::string formattedGregJsonText = FormatJsonHintText(gregJsonText);
strncpy(gSaveContext.gregHintText, formattedGregJsonText.c_str(), sizeof(gSaveContext.gregHintText) - 1); strncpy(gSaveContext.gregHintText, formattedGregJsonText.c_str(), sizeof(gSaveContext.gregHintText) - 1);
gSaveContext.gregHintText[sizeof(gSaveContext.gregHintText) - 1] = 0; gSaveContext.gregHintText[sizeof(gSaveContext.gregHintText) - 1] = 0;
gSaveContext.gregCheck = SpoilerfileCheckNameToEnum[spoilerFileJson["gregLoc"]];
std::string warpMinuetJsonText = spoilerFileJson["warpMinuetText"].get<std::string>(); std::string warpMinuetJsonText = spoilerFileJson["warpMinuetText"].get<std::string>();
strncpy(gSaveContext.warpMinuetText, warpMinuetJsonText.c_str(), sizeof(gSaveContext.warpMinuetText) - 1); strncpy(gSaveContext.warpMinuetText, warpMinuetJsonText.c_str(), sizeof(gSaveContext.warpMinuetText) - 1);
@ -1284,8 +1313,28 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
int index = 0; int index = 0;
for (auto it = hintsJson.begin(); it != hintsJson.end(); ++it) { for (auto it = hintsJson.begin(); it != hintsJson.end(); ++it) {
gSaveContext.hintLocations[index].check = SpoilerfileCheckNameToEnum[it.key()]; gSaveContext.hintLocations[index].check = SpoilerfileCheckNameToEnum[it.key()];
auto hintInfo = it.value();
if (hintInfo["location"].is_null()) {
gSaveContext.hintLocations[index].hintedCheck = RC_UNKNOWN_CHECK;
} else {
gSaveContext.hintLocations[index].hintedCheck = SpoilerfileCheckNameToEnum[hintInfo["location"]];
}
if (hintInfo["item"].is_null()) {
gSaveContext.hintLocations[index].rGet = RG_NONE;
} else {
gSaveContext.hintLocations[index].rGet = SpoilerfileGetNameToEnum[hintInfo["item"]];
}
gSaveContext.hintLocations[index].type = SpoilerfileHintTypeNameToEnum[hintInfo["type"]];
std::string hintMessage = FormatJsonHintText(it.value()); if (gSaveContext.hintLocations[index].type == HINT_TYPE_TRIAL) {
gSaveContext.hintLocations[index].area = RCAREA_GANONS_CASTLE;
} else if (gSaveContext.hintLocations[index].type == HINT_TYPE_JUNK) {
gSaveContext.hintLocations[index].area = RCAREA_INVALID;
} else {
gSaveContext.hintLocations[index].area = SpoilerfileAreaNameToEnum[hintInfo["area"]];
}
std::string hintMessage = FormatJsonHintText(hintInfo["hint"]);
size_t maxHintTextSize = sizeof(gSaveContext.hintLocations[index].hintText); size_t maxHintTextSize = sizeof(gSaveContext.hintLocations[index].hintText);
strncpy(gSaveContext.hintLocations[index].hintText, hintMessage.c_str(), maxHintTextSize - 1); strncpy(gSaveContext.hintLocations[index].hintText, hintMessage.c_str(), maxHintTextSize - 1);
gSaveContext.hintLocations[index].hintText[maxHintTextSize - 1] = 0; gSaveContext.hintLocations[index].hintText[maxHintTextSize - 1] = 0;

View File

@ -20,6 +20,86 @@ typedef struct {
uint8_t id; uint8_t id;
} Sprite; } Sprite;
typedef enum {
HINT_TYPE_TRIAL,
HINT_TYPE_ALWAYS,
HINT_TYPE_WOTH, // Way of the Hero
HINT_TYPE_BARREN,
HINT_TYPE_ENTRANCE,
HINT_TYPE_SOMETIMES,
HINT_TYPE_RANDOM,
HINT_TYPE_ITEM,
HINT_TYPE_SONG,
HINT_TYPE_OVERWORLD,
HINT_TYPE_DUNGEON,
HINT_TYPE_JUNK,
HINT_TYPE_NAMED_ITEM,
HINT_TYPE_MAX
} HintType;
// Check types based on main settings
typedef enum {
RCTYPE_STANDARD, // Base set of rando checks
RCTYPE_SKULL_TOKEN, // Gold Skulltulas
RCTYPE_COW, // Cows
RCTYPE_ADULT_TRADE, // Adult trade quest checks
RCTYPE_FROG_SONG, // Frog song purple rupee checks
RCTYPE_MAP_COMPASS, // Maps/Compasses
RCTYPE_SMALL_KEY, // Small Keys
RCTYPE_GF_KEY, // Gerudo Fortress Keys
RCTYPE_BOSS_KEY, // Boss Keys
RCTYPE_GANON_BOSS_KEY, // Ganon's boss key
RCTYPE_SHOP, // shops
RCTYPE_SCRUB, // scrubs
RCTYPE_MERCHANT, // merchants
RCTYPE_CHEST_GAME, // todo replace this once we implement it, just using it to exclude for now
RCTYPE_LINKS_POCKET, // todo this feels hacky
RCTYPE_GOSSIP_STONE,
RCTYPE_SONG_LOCATION, // Song locations
RCTYPE_BOSS_HEART_OR_OTHER_REWARD, // Boss heart container or lesser dungeon rewards (lens, ice arrow)
RCTYPE_DUNGEON_REWARD, // Dungeon rewards (blue warps)
RCTYPE_OCARINA, // Ocarina locations
} RandomizerCheckType;
typedef enum { RCVORMQ_VANILLA, RCVORMQ_MQ, RCVORMQ_BOTH } RandomizerCheckVanillaOrMQ;
typedef enum {
RCAREA_KOKIRI_FOREST,
RCAREA_LOST_WOODS,
RCAREA_SACRED_FOREST_MEADOW,
RCAREA_HYRULE_FIELD,
RCAREA_LAKE_HYLIA,
RCAREA_GERUDO_VALLEY,
RCAREA_GERUDO_FORTRESS,
RCAREA_WASTELAND,
RCAREA_DESERT_COLOSSUS,
RCAREA_MARKET,
RCAREA_HYRULE_CASTLE,
RCAREA_KAKARIKO_VILLAGE,
RCAREA_GRAVEYARD,
RCAREA_DEATH_MOUNTAIN_TRAIL,
RCAREA_GORON_CITY,
RCAREA_DEATH_MOUNTAIN_CRATER,
RCAREA_ZORAS_RIVER,
RCAREA_ZORAS_DOMAIN,
RCAREA_ZORAS_FOUNTAIN,
RCAREA_LON_LON_RANCH,
RCAREA_DEKU_TREE,
RCAREA_DODONGOS_CAVERN,
RCAREA_JABU_JABUS_BELLY,
RCAREA_FOREST_TEMPLE,
RCAREA_FIRE_TEMPLE,
RCAREA_WATER_TEMPLE,
RCAREA_SPIRIT_TEMPLE,
RCAREA_SHADOW_TEMPLE,
RCAREA_BOTTOM_OF_THE_WELL,
RCAREA_ICE_CAVERN,
RCAREA_GERUDO_TRAINING_GROUND,
RCAREA_GANONS_CASTLE,
// If adding any more areas, Check Tracker will need a refactor
RCAREA_INVALID
} RandomizerCheckArea;
typedef enum { typedef enum {
RC_UNKNOWN_CHECK, RC_UNKNOWN_CHECK,
RC_LINKS_POCKET, RC_LINKS_POCKET,

View File

@ -7,73 +7,6 @@
enum ActorID : int; enum ActorID : int;
enum SceneID : int; enum SceneID : int;
// Check types based on main settings
typedef enum {
RCTYPE_STANDARD, // Base set of rando checks
RCTYPE_SKULL_TOKEN, // Gold Skulltulas
RCTYPE_COW, // Cows
RCTYPE_ADULT_TRADE, // Adult trade quest checks
RCTYPE_FROG_SONG, // Frog song purple rupee checks
RCTYPE_MAP_COMPASS, // Maps/Compasses
RCTYPE_SMALL_KEY, // Small Keys
RCTYPE_GF_KEY, // Gerudo Fortress Keys
RCTYPE_BOSS_KEY, // Boss Keys
RCTYPE_GANON_BOSS_KEY, // Ganon's boss key
RCTYPE_SHOP, // shops
RCTYPE_SCRUB, // scrubs
RCTYPE_MERCHANT, // merchants
RCTYPE_CHEST_GAME, //todo replace this once we implement it, just using it to exclude for now
RCTYPE_LINKS_POCKET, //todo this feels hacky
RCTYPE_GOSSIP_STONE,
RCTYPE_SONG_LOCATION, // Song locations
RCTYPE_BOSS_HEART_OR_OTHER_REWARD, // Boss heart container or lesser dungeon rewards (lens, ice arrow)
RCTYPE_DUNGEON_REWARD, // Dungeon rewards (blue warps)
RCTYPE_OCARINA, // Ocarina locations
} RandomizerCheckType;
typedef enum {
RCVORMQ_VANILLA,
RCVORMQ_MQ,
RCVORMQ_BOTH
} RandomizerCheckVanillaOrMQ;
typedef enum {
RCAREA_KOKIRI_FOREST,
RCAREA_LOST_WOODS,
RCAREA_SACRED_FOREST_MEADOW,
RCAREA_HYRULE_FIELD,
RCAREA_LAKE_HYLIA,
RCAREA_GERUDO_VALLEY,
RCAREA_GERUDO_FORTRESS,
RCAREA_WASTELAND,
RCAREA_DESERT_COLOSSUS,
RCAREA_MARKET,
RCAREA_HYRULE_CASTLE,
RCAREA_KAKARIKO_VILLAGE,
RCAREA_GRAVEYARD,
RCAREA_DEATH_MOUNTAIN_TRAIL,
RCAREA_GORON_CITY,
RCAREA_DEATH_MOUNTAIN_CRATER,
RCAREA_ZORAS_RIVER,
RCAREA_ZORAS_DOMAIN,
RCAREA_ZORAS_FOUNTAIN,
RCAREA_LON_LON_RANCH,
RCAREA_DEKU_TREE,
RCAREA_DODONGOS_CAVERN,
RCAREA_JABU_JABUS_BELLY,
RCAREA_FOREST_TEMPLE,
RCAREA_FIRE_TEMPLE,
RCAREA_WATER_TEMPLE,
RCAREA_SPIRIT_TEMPLE,
RCAREA_SHADOW_TEMPLE,
RCAREA_BOTTOM_OF_THE_WELL,
RCAREA_ICE_CAVERN,
RCAREA_GERUDO_TRAINING_GROUND,
RCAREA_GANONS_CASTLE,
//If adding any more areas, Check Tracker will need a refactor
RCAREA_INVALID
} RandomizerCheckArea;
#define TWO_ACTOR_PARAMS(a, b) (abs(a) << 16) | abs(b) #define TWO_ACTOR_PARAMS(a, b) (abs(a) << 16) | abs(b)
#define RC_OBJECT(rc, rc_v_or_mq, rc_type, rc_area, actor_id, scene_id, actor_params, og_item_id, rc_shortname, rc_spoilername) \ #define RC_OBJECT(rc, rc_v_or_mq, rc_type, rc_area, actor_id, scene_id, actor_params, og_item_id, rc_shortname, rc_spoilername) \