Real time Hint Assembly, Hint object refactor, and expansion of CustomMessage (#4078)

* Hint text refactor WIP DOES NOT BUILD

* Update to show people DOES NOT BUILD

* stuck on wierd errors

* forgot to add

* expression error does not name a type

* commit in case anyone looks

* static assertion failed: T must be an integral type or an enum.

* fail without error, complaining about unrefernced things

* fix some issues, still linker bs

* restructure some trials, conditionalAlwaysHints and hint_list stuff

* builds and does not crash, but there's text issues

* fix text issues

* commit to push, halfway through trimming down log

* finish trimming spoiler logging

* post merge clean up

* plando mode seems to work, looking into song text wierdness

* push for debugging crash on HBA sign

* fix num and anju issues

* fix the damn sign

* Fix Windows build

Renames GetMessage to GetHintMessage (a different name could be chosen, but GetMessage conflicts with a macro in winuser.h)
Changes uint to size_t, uint does not seem to exist on Windows.

* fix biggoron

* remove some manual formatting that autoformat doesn't like

* fix some altar text

* fix more altar text

* last cleanup

---------

Co-authored-by: Christopher Leggett <chris@leggett.dev>
This commit is contained in:
Pepper0ni 2024-04-29 15:41:33 +01:00 committed by GitHub
parent 65c5806333
commit d69814ed95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 10736 additions and 10844 deletions

View File

@ -3,7 +3,9 @@
#include <algorithm>
#include <stdint.h>
#include <cstring>
#include <string>
#include <map>
#include <spdlog/spdlog.h>
#include <variables.h>
using namespace std::literals::string_literals;
@ -14,9 +16,10 @@ static const std::unordered_map<std::string, char> textBoxSpecialCharacters = {
{ "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B },
{ "ù", 0x9C }, { "û", 0x9D }, { "ü", 0x9E }
};
static const std::unordered_map<std::string, char> colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN },
static const std::unordered_map<std::string, std::string> percentColors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN },
{ "b", QM_BLUE }, { "c", QM_LBLUE }, { "p", QM_PINK },
{ "y", QM_YELLOW }, { "B", QM_BLACK } };
static const std::unordered_map<std::string, ItemID> altarIcons = {
{ "0", ITEM_KOKIRI_EMERALD },
{ "1", ITEM_GORON_RUBY },
@ -33,61 +36,190 @@ static const std::unordered_map<std::string, ItemID> altarIcons = {
{ "c", ITEM_OCARINA_FAIRY },
{ "i", ITEM_OCARINA_TIME },
{ "L", ITEM_BOW_ARROW_LIGHT },
{ "k", ITEM_TUNIC_KOKIRI }
{ "k", ITEM_TUNIC_KOKIRI },
{ "m", ITEM_DUNGEON_MAP },
{ "C", ITEM_COMPASS },
{ "s", ITEM_SKULL_TOKEN },
{ "g", ITEM_MASK_GORON }
};
static std::map<std::string, int> pixelWidthTable = {
{ " ", 6 }, { "!", 6 }, { "\"", 5 }, { "#", 7 }, { "$", 7 }, { "%", 11 }, { "&", 9 }, { "\'", 3 },
{ "(", 6 }, { ")", 6 }, { "*", 6 }, { "+", 7 }, { ",", 3 }, { "-", 5 }, { ".", 3 }, { "/", 7 },
{ "0", 8 }, { "1", 4 }, { "2", 7 }, { "3", 7 }, { "4", 8 }, { "5", 7 }, { "6", 7 }, { "7", 7 },
{ "8", 7 }, { "9", 7 }, { ":", 5 }, { ";", 5 }, { "<", 7 }, { "=", 9 }, { ">", 7 }, { "?", 9 },
{ "@", 10 }, { "A", 9 }, { "B", 7 }, { "C", 9 }, { "D", 9 }, { "E", 6 }, { "F", 6 }, { "G", 9 },
{ "H", 8 }, { "I", 3 }, { "J", 6 }, { "K", 8 }, { "L", 6 }, { "M", 10 }, { "N", 9 }, { "O", 10 },
{ "P", 7 }, { "Q", 10 }, { "R", 8 }, { "S", 8 }, { "T", 7 }, { "U", 8 }, { "V", 9 }, { "W", 12 },
{ "X", 9 }, { "Y", 8 }, { "Z", 8 }, { "[", 6 }, { "\\", 8 }, { "]", 6 }, { "^", 8 }, { "_", 7 },
{ "`", 4 }, { "a", 6 }, { "b", 7 }, { "c", 6 }, { "d", 7 }, { "e", 7 }, { "f", 5 }, { "g", 7 },
{ "h", 6 }, { "i", 3 }, { "j", 5 }, { "k", 6 }, { "l", 3 }, { "m", 9 }, { "n", 7 }, { "o", 7 },
{ "p", 7 }, { "q", 7 }, { "r", 6 }, { "s", 6 }, { "t", 6 }, { "u", 6 }, { "v", 7 }, { "w", 9 },
{ "x", 6 }, { "y", 7 }, { "z", 6 }, { "{", 6 }, { "¦", 4 }, { "}", 6 }, { "¡", 5 }, { "¢", 7 },
{ "£", 8 }, { "¤", 7 }, { "¥", 8 }, { "|", 4 }, { "§", 12 }, { "¨", 12 }, { "©", 10 }, { "ª", 5 },
{ "«", 8 }, { "¬", 7 }, { "\u00AD", 6 }, { "®", 10 }, { "¯", 8 }, { "°", 12 }, { "±", 12 }, { "²", 5 },
{ "³", 5 }, { "µ", 6 }, { "", 8 }, { "·", 4 }, { "¹", 4 }, { "º", 5 }, { "»", 9 }, { "¼", 9 },
{ "½", 9 }, { "¾", 10 }, { "¿", 7 }, { "À", 11 }, { "Á", 9 }, { "Â", 9 }, { "Ã", 9 }, { "Ä", 9 },
{ "Å", 9 }, { "Æ", 12 }, { "Ç", 9 }, { "È", 6 }, { "É", 6 }, { "Ê", 6 }, { "Ë", 6 }, { "Ì", 5 },
{ "Í", 5 }, { "Î", 5 }, { "Ï", 5 }, { "Ð", 10 }, { "Ñ", 9 }, { "Ò", 10 }, { "Ó", 10 }, { "Ô", 10 },
{ "Õ", 10 }, { "Ö", 10 }, { "×", 9 }, { "Ø", 10 }, { "Ù", 8 }, { "Ú", 8 }, { "Û", 8 }, { "Ü", 8 },
{ "Ý", 10 }, { "Þ", 8 }, { "ß", 7 }, { "à", 6 }, { "á", 6 }, { "â", 6 }, { "ã", 6 }, { "ä", 6 },
{ "å", 6 }, { "æ", 11 }, { "ç", 6 }, { "è", 7 }, { "é", 7 }, { "ê", 7 }, { "ë", 7 }, { "ì", 5 },
{ "í", 5 }, { "î", 5 }, { "ï", 5 }, { "ð", 7 }, { "ñ", 7 }, { "ò", 7 }, { "ó", 7 }, { "ô", 7 },
{ "õ", 7 }, { "ö", 7 }, { "÷", 11 }, { "ø", 9 }, { "ù", 7 }, { "ú", 7 }, { "û", 7 }, { "ü", 7 },
{ "ý", 8 }, { "þ", 8 }, { "ÿ", 8 }, { "Œ", 11 }, { "œ", 11 }, { "", 5 }, { "", 5 }, { "", 10 },
{ "Ÿ", 10 }, { "~", 8 }
};
CustomMessage::CustomMessage(std::string english_, std::string german_, std::string french_, TextBoxType type_,
TextBoxPosition position_)
: english(std::move(english_)), german(std::move(german_)), french(std::move(french_)), type(type_),
position(position_) {
: type(type_), position(position_){
messages[LANGUAGE_ENG] = std::move(english_);
messages[LANGUAGE_GER] = std::move(german_);
messages[LANGUAGE_FRA] = std::move(french_);
}
CustomMessage::CustomMessage(std::string english_, std::string german_, std::string french_, std::vector<std::string> colors_,
std::vector<bool> capital_, TextBoxType type_, TextBoxPosition position_) {
messages[LANGUAGE_ENG] = std::move(english_);
messages[LANGUAGE_GER] = std::move(german_);
messages[LANGUAGE_FRA] = std::move(french_);
colors = colors_;
capital = capital_;
type = type_;
position = position_;
}
CustomMessage::CustomMessage(std::string english_, TextBoxType type_, TextBoxPosition position_)
: type(type_), position(position_) {
messages[LANGUAGE_ENG] = std::move(english_);
}
CustomMessage::CustomMessage(std::string english_, std::vector<std::string> colors_, std::vector<bool> capital_, TextBoxType type_, TextBoxPosition position_){
messages[LANGUAGE_ENG] = std::move(english_);
colors = colors_;
capital = capital_;
type = type_;
position = position_;
}
CustomMessage::CustomMessage(Text text, TextBoxType type_,TextBoxPosition position_)
: english(text.GetEnglish()), german(text.GetGerman()), french(text.GetFrench()), type(type_),
position(position_) {
: type(type_), position(position_) {
messages[LANGUAGE_ENG] = text.GetEnglish();
messages[LANGUAGE_GER] = text.GetGerman();
messages[LANGUAGE_FRA] = text.GetFrench();
}
const std::string& CustomMessage::GetEnglish() const {
return english;
const std::string CustomMessage::GetEnglish(MessageFormat format) const {
return GetForLanguage(LANGUAGE_ENG, format);
}
const std::string& CustomMessage::GetFrench() const {
if (french.length() > 0) {
return french;
}
return english;
const std::string CustomMessage::GetGerman(MessageFormat format) const {
return GetForLanguage(LANGUAGE_GER, format);
}
const std::string& CustomMessage::GetGerman() const {
if (german.length() > 0) {
return german;
const std::string CustomMessage::GetFrench(MessageFormat format) const {
return GetForLanguage(LANGUAGE_FRA, format);
}
const std::string CustomMessage::GetForCurrentLanguage(MessageFormat format) const {
return GetForLanguage(gSaveContext.language, format);
}
const std::string CustomMessage::GetForLanguage(uint8_t language, MessageFormat format) const {
std::string output = messages[language].length() > 0 ? messages[language] : messages[LANGUAGE_ENG];
ProcessMessageFormat(output, format);
return output;
}
const std::vector<std::string> CustomMessage::GetAllMessages(MessageFormat format) const{
std::vector<std::string> output = messages;
for (auto str : output){
ProcessMessageFormat(str, format);
}
return english;
return output;
}
void CustomMessage::ProcessMessageFormat(std::string& str, MessageFormat format) const {
if (format == MF_FORMATTED){
FormatString(str);
} else if (format == MF_CLEAN){
CleanString(str);
} else if (format == MF_AUTO_FORMAT){
AutoFormatString(str);
}
}
const std::vector<bool>& CustomMessage::GetCapital() const {
return capital;
}
void CustomMessage::SetCapital(std::vector<bool> capital_){
capital = capital_;
}
const std::vector<std::string>& CustomMessage::GetColors() const {
return colors;
}
void CustomMessage::SetColors(std::vector<std::string> colors_){
colors = colors_;
}
const TextBoxType& CustomMessage::GetTextBoxType() const {
return type;
}
void CustomMessage::SetTextBoxType(TextBoxType boxType){
type = boxType;
}
const TextBoxPosition& CustomMessage::GetTextBoxPosition() const {
return position;
}
CustomMessage CustomMessage::operator+(const CustomMessage& right) const {
return CustomMessage(english + right.GetEnglish(), german + right.GetGerman(), french + right.GetFrench());
std::vector<std::string> newColors = colors;
std::vector<std::string> rColors = right.GetColors();
for (auto color: rColors){
newColors.push_back(color);
}
std::vector<bool> newCapital = capital;
newCapital.insert(capital.end(), right.GetCapital().begin(), right.GetCapital().end());
return CustomMessage(messages[LANGUAGE_ENG] + right.GetEnglish(MF_RAW),
messages[LANGUAGE_GER] + right.GetGerman(MF_RAW),
messages[LANGUAGE_FRA] + right.GetFrench(MF_RAW),
newColors, newCapital, type, position);
}
CustomMessage CustomMessage::operator+(const std::string& right) const {
return CustomMessage(english + right, german + right, french + right);
return CustomMessage(messages[LANGUAGE_ENG] + right, messages[LANGUAGE_GER] + right, messages[LANGUAGE_FRA] + right);
}
void CustomMessage::operator+=(const CustomMessage& right) {
messages[LANGUAGE_ENG] += right.GetEnglish(MF_RAW);
messages[LANGUAGE_GER] += right.GetGerman(MF_RAW);
messages[LANGUAGE_FRA] += right.GetFrench(MF_RAW);
colors.insert(colors.end(), right.GetColors().begin(), right.GetColors().end());
capital.insert(capital.end(), right.GetCapital().begin(), right.GetCapital().end());
}
void CustomMessage::operator+=(const std::string& right) {
english += right;
french += right;
german += right;
messages[LANGUAGE_ENG] += right;
messages[LANGUAGE_GER] += right;
messages[LANGUAGE_FRA] += right;
}
bool CustomMessage::operator==(const CustomMessage& operand) const {
return english == operand.english;
return messages[LANGUAGE_ENG] == operand.messages[LANGUAGE_ENG];
}
bool CustomMessage::operator==(const std::string& operand) const {
for (auto str: messages){
if (str == operand){
return true;
}
}
return false;
}
bool CustomMessage::operator!=(const CustomMessage& operand) const {
@ -95,83 +227,209 @@ bool CustomMessage::operator!=(const CustomMessage& operand) const {
}
void CustomMessage::Replace(std::string&& oldStr, std::string&& newStr) {
for (std::string* str : { &english, &french, &german }) {
size_t position = str->find(oldStr);
for (std::string& str : messages) {
size_t position = str.find(oldStr);
while (position != std::string::npos) {
str->replace(position, oldStr.length(), newStr);
position = str->find(oldStr);
str.replace(position, oldStr.length(), newStr);
position = str.find(oldStr);
}
}
Format();
}
void CustomMessage::Replace(std::string&& oldStr, std::string&& newEnglish, std::string&& newGerman,
std::string&& newFrench) {
size_t position = 0;
position = english.find(oldStr);
while (position != std::string::npos) {
english.replace(position, oldStr.length(), newEnglish);
position = english.find(oldStr);
void CustomMessage::Replace(std::string&& oldStr, CustomMessage newMessage) {
for (uint8_t language = 0; language < LANGUAGE_MAX; language++) {
size_t position = messages[language].find(oldStr);
while (position != std::string::npos) {
messages[language].replace(position, oldStr.length(), newMessage.messages[language]);
position = messages[language].find(oldStr);
}
}
position = french.find(oldStr);
while (position != std::string::npos) {
french.replace(position, oldStr.length(), newFrench);
position = french.find(oldStr);
}
position = german.find(oldStr);
while (position != std::string::npos) {
german.replace(position, oldStr.length(), newGerman);
position = german.find(oldStr);
}
Format();
}
void CustomMessage::Format(ItemID iid) {
for (std::string* str : { &english, &french, &german }) {
str->insert(0, ITEM_OBTAINED(iid));
for (std::string &str : messages) {
str.insert(0, ITEM_OBTAINED(iid));
size_t start_pos = 0;
std::replace(str->begin(), str->end(), '&', NEWLINE()[0]);
while ((start_pos = str->find('^', start_pos)) != std::string::npos) {
str->replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid));
std::replace(str.begin(), str.end(), '&', NEWLINE()[0]);
while ((start_pos = str.find('^', start_pos)) != std::string::npos) {
str.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid));
start_pos += 3;
}
std::replace(str->begin(), str->end(), '@', PLAYER_NAME()[0]);
std::replace(str.begin(), str.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(str);
ReplaceColors(str);
ReplaceAltarIcons(str);
}
ReplaceSpecialCharacters();
ReplaceColors();
ReplaceAltarIcons();
*this += MESSAGE_END();
}
void CustomMessage::Format() {
for (std::string* str : { &english, &french, &german }) {
std::replace(str->begin(), str->end(), '&', NEWLINE()[0]);
std::replace(str->begin(), str->end(), '^', WAIT_FOR_INPUT()[0]);
std::replace(str->begin(), str->end(), '@', PLAYER_NAME()[0]);
for (std::string& str : messages) {
FormatString(str);
}
ReplaceSpecialCharacters();
ReplaceColors();
ReplaceAltarIcons();
*this += MESSAGE_END();
}
void CustomMessage::AutoFormat() {
for (std::string& str : messages) {
AutoFormatString(str);
}
}
void CustomMessage::Clean() {
for (std::string& str : messages) {
CleanString(str);
}
}
void CustomMessage::FormatString(std::string& str) const {
std::replace(str.begin(), str.end(), '&', NEWLINE()[0]);
std::replace(str.begin(), str.end(), '^', WAIT_FOR_INPUT()[0]);
std::replace(str.begin(), str.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(str);
ReplaceColors(str);
ReplaceAltarIcons(str);
str += MESSAGE_END();
}
void DeleteControlCode(std::string& str, std::string code){
size_t start_pos = 0;
while ((start_pos = str.find(code, start_pos)) != std::string::npos) {
str.replace(start_pos, code.length(), "");
start_pos += code.length();
}
}
void CustomMessage::CleanString(std::string& str) const {
std::replace(str.begin(), str.end(), '&', "\n"[0]);
std::replace(str.begin(), str.end(), '^', " "[0]);
for (const auto& colorPair : percentColors) {
DeleteControlCode(str, "%" + colorPair.first);
}
std::erase(str, '#');
for (const auto& iconPair : altarIcons) {
DeleteControlCode(str, "$" + iconPair.first);
}
}
static size_t NextLineLength(const std::string* textStr, const size_t lastNewline, bool hasIcon = false) {
const size_t maxLinePixelWidth = hasIcon ? 200 : 216;
size_t totalPixelWidth = 0;
size_t currentPos = lastNewline;
// Looping through the string from the lastNewline until the total
// width of counted characters exceeds the maximum pixels in a line.
size_t nextPosJump = 0;
while (totalPixelWidth < maxLinePixelWidth && currentPos < textStr->length()) {
// Skip over control codes
if (textStr->at(currentPos) == '%') {
nextPosJump = 2;
} else if (textStr->at(currentPos) == '$') {
nextPosJump = 2;
} else if (textStr->at(currentPos) == '@') {
nextPosJump = 1;
// Assume worst case for player name 12 * 8 (widest character * longest name length)
totalPixelWidth += 96;
} else {
// Some characters only one byte while others are two bytes
// So check both possibilities when checking for a character
if (pixelWidthTable.count(textStr->substr(currentPos, 1))) {
totalPixelWidth += pixelWidthTable[textStr->substr(currentPos, 1)];
nextPosJump = 1;
} else if (pixelWidthTable.count(textStr->substr(currentPos, 2))) {
totalPixelWidth += pixelWidthTable[textStr->substr(currentPos, 2)];
nextPosJump = 2;
} else {
SPDLOG_DEBUG("Table does not contain " + textStr->substr(currentPos, 1) + "/" + textStr->substr(currentPos, 2));
SPDLOG_DEBUG("Full string: " + *textStr);
nextPosJump = 1;
}
}
currentPos += nextPosJump;
}
// return the total number of characters we looped through
if (totalPixelWidth > maxLinePixelWidth && textStr->at(currentPos - nextPosJump) != ' ') {
return currentPos - lastNewline - nextPosJump;
} else {
return currentPos - lastNewline;
}
}
void CustomMessage::AutoFormatString(std::string& str) const {// did I do this right?
ReplaceAltarIcons(str);
ReplaceColors(str);
// insert newlines either manually or when encountering a '&'
size_t lastNewline = 0;
const bool hasIcon = str.find('$', 0) != std::string::npos;
size_t lineLength = NextLineLength(&str, lastNewline, hasIcon);
while (lastNewline + lineLength < str.length()) {
const size_t carrot = str.find('^', lastNewline);
const size_t ampersand = str.find('&', lastNewline);
const size_t lastSpace = str.rfind(' ', lastNewline + lineLength);
const size_t lastPeriod = str.rfind('.', lastNewline + lineLength);
// replace '&' first if it's within the newline range
if (ampersand < lastNewline + lineLength) {
lastNewline = ampersand + 1;
// or move the lastNewline cursor to the next line if a '^' is encountered
} else if (carrot < lastNewline + lineLength) {
lastNewline = carrot + 1;
// some lines need to be split but don't have spaces, look for periods instead
} else if (lastSpace == std::string::npos) {
str.replace(lastPeriod, 1, ".&");
lastNewline = lastPeriod + 2;
} else {
str.replace(lastSpace, 1, "&");
lastNewline = lastSpace + 1;
}
lineLength = NextLineLength(&str, lastNewline, hasIcon);
}
ReplaceSpecialCharacters(str);
ReplaceAltarIcons(str);
std::replace(str.begin(), str.end(), '&', NEWLINE()[0]);
std::replace(str.begin(), str.end(), '^', WAIT_FOR_INPUT()[0]);
std::replace(str.begin(), str.end(), '@', PLAYER_NAME()[0]);
str += MESSAGE_END();
}
void CustomMessage::InsertNumber(uint8_t num){
for (std::string& str : messages) {
size_t firstBar = str.find('|');
if (firstBar != std::string::npos) {
size_t secondBar = str.find('|', firstBar + 1);
if (secondBar != std::string::npos) {
size_t thirdBar = str.find('|', secondBar + 1);
if (thirdBar != std::string::npos) {
if (num == 1) {
str.erase(secondBar, thirdBar - secondBar);
} else {
str.erase(firstBar, secondBar - firstBar);
}
}
}
}
}
//remove the remaining bar
this->Replace("|", "");
Replace("[[d]]", std::to_string(num));
}
void CustomMessage::Capitalize() {
for (std::string* str : { &english, &french, &german }) {
(*str)[0] = std::toupper((*str)[0]);
for (std::string str : messages) {
(str)[0] = std::toupper((str)[0]);
}
}
void CustomMessage::ReplaceSpecialCharacters() {
void CustomMessage::ReplaceSpecialCharacters(std::string& str) const {
// add special characters
for (std::string* str : { &english, &french, &german }) {
for (auto specialCharacterPair : textBoxSpecialCharacters) {
size_t start_pos = 0;
std::string textBoxSpecialCharacterString = ""s;
textBoxSpecialCharacterString += specialCharacterPair.second;
while ((start_pos = str->find(specialCharacterPair.first, start_pos)) != std::string::npos) {
str->replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length();
}
for (auto specialCharacterPair : textBoxSpecialCharacters) {
size_t start_pos = 0;
std::string textBoxSpecialCharacterString = ""s;
textBoxSpecialCharacterString += specialCharacterPair.second;
while ((start_pos = str.find(specialCharacterPair.first, start_pos)) != std::string::npos) {
str.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length();
}
}
}
@ -194,31 +452,49 @@ const char* Interface_ReplaceSpecialCharacters(char text[]) {
return textChar;
}
void CustomMessage::ReplaceColors() {
for (std::string* str : { &english, &french, &german }) {
for (const auto& colorPair : colors) {
std::string textToReplace = "%";
textToReplace += colorPair.first;
size_t start_pos = 0;
while ((start_pos = str->find(textToReplace, start_pos)) != std::string::npos) {
str->replace(start_pos, textToReplace.length(), COLOR(colorPair.second));
start_pos += textToReplace.length();
}
void CustomMessage::ReplaceColors(std::string& str) const {
for (const auto& colorPair : percentColors) {
std::string textToReplace = "%";
textToReplace += colorPair.first;
size_t start_pos = 0;
while ((start_pos = str.find(textToReplace, start_pos)) != std::string::npos) {
str.replace(start_pos, textToReplace.length(), COLOR(colorPair.second));
start_pos += textToReplace.length();
}
}
for (auto color: colors) {
if (const size_t firstHashtag = str.find('#'); firstHashtag != std::string::npos) {
str.replace(firstHashtag, 1, COLOR(color));
if (const size_t secondHashtag = str.find('#', firstHashtag + 1); secondHashtag != std::string::npos) {
str.replace(secondHashtag, 1, COLOR(QM_WHITE));
} else {
SPDLOG_DEBUG("non-matching hashtags in string: \"%s\"", str);
}
}
}
// Remove any remaining '#' characters.
std::erase(str, '#');
}
void CustomMessage::ReplaceAltarIcons(std::string& str) const {
for (const auto& iconPair : altarIcons) {
std::string textToReplace = "$";
textToReplace += iconPair.first;
size_t start_pos = 0;
while ((start_pos = str.find(textToReplace, start_pos)) != std::string::npos) {
str.replace(start_pos, textToReplace.length(), ITEM_OBTAINED(iconPair.second));
start_pos += textToReplace.length();
}
}
}
void CustomMessage::ReplaceAltarIcons() {
for (std::string* str : { &english, &french, &german }) {
for (const auto& iconPair : altarIcons) {
std::string textToReplace = "$";
textToReplace += iconPair.first;
size_t start_pos = 0;
while ((start_pos = str->find(textToReplace, start_pos)) != std::string::npos) {
str->replace(start_pos, textToReplace.length(), ITEM_OBTAINED(iconPair.second));
start_pos += textToReplace.length();
}
}
void CustomMessage::InsertNames(std::vector<CustomMessage> toInsert){
for(uint8_t a = 0; a < toInsert.size(); a++){
CustomMessage temp = toInsert[a];
if ((capital.size() > a) && (capital[a] = true)){
temp.Capitalize();
}
Replace("[[" + std::to_string(a+1) + "]]", temp);
}
}
@ -234,8 +510,12 @@ std::string CustomMessage::NEWLINE() {
return "\x01"s;
}
std::string CustomMessage::COLOR(uint8_t x) {
return "\x05"s + char(x);
std::string CustomMessage::COLOR(std::string x) {
return "\x05"s + x;
}
std::string CustomMessage::POINTS(std::string x) {
return "\x1E"s + x;
}
std::string CustomMessage::WAIT_FOR_INPUT() {

View File

@ -1,8 +1,9 @@
#pragma once
#include <string>
#include <unordered_map>
#include <cstdint>
#include <exception>
#include <vector>
#include <string>
#include "../../../include/z64item.h"
#include "../../../include/message_data_textbox_types.h"
@ -10,14 +11,23 @@
#undef MESSAGE_END
#define QM_WHITE 0x00
#define QM_RED 0x41
#define QM_GREEN 0x42
#define QM_BLUE 0x43
#define QM_LBLUE 0x44
#define QM_PINK 0x45
#define QM_YELLOW 0x46
#define QM_BLACK 0x47
#define QM_WHITE "\x00"s
#define QM_RED "\x41"
#define QM_GREEN "\x42"
#define QM_BLUE "\x43"
#define QM_LBLUE "\x44"
#define QM_PINK "\x45"
#define QM_YELLOW "\x46"
#define QM_BLACK "\x47"
#define HS_HORSE_ARCHERY "\x00"s //HS_HBA is an enum already
typedef enum {
MF_FORMATTED,
MF_CLEAN,
MF_RAW,
MF_AUTO_FORMAT
} MessageFormat;
/**
* @brief Encapsulates logic surrounding languages, and formatting strings for OoT's textboxes and
@ -30,25 +40,41 @@ class CustomMessage {
CustomMessage() = default;
CustomMessage(std::string english_, std::string german_, std::string french_,
TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM);
CustomMessage(std::string english_, std::string german_, std::string french_, std::vector<std::string> colors_, std::vector<bool> capital_ = {},
TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM);
CustomMessage(std::string english_, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM);
CustomMessage(std::string english_, std::vector<std::string> colors_, std::vector<bool> capital_ = {}, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM);
CustomMessage(Text text, TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM);
static std::string MESSAGE_END() ;
static std::string ITEM_OBTAINED(uint8_t x) ;
static std::string NEWLINE() ;
static std::string COLOR(uint8_t x) ;
static std::string COLOR(std::string x) ;
static std::string POINTS(std::string x) ;//HIGH_SCORE is also a macro
static std::string WAIT_FOR_INPUT() ;
static std::string PLAYER_NAME() ;
const std::string& GetEnglish() const;
const std::string& GetFrench() const;
const std::string& GetGerman() const;
const std::string GetEnglish(MessageFormat format = MF_FORMATTED) const;
const std::string GetFrench(MessageFormat format = MF_FORMATTED) const;
const std::string GetGerman(MessageFormat format = MF_FORMATTED) const;
const std::string GetForCurrentLanguage(MessageFormat format = MF_FORMATTED) const;
const std::string GetForLanguage(uint8_t language, MessageFormat format = MF_FORMATTED) const;
const std::vector<std::string> GetAllMessages(MessageFormat format = MF_FORMATTED) const;
void ProcessMessageFormat(std::string& str, MessageFormat format) const;
const std::vector<std::string>& GetColors() const;
void SetColors(std::vector<std::string> colors_);
const std::vector<bool>& GetCapital() const;
void SetCapital(std::vector<bool> capital_);
const TextBoxType& GetTextBoxType() const;
void SetTextBoxType(TextBoxType boxType);
const TextBoxPosition& GetTextBoxPosition() const;
CustomMessage operator+(const CustomMessage& right) const;
CustomMessage operator+(const std::string& right) const;
void operator+=(const std::string& right);
bool operator==(const CustomMessage& right) const;
void operator+=(const CustomMessage& right);
bool operator==(const CustomMessage& operand) const;
bool operator==(const std::string& operand) const;
bool operator!=(const CustomMessage& right) const;
/**
@ -63,15 +89,13 @@ class CustomMessage {
/**
* @brief Finds an instance of oldStr in each language of the CustomMessage,
* and replaces it with the corresponding new string provided for each language.
* and replaces it with the corresponding string in the provided CustomMessage.
* Typically used for dynamic variable replacement (i.e. gameplay stats, skulltula count)
*
* @param oldStr the string to be replaced
* @param newEnglish the new string for the English message
* @param newGerman the new string for the German message
* @param newFrench the new string for the French message
* @param newMessage the message containing the new strings.
*/
void Replace(std::string&& oldStr, std::string&& newEnglish, std::string&& newGerman, std::string&& newFrench);
void Replace(std::string&& oldStr, CustomMessage newMessage);
/**
* @brief Capitalizes the first letter of the string for each language.
@ -82,17 +106,22 @@ class CustomMessage {
* @brief Replaces special characters (things like diacritics for non-english langugages)
* with the control codes used to display them in OoT's textboxes.
*/
void ReplaceSpecialCharacters();
void ReplaceSpecialCharacters(std::string& str) const;
/**
* @brief Replaces our color variable strings with the OoT control codes.
*/
void ReplaceColors();
void ReplaceColors(std::string& str) const;
/**
* @brief Replaces `$<char>` variable strings with OoT control codes.
*/
void ReplaceAltarIcons();
void ReplaceAltarIcons(std::string& str) const;
/**
* @brief Replaces [[1]] style variable strings with the provided vector of customMessages
*/
void InsertNames(std::vector<CustomMessage> toInsert);
/**
* @brief Replaces various symbols with the control codes necessary to
@ -103,6 +132,15 @@ class CustomMessage {
*/
void Format(ItemID iid);
/**
* @brief Replaces [[d]] in text with the supplied number, and if plural
* options exist (2 blocks of text surrounded by |) choose the former if it 1,
* and the latter otherwise, deleting the other and the |'s.
*
* @param num the number to insert.
*/
void InsertNumber(uint8_t num);
/**
* @brief Replaces various symbols with the control codes necessary to
* display them in OoT's textboxes. i.e. special characters, colors, newlines,
@ -110,12 +148,44 @@ class CustomMessage {
*/
void Format();
/**
* @brief formats the message specifically to fit in OoT's
* textboxes, and use it's formatting.
*/
void AutoFormat();
/**
* @brief Removes all OoT formatting from the message,
* making it a good form for writing into spoiler logs.
*/
void Clean();
/**
* @brief Replaces various symbols with the control codes necessary to
* display them in OoT's textboxes for a single string
* . i.e. special characters, colors, newlines, wait for input, etc.
*/
void FormatString(std::string& str) const;
/**
* @brief formats the string specifically to fit in OoT's
* textboxes, and use it's formatting.
* RANDOTODO whoever knows exactly what this does check my adaption
*/
void AutoFormatString(std::string& str) const;
/**
* @brief Removes symbols used for control codes from the string,
* leaving raw text
*/
void CleanString(std::string& str) const;
private:
std::string english = "";
std::string french = "";
std::string german = "";
std::vector<std::string> messages = {"","",""};
TextBoxType type = TEXTBOX_TYPE_BLACK;
TextBoxPosition position = TEXTBOX_POS_BOTTOM;
std::vector<std::string> colors = {};
std::vector<bool> capital = {};
};
typedef std::unordered_map<uint16_t, CustomMessage> CustomMessageTable;

View File

@ -100,6 +100,7 @@ typedef enum {
TEXT_MALON_LINK_HAS_TIME_BUT_NO_RECORD = 0x2090,
TEXT_MALON_LINK_HAS_RECORD = 0x2091,
TEXT_MALON_FIRST_BEAT_THIS_RECORD = 0x2092,
TEXT_BIGGORON_I_MAAAADE_THISSSS = 0x3002,
TEXT_MEDIGORON = 0x304C,
TEXT_FIRE_TEMPLE_GORON_OWE_YOU_BIG_TIME = 0x3052,
TEXT_BIGGORON_BETTER_AT_SMITHING = 0x3053,

View File

@ -204,16 +204,7 @@ void MessageDebug_StartTextBox(const char* tableId, uint16_t textId, uint8_t lan
const CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(tableId, textId);
font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition();
switch (language) {
case LANGUAGE_FRA:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetFrench(), maxBufferSize);
break;
case LANGUAGE_GER:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetGerman(), maxBufferSize);
break;
case LANGUAGE_ENG:
default:
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetEnglish(), maxBufferSize);
break;
font->msgLength = SohUtils::CopyStringToCharBuffer(buffer, messageEntry.GetForLanguage(language), maxBufferSize);
}
msgCtx->msgLength = static_cast<int32_t>(font->msgLength);
}

View File

@ -1161,9 +1161,7 @@ void RegisterAltTrapTypes() {
void RegisterRandomizerSheikSpawn() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneSpawnActors>([]() {
if (!gPlayState) return;
bool canSheik = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIAL_COUNT) != 0 &&
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LIGHT_ARROWS_HINT));
if (!IS_RANDO || !LINK_IS_ADULT || !canSheik) return;
if (!IS_RANDO || !LINK_IS_ADULT || !OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHEIK_LA_HINT)) return;
switch (gPlayState->sceneNum) {
case SCENE_TEMPLE_OF_TIME:
if (gPlayState->roomCtx.curRoom.num == 1) {

View File

@ -403,7 +403,8 @@ const std::vector<const char*> randomizerCvars = {
"gRandomizeLacsRewardOptions",
"gRandomizeLacsStoneCount",
"gRandomizeLacsTokenCount",
"gRandomizeLAHint",
"gRandomizeGanondorfHint",
"gRandomizeSheikLAHint",
"gRandomizeLinksPocket",
"gRandomizeLogicRules",
"gRandomizeMedallionCount",

View File

@ -158,7 +158,7 @@ constexpr std::array DungeonColors = {
CreateMessage(textId, unk_04, textBoxType, textBoxPosition, text.GetEnglish(), text.GetFrench(), text.GetSpanish());
}
Text AddColorsAndFormat(Text text, const std::vector<uint8_t>& colors /*= {}*/) {
Text AddColorsAndFormat(Text text, const std::vector<std::string>& colors /*= {}*/) {
//for each language
for (std::string* textStr : {&text.english, &text.french, &text.spanish}) {
@ -280,12 +280,12 @@ constexpr std::array DungeonColors = {
std::string SET_SPEED(uint8_t x) {
return "\x7F\x10"s + char(x);
}
std::string SKULLTULAS_DESTROYED() { return "\x7F\x15"s; }
std::string SKULLTULAS_DESTROYED() { return "\x7F\x15"s; } //RANDOTODO just refernce the versions in CustomMessage
std::string CURRENT_TIME() { return "\x7F\x17"s; }
std::string UNSKIPPABLE() { return "\x7F\x19"s; }
std::string TWO_WAY_CHOICE() { return "\x1B"s; }
std::string NEWLINE() { return "\x7F\x1C"s; }
std::string COLOR(uint8_t x) { return "\x7F\x1D"s + char(x); }
std::string COLOR(std::string x) { return "\x7F\x1D"s + x; }
std::string CENTER_TEXT() { return "\x7F\x1E"s; }
std::string IF_NOT_MQ() { return "\x7F\x29"s; }
std::string MQ_ELSE() { return "\x7F\x2A"s; }

View File

@ -7,15 +7,6 @@
#include "text.hpp"
#define QM_WHITE 0x00
#define QM_RED 0x41
#define QM_GREEN 0x42
#define QM_BLUE 0x43
#define QM_LBLUE 0x44
#define QM_PINK 0x45
#define QM_YELLOW 0x46
#define QM_BLACK 0x47
namespace CustomMessages {
typedef struct {
// In the true file format, offset is the offset into the QM file.
@ -73,7 +64,7 @@ typedef struct {
std::string UNSKIPPABLE();
std::string TWO_WAY_CHOICE();
std::string NEWLINE();
std::string COLOR(uint8_t x);
std::string COLOR(std::string x);
std::string CENTER_TEXT();
std::string IF_NOT_MQ();
std::string MQ_ELSE();

View File

@ -9,7 +9,6 @@
#include "spoiler_log.hpp"
#include "starting_inventory.hpp"
#include "hints.hpp"
#include "hint_list.hpp"
#include "../entrance.h"
#include "shops.hpp"
#include "pool_functions.hpp"
@ -1145,13 +1144,6 @@ int Fill() {
ctx->CreateItemOverrides();
ctx->GetEntranceShuffler()->CreateEntranceOverrides();
SPDLOG_INFO("Creating Other Hint Texts...");
//funny ganon line
Text ganonText = RandomElement(GetHintCategory(HintCategory::GanonLine)).GetText();
CreateMessageFromTextObject(0x70CB, 0, 2, 3, AddColorsAndFormat(ganonText));
SetGanonText(ganonText);
SPDLOG_INFO("Creating Other Hint Texts Done");
CreateAllHints();
CreateWarpSongTexts();
return 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +0,0 @@
#pragma once
#include "hints.hpp"
#include <vector>
extern std::array<HintText, RHT_MAX> hintTable;
void HintTable_Init();
const HintText& Hint(const RandomizerHintTextKey hintKey);
const HintText& Hint(const RandomizerArea area);
std::vector<HintText> GetHintCategory(HintCategory category);
void HintTable_Init_Item();
void HintTable_Init_Exclude_Overworld();
void HintTable_Init_Exclude_Dungeon();

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,15 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include <variant>
#include "text.hpp"
#include "random.hpp"
#include <functional>
#include "../randomizerTypes.h"
#include "../../custom-message/CustomMessageManager.h"
struct HintDistributionSetting {
std::string name;
HintType type;
@ -16,21 +19,8 @@ struct HintDistributionSetting {
std::function<bool(RandomizerCheck)> filter;
uint8_t dungeonLimit;
HintDistributionSetting(std::string _name,
HintType _type,
uint32_t _weight,
uint8_t _fixed,
uint8_t _copies,
std::function<bool(RandomizerCheck)> _filter,
uint8_t _dungeonLimit = 40){
name = _name;
type = _type;
weight = _weight;
fixed = _fixed;
copies = _copies;
filter = _filter;
dungeonLimit = _dungeonLimit;
}
HintDistributionSetting(std::string _name, HintType _type, uint32_t _weight, uint8_t _fixed, uint8_t _copies,
std::function<bool(RandomizerCheck)> _filter, uint8_t _dungeonLimit = 40);
};
struct HintSetting {
@ -40,185 +30,45 @@ struct HintSetting {
std::vector<HintDistributionSetting> distTable;
};
enum class HintCategory {
Item,
Always,
Sometimes,
Exclude,
Entrance,
Region,
Junk,
DungeonName,
Boss,
Bridge,
GanonsBossKey,
LACS,
Altar,
Validation,
OtherHint,
MasterSword,
GanonLine,
SheikLine,
MerchantsDialogs,
};
class HintText {
public:
HintText() = default;
HintText(std::vector<Text> obscureText_, std::vector<Text> ambiguousText_, Text clearText_, HintCategory type_)
: obscureText(std::move(obscureText_)),
ambiguousText(std::move(ambiguousText_)),
clearText(std::move(clearText_)),
type(type_) {
}
static auto Item(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Item};
}
static auto Always(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Always};
}
static auto Sometimes(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Sometimes};
}
static auto Exclude(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Exclude};
}
static auto Entrance(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Entrance};
}
static auto Region(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Region};
}
static auto Junk(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Junk};
}
static auto DungeonName(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::DungeonName};
}
static auto Boss(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Boss};
}
static auto Bridge(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Bridge};
}
static auto GanonsBossKey(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::GanonsBossKey};
}
static auto LACS(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::LACS};
}
static auto Altar(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Altar};
}
static auto Validation(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::Validation};
}
static auto OtherHint(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::OtherHint};
}
static auto MasterSword(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::MasterSword};
}
static auto GanonLine(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::GanonLine};
}
static auto SheikLine(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::SheikLine};
}
static auto MerchantsDialogs(std::vector<Text>&& obscureText, std::vector<Text>&& ambiguousText = {}, Text&& clearText = {}) {
return HintText{std::move(obscureText), std::move(ambiguousText), std::move(clearText), HintCategory::MerchantsDialogs};
}
Text& GetObscure() {
return RandomElement(obscureText);
}
const Text& GetObscure() const {
return RandomElement(obscureText);
}
Text& GetAmbiguous() {
if (ambiguousText.size() > 0) {
return RandomElement(ambiguousText);
}
return RandomElement(obscureText);
}
const Text& GetAmbiguous() const {
if (ambiguousText.size() > 0) {
return RandomElement(ambiguousText);
}
return RandomElement(obscureText);
}
const Text& GetClear() const {
if (clearText.GetEnglish().empty()) {
return GetObscure();
}
return clearText;
}
const Text& GetText() const;
const Text GetTextCopy() const;
HintCategory GetType() const {
return type;
}
bool operator==(const HintText& right) const {
return obscureText == right.obscureText &&
ambiguousText == right.ambiguousText &&
clearText == right.clearText;
}
bool operator!=(const HintText& right) const {
return !operator==(right);
}
HintText(CustomMessage clearText_, std::vector<CustomMessage> ambiguousText_ = {}, std::vector<CustomMessage> obscureText_ = {});
const CustomMessage& GetClear() const;
const CustomMessage& GetObscure() const;
const CustomMessage& GetObscure(uint8_t selection) const;
const CustomMessage& GetAmbiguous() const;
const CustomMessage& GetAmbiguous(uint8_t selection) const;
uint8_t GetAmbiguousSize() const;
uint8_t GetObscureSize() const;
const CustomMessage& GetHintMessage(uint8_t selection = 0) const;
const CustomMessage GetMessageCopy() const;
bool operator==(const HintText& right) const;
bool operator!=(const HintText& right) const;
private:
std::vector<Text> obscureText = {};
std::vector<Text> ambiguousText = {};
Text clearText;
HintCategory type;
CustomMessage clearText;
std::vector<CustomMessage> ambiguousText = {};
std::vector<CustomMessage> obscureText = {};
};
using ConditionalAlwaysHint = std::pair<RandomizerCheck, std::function<bool()>>;
struct StaticHintInfo{
HintType type;
std::vector<RandomizerHintTextKey> hintKeys;
RandomizerSettingKey setting;
std::variant<bool, uint8_t> condition;
std::vector<RandomizerCheck> targetChecks;
std::vector<RandomizerGet> targetItems;
std::vector<RandomizerCheck> hintChecks;
bool yourPocket;
int num;
typedef enum {
DUNGEON_NEITHER,
DUNGEON_BARREN,
DUNGEON_WOTH,
} DungeonHintInfo;
//10 dungeons as GTG and GC are excluded
extern std::array<DungeonHintInfo, 10> dungeonInfoData;
extern std::array<ConditionalAlwaysHint, 12> conditionalAlwaysHints;
StaticHintInfo() = default;
StaticHintInfo(HintType _type, std::vector<RandomizerHintTextKey> _hintKeys, RandomizerSettingKey _setting, std::variant<bool, uint8_t> _condition,
std::vector<RandomizerCheck> _targetChecks, std::vector<RandomizerGet> _targetItems = {},
std::vector<RandomizerCheck> _hintChecks = {}, bool _yourPocket = false, int _num = 0);
};
extern void CreateAllHints();
extern void CreateWarpSongTexts();
void SetGanonText(Text text);
std::string GetMasterSwordHintLoc();
std::string GetLightArrowHintLoc();
void CreateStaticHints();

View File

@ -4,7 +4,6 @@
#include <vector>
#include <list>
#include "hint_list.hpp"
#include "fill.hpp"
#include "../randomizerTypes.h"
#include "../context.h"

View File

@ -38,10 +38,10 @@ void AreaTable_Init_CastleTown() {
EventAccess(&logic->GossipStoneFairy, {[]{return logic->GossipStoneFairy || logic->CanSummonGossipFairyWithoutSuns;}}),
}, {
//Locations
LocationAccess(RC_TOT_LEFT_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_TOT_LEFTMOST_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_TOT_LEFT_CENTER_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_TOT_RIGHT_CENTER_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_TOT_RIGHT_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_TOT_RIGHTMOST_GOSSIP_STONE, {[]{return true;}}),
}, {
//Exits
Entrance(RR_THE_MARKET, {[]{return true;}}),

View File

@ -32,12 +32,12 @@ void AreaTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_SHADOW_TRIAL, {[]{return true;}}),
Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL, {[]{return true;}}),
Entrance(RR_GANONS_CASTLE_LIGHT_TRIAL, {[]{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}}),
Entrance(RR_GANONS_CASTLE_TOWER, {[]{return (logic->ForestTrialClear || randoCtx->GetTrial(Rando::FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || randoCtx->GetTrial(Rando::FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || randoCtx->GetTrial(Rando::WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || randoCtx->GetTrial(Rando::SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || randoCtx->GetTrial(Rando::SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || randoCtx->GetTrial(Rando::LIGHT_TRIAL)->IsSkipped());}}),
Entrance(RR_GANONS_CASTLE_TOWER, {[]{return (logic->ForestTrialClear || randoCtx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || randoCtx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || randoCtx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || randoCtx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || randoCtx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || randoCtx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}}),
Entrance(RR_GANONS_CASTLE_DEKU_SCRUBS, {[]{return randoCtx->GetTrickOption(RT_LENS_GANON) || logic->CanUse(RG_LENS_OF_TRUTH);}}),
});
@ -134,12 +134,12 @@ void AreaTable_Init_GanonsCastle() {
Entrance(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL, {[]{return true;}}),
Entrance(RR_GANONS_CASTLE_MQ_SPIRIT_TRIAL, {[]{return true;}}),
Entrance(RR_GANONS_CASTLE_MQ_LIGHT_TRIAL, {[]{return logic->CanUse(RG_GOLDEN_GAUNTLETS);}}),
Entrance(RR_GANONS_CASTLE_TOWER, {[]{return (logic->ForestTrialClear || randoCtx->GetTrial(Rando::FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || randoCtx->GetTrial(Rando::FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || randoCtx->GetTrial(Rando::WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || randoCtx->GetTrial(Rando::SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || randoCtx->GetTrial(Rando::SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || randoCtx->GetTrial(Rando::LIGHT_TRIAL)->IsSkipped());}}),
Entrance(RR_GANONS_CASTLE_TOWER, {[]{return (logic->ForestTrialClear || randoCtx->GetTrial(TK_FOREST_TRIAL)->IsSkipped()) &&
(logic->FireTrialClear || randoCtx->GetTrial(TK_FIRE_TRIAL)->IsSkipped()) &&
(logic->WaterTrialClear || randoCtx->GetTrial(TK_WATER_TRIAL)->IsSkipped()) &&
(logic->ShadowTrialClear || randoCtx->GetTrial(TK_SHADOW_TRIAL)->IsSkipped()) &&
(logic->SpiritTrialClear || randoCtx->GetTrial(TK_SPIRIT_TRIAL)->IsSkipped()) &&
(logic->LightTrialClear || randoCtx->GetTrial(TK_LIGHT_TRIAL)->IsSkipped());}}),
Entrance(RR_GANONS_CASTLE_MQ_DEKU_SCRUBS, {[]{return randoCtx->GetTrickOption(RT_LENS_GANON_MQ) || logic->CanUse(RG_LENS_OF_TRUTH);}}),
});

View File

@ -34,7 +34,7 @@ void AreaTable_Init_HyruleField() {
//Locations
LocationAccess(RC_HF_SOUTHEAST_GROTTO_CHEST, {[]{return true;}}),
LocationAccess(RC_HF_SOUTHEAST_GROTTO_FISH, {[]{return logic->HasBottle;}}),
LocationAccess(RC_HF_SOUTHEAST_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_HF_SOUTHEAST_GROTTO_BEEHIVE_LEFT, {[]{return logic->CanBreakLowerBeehives;}}),
LocationAccess(RC_HF_SOUTHEAST_GROTTO_BEEHIVE_RIGHT, {[]{return logic->CanBreakLowerBeehives;}}),
}, {
@ -77,7 +77,7 @@ void AreaTable_Init_HyruleField() {
//Locations
LocationAccess(RC_HF_NEAR_MARKET_GROTTO_CHEST, {[]{return true;}}),
LocationAccess(RC_HF_NEAR_MARKET_GROTTO_FISH, {[]{return logic->HasBottle;}}),
LocationAccess(RC_HF_NEAR_MARKET_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_HF_NEAR_MARKET_GROTTO_BEEHIVE_LEFT, {[]{return logic->CanBreakLowerBeehives;}}),
LocationAccess(RC_HF_NEAR_MARKET_GROTTO_BEEHIVE_RIGHT, {[]{return logic->CanBreakLowerBeehives;}}),
}, {

View File

@ -281,7 +281,7 @@ void AreaTable_Init_Kakariko() {
EventAccess(&logic->GossipStoneFairy, {[]{return logic->GossipStoneFairy || logic->CanSummonGossipFairyWithoutSuns;}}),
}, {
//Locations
LocationAccess(RC_GY_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_GRAVEYARD_GOSSIP_STONE, {[]{return true;}}),
}, {
//Exits
Entrance(RR_THE_GRAVEYARD, {[]{return true;}}),

View File

@ -98,7 +98,7 @@ void AreaTable_Init_LostWoods() {
//Locations
LocationAccess(RC_KF_STORMS_GROTTO_CHEST, {[]{return true;}}),
LocationAccess(RC_KF_STORMS_GROTTO_FISH, {[]{return logic->HasBottle;}}),
LocationAccess(RC_KF_STORMS_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_KF_STORMS_GROTTO_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_KF_STORMS_GROTTO_BEEHIVE_LEFT, {[]{return logic->CanBreakLowerBeehives;}}),
LocationAccess(RC_KF_STORMS_GROTTO_BEEHIVE_RIGHT, {[]{return logic->CanBreakLowerBeehives;}}),
}, {
@ -168,7 +168,7 @@ void AreaTable_Init_LostWoods() {
//Locations
LocationAccess(RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, {[]{return true;}}),
LocationAccess(RC_LW_NEAR_SHORTCUTS_GROTTO_FISH, {[]{return logic->HasBottle;}}),
LocationAccess(RC_LW_NEAR_SHORTCUTS_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_LW_NEAR_SHORTCUTS_GROTTO_BEEHIVE_LEFT, {[]{return logic->CanBreakLowerBeehives;}}),
LocationAccess(RC_LW_NEAR_SHORTCUTS_GROTTO_BEEHIVE_RIGHT, {[]{return logic->CanBreakLowerBeehives;}}),
}, {

View File

@ -158,8 +158,8 @@ void AreaTable_Init_ZorasDomain() {
LocationAccess(RC_ZF_GS_TREE, {[]{return logic->IsChild;}}),
LocationAccess(RC_ZF_GS_ABOVE_THE_LOG, {[]{return logic->IsChild && logic->HookshotOrBoomerang && logic->AtNight && logic->CanGetNightTimeGS;}}),
LocationAccess(RC_ZF_GS_HIDDEN_CAVE, {[]{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanBlastOrSmash && logic->HookshotOrBoomerang && logic->IsAdult && logic->AtNight && logic->CanGetNightTimeGS;}}),
LocationAccess(RC_FAIRY_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_JABU_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_ZF_FAIRY_GOSSIP_STONE, {[]{return true;}}),
LocationAccess(RC_ZF_JABU_GOSSIP_STONE, {[]{return true;}}),
}, {
//Exits
Entrance(RR_ZD_BEHIND_KING_ZORA, {[]{return true;}}),

View File

@ -1,5 +1,4 @@
#include "menu.hpp"
#include "hint_list.hpp"
#include "../static_data.h"
#include "../item_location.h"
#include "location_access.hpp"
@ -12,7 +11,6 @@
void RandoMain::GenerateRando(std::set<RandomizerCheck> excludedLocations, std::set<RandomizerTrick> enabledTricks,
std::string seedString) {
HintTable_Init();
// std::string settingsFileName = "./randomizer/latest_settings.json";
// CVarSetString("gLoadedPreset", settingsFileName.c_str());

View File

@ -38,7 +38,6 @@ using namespace Rando;
json jsonData;
std::map<RandomizerHintTextKey, Rando::ItemLocation*> hintedLocations;
extern std::array<std::string, HINT_TYPE_MAX> hintTypeNames;
extern std::array<std::string, 17> hintCategoryNames;
extern Area* GetHintRegion(uint32_t);
@ -499,16 +498,7 @@ static void WriteRequiredTrials() {
auto ctx = Rando::Context::GetInstance();
for (const auto& trial : ctx->GetTrials()->GetTrialList()) {
if (trial->IsRequired()) {
std::string trialName;
switch (gSaveContext.language) {
case LANGUAGE_FRA:
trialName = trial->GetName().GetFrench();
break;
case LANGUAGE_ENG:
default:
trialName = trial->GetName().GetEnglish();
break;
}
std::string trialName = trial->GetName().GetForCurrentLanguage(MF_CLEAN);
jsonData["requiredTrials"].push_back(RemoveLineBreaks(trialName));
}
}
@ -553,117 +543,13 @@ Rando::ItemLocation* GetItemLocation(RandomizerGet item) {
})[0]);
}
// Writes the hints to the spoiler log, if they are enabled.
static void WriteHints() {
auto ctx = Rando::Context::GetInstance();
uint8_t language = ctx->GetOption(RSK_LANGUAGE).GetSelectedOptionIndex();
if (ctx->GetOption(RSK_SHUFFLE_WARP_SONGS)) {
jsonData["warpMinuetText"] = ctx->GetHint(RH_MINUET_WARP_LOC)->GetText().GetForLanguage(language);
jsonData["warpBoleroText"] = ctx->GetHint(RH_BOLERO_WARP_LOC)->GetText().GetForLanguage(language);
jsonData["warpSerenadeText"] = ctx->GetHint(RH_SERENADE_WARP_LOC)->GetText().GetForLanguage(language);
jsonData["warpRequiemText"] = ctx->GetHint(RH_REQUIEM_WARP_LOC)->GetText().GetForLanguage(language);
jsonData["warpNocturneText"] = ctx->GetHint(RH_NOCTURNE_WARP_LOC)->GetText().GetForLanguage(language);
jsonData["warpPreludeText"] = ctx->GetHint(RH_PRELUDE_WARP_LOC)->GetText().GetForLanguage(language);
}
jsonData["childAltar"]["hintText"] = ctx->GetHint(RH_ALTAR_CHILD)->GetText().GetForLanguage(language);
jsonData["adultAltar"]["hintText"] = ctx->GetHint(RH_ALTAR_ADULT)->GetText().GetForLanguage(language);
Rando::ItemLocation* emeraldLoc = GetItemLocation(RG_KOKIRI_EMERALD);
Rando::ItemLocation* rubyLoc = GetItemLocation(RG_GORON_RUBY);
Rando::ItemLocation* sapphireLoc = GetItemLocation(RG_ZORA_SAPPHIRE);
std::string emeraldArea;
std::string erubyArea;
std::string sapphireArea;
jsonData["childAltar"]["rewards"]["emeraldLoc"] = Rando::StaticData::GetLocation(emeraldLoc->GetRandomizerCheck())->GetName();
jsonData["childAltar"]["rewards"]["rubyLoc"] = Rando::StaticData::GetLocation(rubyLoc->GetRandomizerCheck())->GetName();
jsonData["childAltar"]["rewards"]["sapphireLoc"] =
Rando::StaticData::GetLocation(sapphireLoc->GetRandomizerCheck())->GetName();
Rando::ItemLocation* forestMedallionLoc = GetItemLocation(RG_FOREST_MEDALLION);
Rando::ItemLocation* fireMedallionLoc = GetItemLocation(RG_FIRE_MEDALLION);
Rando::ItemLocation* waterMedallionLoc = GetItemLocation(RG_WATER_MEDALLION);
Rando::ItemLocation* shadowMedallionLoc = GetItemLocation(RG_SHADOW_MEDALLION);
Rando::ItemLocation* spiritMedallionLoc = GetItemLocation(RG_SPIRIT_MEDALLION);
Rando::ItemLocation* lightMedallionLoc = GetItemLocation(RG_LIGHT_MEDALLION);
jsonData["adultAltar"]["rewards"]["forestMedallionLoc"] =
Rando::StaticData::GetLocation(forestMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["adultAltar"]["rewards"]["fireMedallionLoc"] =
Rando::StaticData::GetLocation(fireMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["adultAltar"]["rewards"]["waterMedallionLoc"] =
Rando::StaticData::GetLocation(waterMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["adultAltar"]["rewards"]["shadowMedallionLoc"] =
Rando::StaticData::GetLocation(shadowMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["adultAltar"]["rewards"]["spiritMedallionLoc"] =
Rando::StaticData::GetLocation(spiritMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["adultAltar"]["rewards"]["lightMedallionLoc"] =
Rando::StaticData::GetLocation(lightMedallionLoc->GetRandomizerCheck())->GetName();
jsonData["ganonText"] = ctx->GetHint(RH_GANONDORF_NOHINT)->GetText().GetForLanguage(language);
if (ctx->GetOption(RSK_LIGHT_ARROWS_HINT)){
jsonData["ganonHintText"] = ctx->GetHint(RH_GANONDORF_HINT)->GetText().GetForLanguage(language);
jsonData["lightArrowHintLoc"] = GetLightArrowHintLoc();
jsonData["lightArrowArea"] = ::Hint(ctx->GetHint(RH_GANONDORF_HINT)->GetHintedArea()).GetText().GetEnglish();
jsonData["masterSwordHintLoc"] = GetMasterSwordHintLoc();
if (!ctx->GetOption(RSK_TRIAL_COUNT).Is(0)) {
jsonData["sheikText"] = ctx->GetHint(RH_SHEIK_LIGHT_ARROWS)->GetText().GetForLanguage(language);
}
}
if (ctx->GetOption(RSK_DAMPES_DIARY_HINT)){
jsonData["dampeText"] = ctx->GetHint(RH_DAMPES_DIARY)->GetText().GetForLanguage(language);
jsonData["dampeHintLoc"] = Rando::StaticData::GetLocation(ctx->GetHint(RH_DAMPES_DIARY)->GetHintedLocation())->GetName();
jsonData["dampeRegion"] = ::Hint(ctx->GetHint(RH_DAMPES_DIARY)->GetHintedArea()).GetText().GetEnglish();
}
if (ctx->GetOption(RSK_GREG_HINT)){
jsonData["gregText"] = ctx->GetHint(RH_GREG_RUPEE)->GetText().GetForLanguage(language);
jsonData["gregLoc"] = Rando::StaticData::GetLocation(ctx->GetHint(RH_GREG_RUPEE)->GetHintedLocation())->GetName();
jsonData["gregRegion"] = ::Hint(ctx->GetHint(RH_GREG_RUPEE)->GetHintedArea()).GetText().GetEnglish();
}
if (ctx->GetOption(RSK_SARIA_HINT)){
jsonData["sariaText"] = ctx->GetHint(RH_SARIA)->GetText().GetForLanguage(language);
jsonData["sariaHintLoc"] = Rando::StaticData::GetLocation(ctx->GetHint(RH_SARIA)->GetHintedLocation())->GetName();
jsonData["sariaRegion"] = ::Hint(ctx->GetHint(RH_SARIA)->GetHintedArea()).GetText().GetEnglish();
}
if (ctx->GetOption(RSK_FISHING_POLE_HINT)) {
jsonData["fishingPoleText"] = ctx->GetHint(RH_FISHING_POLE)->GetText().GetForLanguage(language);
jsonData["fishingPoleHintLoc"] = Rando::StaticData::GetLocation(ctx->GetHint(RH_FISHING_POLE)->GetHintedLocation())->GetName();
jsonData["fishingPoleRegion"] = ::Hint(ctx->GetHint(RH_FISHING_POLE)->GetHintedArea()).GetText().GetEnglish();
}
if (ctx->GetOption(RSK_GOSSIP_STONE_HINTS).Is(RO_GOSSIP_STONES_NONE)) {
return;
}
for (const RandomizerCheck key : Rando::StaticData::gossipStoneLocations) { //RANDOTODO should be merged with static hints, iterate over hint keys
Rando::Hint* hint = ctx->GetHint((RandomizerHintKey)(key - RC_COLOSSUS_GOSSIP_STONE + 1));
Rando::ItemLocation* hintedLocation = ctx->GetItemLocation(hint->GetHintedLocation());
std::string hintTextString = hint->GetText().GetForLanguage(language);
HintType hintType = hint->GetHintType();
std::string textStr = hintTextString;
std::string name = Rando::StaticData::GetLocation(key)->GetName();
jsonData["hints"][name]["hint"] = textStr;
jsonData["hints"][name]["distribution"] = hint->GetDistribution();
jsonData["hints"][name]["type"] = hintTypeNames[(int)hintType];
if (hintType == HINT_TYPE_ITEM_LOCATION || hintType == HINT_TYPE_ITEM_AREA || hintType == HINT_TYPE_WOTH) {
jsonData["hints"][name]["item"] = hintedLocation->GetPlacedItemName().GetEnglish();
jsonData["hints"][name]["location"] = Rando::StaticData::GetLocation(hintedLocation->GetRandomizerCheck())->GetName();
}
if (hintType != HINT_TYPE_TRIAL && hintType != HINT_TYPE_JUNK) {
jsonData["hints"][name]["area"] = ::Hint(hint->GetHintedArea()).GetText().Capitalize().GetEnglish();
}
}
}
static void WriteAllLocations() {
auto ctx = Rando::Context::GetInstance();
for (const RandomizerCheck key : ctx->allLocations) {
Rando::ItemLocation* location = ctx->GetItemLocation(key);
std::string placedItemName;
switch (ctx->GetOption(RSK_LANGUAGE).GetSelectedOptionIndex()) {
switch (gSaveContext.language) {
case 0:
default:
placedItemName = location->GetPlacedItemName().english;
@ -689,12 +575,12 @@ static void WriteAllLocations() {
jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()]["price"] =
location->GetPrice();
}
if (location->IsHintedAt()) {
if (location->IsAHintAccessible()) {
hintedLocations.emplace(Rando::StaticData::GetLocation(key)->GetHintKey(), location);
}
if (location->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
switch (ctx->GetOption(RSK_LANGUAGE).GetSelectedOptionIndex()) {
switch (gSaveContext.language) {
case 0:
default:
jsonData["locations"][Rando::StaticData::GetLocation(location->GetRandomizerCheck())->GetName()]["model"] =
@ -737,7 +623,7 @@ const char* SpoilerLog_Write() {
WriteSettings();
WriteExcludedLocations();
WriteStartingInventory();
WriteEnabledTricks(spoilerLog);
WriteEnabledTricks(spoilerLog); //RANDOTODO clean up spoilerLog refernces
//if (Settings::Logic.Is(LOGIC_GLITCHED)) {
// WriteEnabledGlitches(spoilerLog);
//}
@ -748,7 +634,7 @@ const char* SpoilerLog_Write() {
ctx->playthroughLocations.clear();
ctx->playthroughBeatable = false;
WriteHints();
ctx->WriteHintJson(jsonData);
WriteShuffledEntrances();
WriteAllLocations();

View File

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include "z64.h"
#include <stdint.h>
#define PLURAL 0
#define SINGULAR 1
@ -54,11 +54,11 @@ public:
const std::string& GetForLanguage(uint8_t language) const {
switch (language) {
case LANGUAGE_ENG:
case 0: //LANGUAGE_ENG: changed to resolve #include loops
return GetEnglish();
case LANGUAGE_FRA:
case 2: //LANGUAGE_FRA:
return GetFrench();
case LANGUAGE_GER:
case 1: //LANGUAGE_GER:
return GetGerman();
default:
return GetEnglish();
@ -97,6 +97,29 @@ public:
}
}
void Replace(std::string oldStr, Text newText) {
size_t position = english.find(oldStr);
while (position != std::string::npos) {
english.replace(position, oldStr.length(), newText.GetEnglish());
position = english.find(oldStr);
}
position = french.find(oldStr);
while (position != std::string::npos) {
french.replace(position, oldStr.length(), newText.GetFrench());
position = french.find(oldStr);
}
position = spanish.find(oldStr);
while (position != std::string::npos) {
spanish.replace(position, oldStr.length(), newText.GetSpanish());
position = spanish.find(oldStr);
}
position = german.find(oldStr);
while (position != std::string::npos) {
german.replace(position, oldStr.length(), newText.GetGerman());
position = german.find(oldStr);
}
}
// Convert first char to upper case
Text Capitalize(void) const {
Text cap = *this + "";

View File

@ -5,11 +5,11 @@
#include "3drando/shops.hpp"
#include "dungeon.h"
#include "logic.h"
#include "trial.h"
#include "entrance.h"
#include "settings.h"
#include "rando_hash.h"
#include "fishsanity.h"
#include "3drando/hints.hpp"
#include <fstream>
#include <spdlog/spdlog.h>
@ -20,56 +20,6 @@ namespace Rando {
std::weak_ptr<Context> Context::mContext;
Context::Context() {
mSpoilerfileHintTypeNameToEnum = {
{ "Static", HINT_TYPE_STATIC },
{ "Trial", HINT_TYPE_TRIAL },
{ "WotH", HINT_TYPE_WOTH },
{ "Barren", HINT_TYPE_BARREN },
{ "Entrance", HINT_TYPE_ENTRANCE },
{ "Item Area", HINT_TYPE_ITEM_AREA },
{ "Item Location", HINT_TYPE_ITEM_LOCATION },
{ "Junk", HINT_TYPE_JUNK },
};
mSpoilerfileAreaNameToEnum = {
{"No Hint", RA_NONE},
{"Link's Pocket", RA_LINKS_POCKET},
{"Kokiri Forest", RA_KOKIRI_FOREST},
{"The Lost Woods", RA_THE_LOST_WOODS},
{"Sacred Forest Meadow", RA_SACRED_FOREST_MEADOW},
{"Hyrule Field", RA_HYRULE_FIELD},
{"Lake Hylia", RA_LAKE_HYLIA},
{"Gerudo Valley", RA_GERUDO_VALLEY},
{"Gerudo Fortress", RA_GERUDO_FORTRESS},
{"Haunted Wasteland", RA_HAUNTED_WASTELAND},
{"Desert Colossus", RA_DESERT_COLOSSUS},
{"The Market", RA_THE_MARKET},
{"Temple of Time", RA_TEMPLE_OF_TIME},
{"Hyrule Castle", RA_HYRULE_CASTLE},
{"Outside Ganon's Castle", RA_OUTSIDE_GANONS_CASTLE},
{"Castle Grounds", RA_CASTLE_GROUNDS},
{"Kakariko Village", RA_KAKARIKO_VILLAGE},
{"the Graveyard", RA_THE_GRAVEYARD},
{"Death Mountain Trail", RA_DEATH_MOUNTAIN_TRAIL},
{"Goron City", RA_GORON_CITY},
{"Death Mountain Crater", RA_DEATH_MOUNTAIN_CRATER},
{"Zora's River", RA_ZORAS_RIVER},
{"Zora's Domain", RA_ZORAS_DOMAIN},
{"Zora's Fountain", RA_ZORAS_FOUNTAIN},
{"Lon Lon Ranch", RA_LON_LON_RANCH},
{"Deku Tree", RA_DEKU_TREE},
{"Dodongo's Cavern", RA_DODONGOS_CAVERN},
{"Jabu-Jabu's Belly", RA_JABU_JABUS_BELLY},
{"Forest Temple", RA_FOREST_TEMPLE},
{"Fire Temple", RA_FIRE_TEMPLE},
{"Water Temple", RA_WATER_TEMPLE},
{"Spirit Temple", RA_SPIRIT_TEMPLE},
{"Shadow Temple", RA_SHADOW_TEMPLE},
{"Bottom of the Well", RA_BOTTOM_OF_THE_WELL},
{"Ice Cavern", RA_ICE_CAVERN},
{"Gerudo training Grounds", RA_GERUDO_TRAINING_GROUND},
{"Inside Ganon's Castle", RA_GANONS_CASTLE},
};
for (int i = 0; i < RC_MAX; i++) {
itemLocationTable[i] = ItemLocation(static_cast<RandomizerCheck>(i));
@ -83,11 +33,15 @@ Context::Context() {
}
RandomizerArea Context::GetAreaFromString(std::string str) {
return mSpoilerfileAreaNameToEnum[str];
return (RandomizerArea)StaticData::areaNameToEnum[str];
}
void Context::InitStaticData() {
StaticData::InitItemTable();
StaticData::HintTable_Init();
StaticData::trialNameToEnum = StaticData::PopulateTranslationMap(StaticData::trialData);
StaticData::hintNameToEnum = StaticData::PopulateTranslationMap(StaticData::hintNames);
StaticData::hintTypeNameToEnum = StaticData::PopulateTranslationMap(StaticData::hintTypeNames);
StaticData::areaNameToEnum = StaticData::PopulateTranslationMap(StaticData::areaNames);
StaticData::InitLocationTable();
}
@ -104,14 +58,12 @@ std::shared_ptr<Context> Context::GetInstance() {
return mContext.lock();
}
Hint* Context::GetHint(const RandomizerHintKey hintKey) {
Hint* Context::GetHint(const RandomizerHint hintKey) {
return &hintTable[hintKey];
}
void Context::AddHint(const RandomizerHintKey hintId, const Text& text, const RandomizerCheck hintedLocation, const HintType hintType,
std::string distributionName, RandomizerArea hintedArea) {
hintTable[hintId] = Hint(text, hintedLocation, hintType, distributionName, hintedArea);
GetItemLocation(hintedLocation)->AddHintedBy(hintId);
void Context::AddHint(const RandomizerHint hintId, const Hint hint) {
hintTable[hintId] = hint; //RANDOTODO this should probably be an rvalue
}
ItemLocation* Context::GetItemLocation(const RandomizerCheck locKey) {
@ -222,7 +174,7 @@ std::vector<RandomizerCheck> Context::GetLocations(const std::vector<RandomizerC
}
void Context::ClearItemLocations() {
for (int i = 0; i < itemLocationTable.size(); i++) {
for (size_t i = 0; i < itemLocationTable.size(); i++) {
GetItemLocation(static_cast<RandomizerCheck>(i))->ResetVariables();
}
}
@ -250,7 +202,7 @@ void Context::LocationReset() {
GetItemLocation(il)->RemoveFromPool();
}
for (const RandomizerCheck il : StaticData::otherHintLocations) {
for (const RandomizerCheck il : StaticData::staticHintLocations) {
GetItemLocation(il)->RemoveFromPool();
}
}
@ -258,7 +210,9 @@ void Context::LocationReset() {
void Context::HintReset() {
for (const RandomizerCheck il : StaticData::gossipStoneLocations) {
GetItemLocation(il)->ResetVariables();
GetHint(static_cast<RandomizerHintKey>(il - RC_COLOSSUS_GOSSIP_STONE + 1))->ResetVariables();
}
for (Hint& hint : hintTable){
hint.ResetVariables();
}
}
@ -393,126 +347,52 @@ void Context::ParseHashIconIndexesJson(nlohmann::json spoilerFileJson) {
void Context::ParseItemLocationsJson(nlohmann::json spoilerFileJson) {
nlohmann::json locationsJson = spoilerFileJson["locations"];
for (auto it = locationsJson.begin(); it != locationsJson.end(); ++it) {
RandomizerCheck rc = StaticData::SpoilerfileCheckNameToEnum[it.key()];
RandomizerCheck rc = StaticData::locationNameToEnum[it.key()];
if (it->is_structured()) {
nlohmann::json itemJson = *it;
for (auto itemit = itemJson.begin(); itemit != itemJson.end(); ++itemit) {
if (itemit.key() == "item") {
itemLocationTable[rc].SetPlacedItem(StaticData::SpoilerfileItemNameToEnum[itemit.value().get<std::string>()]);
itemLocationTable[rc].SetPlacedItem(StaticData::itemNameToEnum[itemit.value().get<std::string>()]);
} else if (itemit.key() == "price") {
itemLocationTable[rc].SetCustomPrice(itemit.value().get<uint16_t>());
} else if (itemit.key() == "model") {
overrides[rc] = ItemOverride(rc, StaticData::SpoilerfileItemNameToEnum[itemit.value().get<std::string>()]);
overrides[rc] = ItemOverride(rc, StaticData::itemNameToEnum[itemit.value().get<std::string>()]);
} else if (itemit.key() == "trickName") {
overrides[rc].SetTrickName(Text(itemit.value().get<std::string>()));
}
}
} else {
itemLocationTable[rc].SetPlacedItem(StaticData::SpoilerfileItemNameToEnum[it.value().get<std::string>()]);
itemLocationTable[rc].SetPlacedItem(StaticData::itemNameToEnum[it.value().get<std::string>()]);
}
}
}
void Context::WriteHintJson(nlohmann::ordered_json& spoilerFileJson){
for (Hint hint: hintTable){
hint.logHint(spoilerFileJson);
}
}
nlohmann::json getValueForMessage(std::unordered_map<std::string, nlohmann::json> map, CustomMessage message){
std::vector<std::string> strings = message.GetAllMessages();
for (uint8_t language = 0; language < LANGUAGE_MAX; language++){
if (map.contains(strings[language])){
return strings[language];
}
}
return {};
}
void Context::ParseHintJson(nlohmann::json spoilerFileJson) {
// Child Altar
std::string childAltarText = spoilerFileJson["childAltar"]["hintText"].get<std::string>();
AddHint(RH_ALTAR_CHILD, Text(childAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", RA_NONE);
mEmeraldLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["emeraldLoc"]];
mRubyLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["rubyLoc"]];
mSapphireLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["childAltar"]["rewards"]["sapphireLoc"]];
// Adult Altar
std::string adultAltarText = spoilerFileJson["adultAltar"]["hintText"].get<std::string>();
AddHint(RH_ALTAR_ADULT, Text(adultAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", RA_NONE);
mForestMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["forestMedallionLoc"].get<std::string>()];
mFireMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["fireMedallionLoc"].get<std::string>()];
mWaterMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["waterMedallionLoc"].get<std::string>()];
mShadowMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["shadowMedallionLoc"].get<std::string>()];
mSpiritMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["spiritMedallionLoc"].get<std::string>()];
mLightMedallionLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["adultAltar"]["rewards"]["lightMedallionLoc"].get<std::string>()];
// Ganondorf and Sheik Light Arrow Hints
std::string ganonHintText = spoilerFileJson["ganonHintText"].get<std::string>();
RandomizerCheck lightArrowLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["lightArrowHintLoc"].get<std::string>()];
std::string lightArrowRegion = spoilerFileJson["lightArrowArea"].get<std::string>();
AddHint(RH_GANONDORF_HINT, Text(ganonHintText), lightArrowLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[lightArrowRegion]);
if (spoilerFileJson.contains("sheikText")) {
std::string sheikText = spoilerFileJson["sheikText"].get<std::string>();
AddHint(RH_SHEIK_LIGHT_ARROWS, Text(sheikText), lightArrowLoc, HINT_TYPE_STATIC, lightArrowRegion);
for (auto hintData : spoilerFileJson["Gossip Stone Hints"].items()){
RandomizerHint hint = (RandomizerHint)StaticData::hintNameToEnum[hintData.key()];
AddHint(hint, Hint(hint, hintData.value()));
}
std::string ganonText = spoilerFileJson["ganonText"].get<std::string>();
AddHint(RH_GANONDORF_NOHINT, Text(ganonText), RC_UNKNOWN_CHECK, HINT_TYPE_JUNK, "Static", RA_GANONS_CASTLE);
// Dampe Hookshot Hint
if (spoilerFileJson.contains("dampeText")) {
std::string dampeText = spoilerFileJson["dampeText"].get<std::string>();
std::string dampeRegion = spoilerFileJson["dampeRegion"].get<std::string>();
RandomizerCheck dampeHintLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["dampeHintLoc"].get<std::string>()];
AddHint(RH_DAMPES_DIARY, Text(dampeText), dampeHintLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[dampeRegion]);
}
// Greg Hint
if (spoilerFileJson.contains("gregText")) {
std::string gregText = spoilerFileJson["gregText"].get<std::string>();
std::string gregRegion = spoilerFileJson["gregRegion"].get<std::string>();
RandomizerCheck gregLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["gregLoc"].get<std::string>()];
AddHint(RH_GREG_RUPEE, Text(gregText), gregLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[gregRegion]);
}
// Saria Magic Hint
if (spoilerFileJson.contains("sariaText")) {
std::string sariaText = spoilerFileJson["sariaText"].get<std::string>();
std::string sariaRegion = spoilerFileJson["sariaRegion"].get<std::string>();
RandomizerCheck sariaHintLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["sariaHintLoc"].get<std::string>()];
AddHint(RH_SARIA, Text(sariaText), sariaHintLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[sariaRegion]);
}
// Fishing Pole Hint
if(spoilerFileJson.contains("fishingPoleText")) {
std::string fishingPoleText = spoilerFileJson["fishingPoleText"].get<std::string>();
std::string fishingPoleRegion = spoilerFileJson["fishingPoleRegion"].get<std::string>();
RandomizerCheck fishingPoleHintLoc = StaticData::SpoilerfileCheckNameToEnum[spoilerFileJson["fishingPoleHintLoc"].get<std::string>()];
AddHint(RH_FISHING_POLE, Text(fishingPoleText), fishingPoleHintLoc, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[fishingPoleRegion]);
}
// Warp Songs
if (spoilerFileJson.contains("warpMinuetText")) {
std::string warpMinuetText = spoilerFileJson["warpMinuetText"].get<std::string>(); //RANDOTODO fall back for if location is used
AddHint(RH_MINUET_WARP_LOC, Text(warpMinuetText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpMinuetText]);
}
if (spoilerFileJson.contains("warpBoleroText")) {
std::string warpBoleroText = spoilerFileJson["warpBoleroText"].get<std::string>();
AddHint(RH_BOLERO_WARP_LOC, Text(warpBoleroText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpBoleroText]);
}
if (spoilerFileJson.contains("warpSerenadeText")) {
std::string warpSerenadeText = spoilerFileJson["warpSerenadeText"].get<std::string>();
AddHint(RH_SERENADE_WARP_LOC, Text(warpSerenadeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpSerenadeText]);
}
if (spoilerFileJson.contains("warpRequiemText")) {
std::string warpRequiemText = spoilerFileJson["warpRequiemText"].get<std::string>();
AddHint(RH_REQUIEM_WARP_LOC, Text(warpRequiemText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpRequiemText]);
}
if (spoilerFileJson.contains("warpNocturneText")) {
std::string warpNocturneText = spoilerFileJson["warpNocturneText"].get<std::string>();
AddHint(RH_NOCTURNE_WARP_LOC, Text(warpNocturneText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpNocturneText]);
}
if (spoilerFileJson.contains("warpPreludeText")) {
std::string warpPreludeText = spoilerFileJson["warpPreludeText"].get<std::string>();
AddHint(RH_PRELUDE_WARP_LOC, Text(warpPreludeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", mSpoilerfileAreaNameToEnum[warpPreludeText]);
}
// Gossip Stones
nlohmann::json hintsJson = spoilerFileJson["hints"];
for (auto it = hintsJson.begin(); it != hintsJson.end(); ++it) {
RandomizerCheck gossipStoneLoc = StaticData::SpoilerfileCheckNameToEnum[it.key()];
nlohmann::json hintInfo = it.value();
std::string hintText = hintInfo["hint"].get<std::string>();
HintType hintType = mSpoilerfileHintTypeNameToEnum[hintInfo["type"].get<std::string>()];
RandomizerCheck hintedLocation = hintInfo.contains("location") ? StaticData::SpoilerfileCheckNameToEnum[hintInfo["location"]] : RC_UNKNOWN_CHECK;
RandomizerArea hintedArea = hintInfo.contains("area") ? mSpoilerfileAreaNameToEnum[hintInfo["area"].get<std::string>()] : RA_NONE;
std::string distribution = hintInfo["distribution"].get<std::string>();
AddHint(static_cast<RandomizerHintKey>(gossipStoneLoc - RC_COLOSSUS_GOSSIP_STONE + 1), Text(hintText), hintedLocation, hintType, distribution, hintedArea);
for (auto hintData : spoilerFileJson["Static Hints"].items()){
RandomizerHint hint = (RandomizerHint)StaticData::hintNameToEnum[hintData.key()];
AddHint(hint, Hint(hint, hintData.value()));
}
CreateStaticHints();
}
std::shared_ptr<Settings> Context::GetSettings() {
@ -554,6 +434,10 @@ TrialInfo* Context::GetTrial(size_t key) const {
return mTrials->GetTrial(static_cast<TrialKey>(key));
}
TrialInfo* Context::GetTrial(TrialKey key) const {
return mTrials->GetTrial(key);
}
Sprite* Context::GetSeedTexture(const uint8_t index) {
return &gSeedTextures[index];
}

View File

@ -7,6 +7,7 @@
#include "3drando/text.hpp"
#include "hint.h"
#include "fishsanity.h"
#include "trial.h"
#include <memory>
#include <array>
@ -36,9 +37,8 @@ class Context {
static std::shared_ptr<Context> CreateInstance();
static std::shared_ptr<Context> GetInstance();
void InitStaticData();
Hint* GetHint(RandomizerHintKey hintKey);
void AddHint(RandomizerHintKey hintId, const Text& text, RandomizerCheck hintedLocation, HintType hintType, std::string distributionName,
RandomizerArea hintedArea = RA_NONE);
Hint* GetHint(RandomizerHint hintKey);
void AddHint(const RandomizerHint hintId, const Hint hint);
ItemLocation* GetItemLocation(RandomizerCheck locKey);
ItemLocation* GetItemLocation(size_t locKey);
ItemOverride& GetItemOverride(RandomizerCheck locKey);
@ -73,6 +73,7 @@ class Context {
void ResetLogic();
std::shared_ptr<Trials> GetTrials();
TrialInfo* GetTrial(size_t key) const;
TrialInfo* GetTrial(TrialKey key) const;
static Sprite* GetSeedTexture(uint8_t index);
Option& GetOption(RandomizerSettingKey key) const;
TrickOption& GetTrickOption(RandomizerTrick key) const;
@ -80,6 +81,7 @@ class Context {
void ParseSpoiler(const char* spoilerFileName, bool plandoMode);
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
void ParseItemLocationsJson(nlohmann::json spoilerFileJson);
void WriteHintJson(nlohmann::ordered_json& spoilerFileJson);
void ParseHintJson(nlohmann::json spoilerFileJson);
std::map<RandomizerCheck, ItemOverride> overrides = {};
std::vector<std::vector<RandomizerCheck>> playthroughLocations = {};
@ -93,18 +95,7 @@ class Context {
private:
static std::weak_ptr<Context> mContext;
std::unordered_map<std::string, HintType> mSpoilerfileHintTypeNameToEnum;
std::unordered_map<std::string, RandomizerArea> mSpoilerfileAreaNameToEnum;
std::array<Hint, RH_MAX> hintTable = {};
RandomizerCheck mEmeraldLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mRubyLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mSapphireLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mForestMedallionLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mFireMedallionLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mWaterMedallionLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mShadowMedallionLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mSpiritMedallionLoc = RC_UNKNOWN_CHECK;
RandomizerCheck mLightMedallionLoc = RC_UNKNOWN_CHECK;
std::array<ItemLocation, RC_MAX> itemLocationTable = {};
std::shared_ptr<Settings> mSettings;
std::shared_ptr<EntranceShuffler> mEntranceShuffler;

View File

@ -1,56 +1,729 @@
#include "hint.h"
#include "map"
#include "string"
#include "context.h"
#include <spdlog/spdlog.h>
#include "static_data.h"
namespace Rando {
Hint::Hint() : text(std::move(Text())) {}
Hint::Hint(Text text_): text(std::move(text_)) {}
Hint::Hint(Text text_, RandomizerCheck hintedLocation_, HintType hintType_, std::string distributionName_, RandomizerArea hintedArea_)
: text(std::move(text_)), hintedLocation(hintedLocation_), hintType(hintType_),
hintedArea(hintedArea_), distribution(std::move(distributionName_)) {
Hint::Hint(){}
Hint::Hint(RandomizerHint ownKey_,
HintType hintType_,
std::string distribution_,
std::vector<RandomizerHintTextKey> hintKeys_,
std::vector<RandomizerCheck> locations_,
std::vector<RandomizerArea> areas_,
std::vector<TrialKey> trials_)
: ownKey(ownKey_), hintType(hintType_), distribution(std::move(distribution_)), hintKeys(hintKeys_), locations(locations_), areas(areas_), trials(trials_) {
FillGapsInData();
SetLocationsAsHinted();
NamesChosen();
enabled = true;
}
const Text& Hint::GetText() const {
return text;
Hint::Hint(RandomizerHint ownKey_,
HintType hintType_,
std::vector<RandomizerHintTextKey> hintKeys_,
std::vector<RandomizerCheck> locations_,
std::vector<RandomizerArea> areas_,
std::vector<TrialKey> trials_,
bool yourPocket_,
int num_)
: ownKey(ownKey_), hintType(hintType_), hintKeys(hintKeys_), locations(locations_), areas(areas_), trials(trials_), yourPocket(yourPocket_), num(num_) {
FillGapsInData();
SetLocationsAsHinted();
NamesChosen();
enabled = true;
}
void Hint::SetHintedLocation(RandomizerCheck location) {
hintedLocation = location;
Hint::Hint(RandomizerHint ownKey_, std::vector<CustomMessage> messages_)
: ownKey(ownKey_), messages(messages_){
hintType = HINT_TYPE_MESSAGE;
enabled = true;
}
RandomizerCheck Hint::GetHintedLocation() {
return hintedLocation;
Hint::Hint(RandomizerHint ownKey_, nlohmann::json json_){
ownKey = ownKey_;
if (json_.contains("enabled") && !json_["enabled"].get<bool>()){
return;
}
enabled = true;
if (json_.contains("type")){
hintType = (HintType)StaticData::hintTypeNameToEnum[json_["type"].get<std::string>()];
}
if (hintType == HINT_TYPE_MESSAGE){
if (json_.contains("messages")){
for (auto message: json_["messages"]){
messages.push_back(CustomMessage(message.get<std::string>()));
}
} else if (json_.contains("message")){
messages.push_back(CustomMessage(json_["message"].get<std::string>()));
}
}
if (json_.contains("distribution")){
distribution = json_["distribution"].get<std::string>();
}
if (json_.contains("locations")){
for (auto loc: json_["locations"]){
locations.push_back(StaticData::locationNameToEnum[loc.get<std::string>()]);
}
} else if (json_.contains("location")){
locations.push_back(StaticData::locationNameToEnum[json_["location"].get<std::string>()]);
}
if (json_.contains("itemNamesChosen")){
for (auto name: json_["itemNamesChosen"]){
itemNamesChosen.push_back(name.get<uint8_t>());
}
} else if (json_.contains("itemNameChosen")){
itemNamesChosen.push_back(json_["itemNameChosen"].get<uint8_t>());
}
if (json_.contains("areas")){
for (auto area: json_["areas"]){
areas.push_back((RandomizerArea)Rando::StaticData::areaNameToEnum[area]);
}
} else if (json_.contains("area")){
areas.push_back((RandomizerArea)Rando::StaticData::areaNameToEnum[json_["area"]]);
}
if (json_.contains("areaNamesChosen")){
for (auto name: json_["areaNamesChosen"]){
areaNamesChosen.push_back(name.get<uint8_t>());
}
} else if (json_.contains("areaNameChosen")){
areaNamesChosen.push_back(json_["areaNameChosen"].get<uint8_t>());
}
if (json_.contains("trials")){
for (auto trial: json_["trials"]){
trials.push_back((TrialKey)Rando::StaticData::trialNameToEnum[trial]);
}
} else if (json_.contains("trial")){
trials.push_back((TrialKey)Rando::StaticData::trialNameToEnum[json_["trial"]]);
}
if (json_.contains("hintKeys")){
for (auto hintKey: json_["hintKeys"]){
hintKeys.push_back((RandomizerHintTextKey)hintKey.get<uint32_t>());
}
} else if (json_.contains("hintKey")){
hintKeys.push_back((RandomizerHintTextKey)json_["hintKey"].get<uint32_t>());
}
if (json_.contains("hintTextsChosen")){
for (auto name: json_["hintTextsChosen"]){
hintTextsChosen.push_back(name.get<uint8_t>());
}
} else if (json_.contains("hintTextChosen")){
hintTextsChosen.push_back(json_["hintTextChosen"].get<uint8_t>());
}
if (json_.contains("num")){
num = json_["num"].get<int>();
}
FillGapsInData();
SetLocationsAsHinted();
}
void Hint::FillGapsInData(){
auto ctx = Rando::Context::GetInstance();
if (locations.size() == 0 && StaticData::staticHintInfoMap.contains(ownKey)){
locations = StaticData::staticHintInfoMap[ownKey].targetChecks;
}
bool fillAreas = true;
bool fillItems = true;
if (areas.size() > 0){
fillAreas = false;
}
if (items.size() > 0){
fillItems = false;
}
for(uint8_t c = 0; c < locations.size(); c++){
if (fillAreas){
areas.push_back(ctx->GetItemLocation(locations[c])->GetArea());
}
if (fillItems){
items.push_back(ctx->GetItemLocation(locations[c])->GetPlacedRandomizerGet());
}
}
}
void Hint::SetLocationsAsHinted() const {
auto ctx = Rando::Context::GetInstance();
for (uint8_t count = 0; count < locations.size(); count++){
ctx->GetItemLocation(locations[count])->AddHintedBy(ownKey);
}
}
uint8_t GetRandomHintTextEntry(const HintText hintText){
auto ctx = Rando::Context::GetInstance();
uint8_t size = 0;
if (ctx->GetOption(RSK_HINT_CLARITY).Is(RO_HINT_CLARITY_AMBIGUOUS)){
size = hintText.GetAmbiguousSize();
} else if (ctx->GetOption(RSK_HINT_CLARITY).Is(RO_HINT_CLARITY_OBSCURE)){
size = hintText.GetObscureSize();
}
if (size > 0){
return Random(0, size);
}
return 0;
}
void Hint::NamesChosen(){
auto ctx = Rando::Context::GetInstance();
std::vector<uint8_t> namesTemp = {};
bool saveNames = false;
uint8_t numMessages = GetNumberOfMessages();
for (uint8_t c = 0; c < numMessages; c++){
uint8_t selection = GetRandomHintTextEntry(GetHintText(c));
if (selection > 0){
saveNames = true;
}
namesTemp.push_back(selection);
}
if (saveNames) {
hintTextsChosen = namesTemp;
}
if (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_ITEM_AREA || hintType == HINT_TYPE_MERCHANT){
bool mysterious = hintType == HINT_TYPE_MERCHANT && ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ON_NO_HINT);
for(uint8_t c = 0; c < locations.size(); c++){
namesTemp = {};
saveNames = false;
uint8_t selection = GetRandomHintTextEntry(GetItemHintText(c, mysterious));
if (selection > 0){
saveNames = true;
}
namesTemp.push_back(selection);
}
if (saveNames) {
itemNamesChosen = namesTemp;
}
}
if (hintType == HINT_TYPE_FOOLISH || hintType == HINT_TYPE_ITEM_AREA || hintType == HINT_TYPE_WOTH ||
hintType == HINT_TYPE_ALTAR_CHILD || hintType == HINT_TYPE_ALTAR_ADULT){
namesTemp = {};
saveNames = false;
for(uint8_t c = 0; c < areas.size(); c++){
uint8_t selection = GetRandomHintTextEntry(GetAreaHintText(c));
if (selection > 0){
saveNames = true;
}
namesTemp.push_back(selection);
}
if (saveNames) {
areaNamesChosen = namesTemp;
}
}
}
uint8_t Hint::GetNumberOfMessages() const {
size_t numMessages = std::max(messages.size(), hintKeys.size());
if (StaticData::staticHintInfoMap.contains(ownKey)){
numMessages = std::max(StaticData::staticHintInfoMap[ownKey].hintKeys.size(), numMessages);
}
if (numMessages == 0){
numMessages = 1; //RANDOTODO make std::max actually fucking work for 3 arguments
}
return numMessages;
}
const std::vector<std::string> Hint::GetAllMessageStrings(MessageFormat format) const {
std::vector<std::string> hintMessages = {};
uint8_t numMessages = GetNumberOfMessages();
for (int c = 0; c < numMessages; c++){
hintMessages.push_back(GetHintMessage(format, c).GetForCurrentLanguage(format));
}
return hintMessages;
}
const HintText Hint::GetHintText(uint8_t id) const {
auto ctx = Rando::Context::GetInstance();
if (hintKeys.size() > id){
return StaticData::hintTextTable[hintKeys[id]];
}
// If a static hint, load default from staticHintInfoMap
if (StaticData::staticHintInfoMap.contains(ownKey) && StaticData::staticHintInfoMap[ownKey].hintKeys.size() > id){
return StaticData::hintTextTable[StaticData::staticHintInfoMap[ownKey].hintKeys[id]];
}
switch (hintType){
case HINT_TYPE_HINT_KEY:
return StaticData::hintTextTable[0];
break;
case HINT_TYPE_TRIAL:
if (ctx->GetTrial(trials[0])->IsRequired()) {
return StaticData::hintTextTable[RHT_TRIAL_ON];
} else {
return StaticData::hintTextTable[RHT_TRIAL_OFF];
}
case HINT_TYPE_WOTH:
return StaticData::hintTextTable[RHT_WAY_OF_THE_HERO];
case HINT_TYPE_FOOLISH:
return StaticData::hintTextTable[RHT_FOOLISH];
case HINT_TYPE_ITEM:
if (locations.size() > 0) {
return *StaticData::GetLocation(locations[0])->GetHint();
} else {
return CustomMessage("ERROR: ITEM HINT WITH NO LOCATIONS OR HINT KEY");
}
case HINT_TYPE_ITEM_AREA:
if (locations.size() > 0) {
if (StaticData::GetLocation(locations[0])->IsDungeon()) {
return StaticData::hintTextTable[RHT_HOARDS];
} else {
return StaticData::hintTextTable[RHT_CAN_BE_FOUND_AT];
}
} else {
return CustomMessage("ERROR: ITEM AREA HINT WITH NO LOCATION"); //RANDOTODO get isDungeon from area?
}
default:
return CustomMessage("ERROR: NO HINTKEY PROVIDED AND HINT TYPE HAS NO DEFAULT");
}
}
const CustomMessage Hint::GetHintMessage(MessageFormat format, uint8_t id) const {
auto ctx = Rando::Context::GetInstance();
CustomMessage hintText = CustomMessage("");
uint8_t chosenMessage = 0;
if (hintTextsChosen.size() > id){
chosenMessage = id;
}
if (hintType == HINT_TYPE_MESSAGE){
if (id < messages.size()){
hintText = messages[id];
}
} else if (hintType == HINT_TYPE_ALTAR_CHILD){
if (ctx->GetOption(RSK_TOT_ALTAR_HINT)){
hintText = StaticData::hintTextTable[RHT_CHILD_ALTAR_STONES].GetHintMessage();
}
if (ctx->GetOption(RSK_DOOR_OF_TIME).Is(RO_DOOROFTIME_OPEN)) {
hintText += CustomMessage(StaticData::hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTOPEN].GetHintMessage());
} else if (ctx->GetOption(RSK_DOOR_OF_TIME).Is(RO_DOOROFTIME_SONGONLY)) {
hintText += CustomMessage(StaticData::hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTSONGONLY].GetHintMessage());
} else {
hintText += CustomMessage(StaticData::hintTextTable[RHT_CHILD_ALTAR_TEXT_END_DOTCLOSED].GetHintMessage());
}
} else if (hintType == HINT_TYPE_ALTAR_ADULT){
if (ctx->GetOption(RSK_TOT_ALTAR_HINT)){
hintText = StaticData::hintTextTable[RHT_ADULT_ALTAR_MEDALLIONS].GetHintMessage();
}
hintText += GetBridgeReqsText() + GetGanonBossKeyText() + StaticData::hintTextTable[RHT_ADULT_ALTAR_TEXT_END].GetHintMessage();
} else {
hintText = GetHintText(id).GetHintMessage(chosenMessage);
}
std::vector<CustomMessage> toInsert = {};
switch (hintType){
case HINT_TYPE_ITEM:{
//if we write items
for(uint8_t b = 0; b < locations.size(); b++){
toInsert.push_back(GetItemName(b));
}
break;}
case HINT_TYPE_MERCHANT:{
//if we write items, but need to adjust for merchants
bool mysterious = hintType == HINT_TYPE_MERCHANT && ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ON_NO_HINT);
for(uint8_t b = 0; b < locations.size(); b++){
toInsert.push_back(GetItemName(b, mysterious));
}
break;}
case HINT_TYPE_TRIAL:{
//If we write trials
for(uint8_t b = 0; b < trials.size(); b++){
toInsert.push_back(ctx->GetTrial(trials[b])->GetName());
}
break;}
case HINT_TYPE_ITEM_AREA:{
//If we write items and areas
for(uint8_t b = 0; b < items.size(); b++){
toInsert.push_back(GetItemName(b));
toInsert.push_back(GetAreaName(b));
}
break;}
case HINT_TYPE_ALTAR_CHILD:
case HINT_TYPE_ALTAR_ADULT:
case HINT_TYPE_AREA:
case HINT_TYPE_WOTH:
case HINT_TYPE_FOOLISH:{
//If we write areas
for(uint8_t b = 0; b < areas.size(); b++){
toInsert.push_back(GetAreaName(b));
}
break;}
default:
break;
}
hintText.InsertNames(toInsert);
if (num != 0){
hintText.InsertNumber(num);
}
if (format == MF_FORMATTED){
hintText.Format();
} else if (format == MF_AUTO_FORMAT){
hintText.AutoFormat();
} else if (format == MF_CLEAN){
hintText.Clean();
}
return hintText;
}
oJson Hint::toJSON() {
auto ctx = Rando::Context::GetInstance();
nlohmann::ordered_json log = {};
if (enabled){
log["type"] = StaticData::hintTypeNames[hintType].GetForCurrentLanguage(MF_CLEAN);
std::vector<std::string> hintMessages = GetAllMessageStrings(MF_CLEAN);
if (hintMessages.size() == 1){
log["message"] = hintMessages[0];
} else if (hintMessages.size() > 1){
log["messages"] = hintMessages;
}
if (distribution != ""){
log["distribution"] = distribution;
}
if (hintType != HINT_TYPE_FOOLISH){
if (!(StaticData::staticHintInfoMap.contains(ownKey) &&
StaticData::staticHintInfoMap[ownKey].targetChecks.size() > 0)){
if (locations.size() == 1){
log["location"] = StaticData::GetLocation(locations[0])->GetName();//RANDOTODO change to CustomMessage when VB is done;
} else if (locations.size() > 1){
//If we have defaults, no need to write more
std::vector<std::string> locStrings = {};
for (size_t c = 0; c < locations.size(); c++){
locStrings.push_back(StaticData::GetLocation(locations[c])->GetName());//RANDOTODO change to CustomMessage when VB is done
}
log["locations"] = locStrings;
}
}
if (!(StaticData::staticHintInfoMap.contains(ownKey) &&
StaticData::staticHintInfoMap[ownKey].targetItems.size() > 0)){
if (items.size() == 1){
log["item"] = StaticData::GetItemTable()[items[0]].GetName().GetEnglish();//RANDOTODO change to CustomMessage;
} else if (items.size() > 1){
std::vector<std::string> itemStrings = {};
for (size_t c = 0; c < items.size(); c++){
itemStrings.push_back(StaticData::GetItemTable()[items[c]].GetName().GetEnglish());//RANDOTODO change to CustomMessage
}
log["items"] = itemStrings;
}
}
if (itemNamesChosen.size() == 1){
log["itemNameChosen"] = itemNamesChosen[0];
} else if (itemNamesChosen.size() > 1){
std::vector<uint8_t> nameNums = {};
for (size_t c = 0; c < itemNamesChosen.size(); c++){
nameNums.push_back(itemNamesChosen[c]);
}
log["itemNamesChosen"] = nameNums;
}
}
if (areas.size() == 1){
log["area"] = StaticData::hintTextTable[StaticData::areaNames[areas[0]]].GetClear().GetForCurrentLanguage(MF_CLEAN);
} else if (areas.size() > 0 &&
!(StaticData::staticHintInfoMap.contains(ownKey) && StaticData::staticHintInfoMap[ownKey].targetChecks.size() > 0)){
// If we got locations from defaults, areas are derived from them and don't need logging
std::vector<std::string> areaStrings = {};
for (size_t c = 0; c < areas.size(); c++){
areaStrings.push_back(StaticData::hintTextTable[StaticData::areaNames[areas[c]]].GetClear().GetForCurrentLanguage(MF_CLEAN));
}
log["areas"] = areaStrings;
}
if (areaNamesChosen.size() == 1){
log["areaNameChosen"] = areaNamesChosen[0];
} else if (areaNamesChosen.size() > 1){
std::vector<uint8_t> nameNums = {};
for (size_t c = 0; c < areaNamesChosen.size(); c++){
nameNums.push_back(areaNamesChosen[c]);
}
log["areaNamesChosen"] = nameNums;
}
if (trials.size() == 1){
log["trial"] = ctx->GetTrial(trials[0])->GetName().GetForCurrentLanguage(MF_CLEAN);
} else if (trials.size() > 0){
std::vector<std::string> trialStrings = {};
for (size_t c = 0; c < trials.size(); c++){
trialStrings.push_back(ctx->GetTrial(trials[c])->GetName().GetForCurrentLanguage(MF_CLEAN));
}
log["trials"] = trialStrings;
}
if (hintKeys.size() == 1){
log["hintKey"] = hintKeys[0];
} else if (hintKeys.size() > 1){
std::vector<uint32_t> hintKeyNums = {};
for (size_t c = 0; c < hintKeys.size(); c++){
hintKeyNums.push_back(hintKeys[c]);
}
log["hintKeys"] = hintKeyNums;
}
if (hintTextsChosen.size() == 1){
log["hintTextChosen"] = hintTextsChosen[0];
} else if (hintTextsChosen.size() > 1){
std::vector<uint8_t> nameNums = {};
for (size_t c = 0; c < hintTextsChosen.size(); c++){
nameNums.push_back(hintTextsChosen[c]);
}
log["hintTextsChosen"] = nameNums;
}
if (num != 0){
log["num"] = num;
}
}
return log;
}
void Hint::logHint(oJson& jsonData){
auto ctx = Rando::Context::GetInstance();
std::string logMap = "Static Hints";
bool staticHint = true;
if (ownKey < RH_GANONDORF_HINT){
logMap = "Gossip Stone Hints";
staticHint = false;
}
if (enabled &&
(!(staticHint && (hintType == HINT_TYPE_ITEM || hintType == HINT_TYPE_MERCHANT) && ctx->GetOption(RSK_HINT_CLARITY).Is(RO_HINT_CLARITY_CLEAR)))){
//skip if not enabled or if a static hint with no possible variance
jsonData[logMap][Rando::StaticData::hintNames[ownKey].GetForCurrentLanguage(MF_CLEAN)] = toJSON();
}
}
const HintText Hint::GetItemHintText(uint8_t slot, bool mysterious) const {
//RANDOTODO make unreliant on locations, or make Hint accept ItemLocation
auto ctx = Rando::Context::GetInstance();
RandomizerCheck hintedCheck = locations[slot];
RandomizerGet targetRG = ctx->GetItemLocation(hintedCheck)->GetPlacedRandomizerGet();
if (mysterious){
return StaticData::hintTextTable[RHT_MYSTERIOUS_ITEM];
} else if (!ctx->GetOption(RSK_HINT_CLARITY).Is(RO_HINT_CLARITY_AMBIGUOUS) && targetRG == RG_ICE_TRAP) { //RANDOTODO store in item hint instead of item
return HintText(CustomMessage({ctx->overrides[hintedCheck].GetTrickName()}));
} else {
return ctx->GetItemLocation(hintedCheck)->GetPlacedItem().GetHint();
}
}
const HintText Hint::GetAreaHintText(uint8_t slot) const {
CustomMessage areaText;
if (yourPocket && (areas[slot] == RA_LINKS_POCKET || areas[slot] == RA_NONE)){
return StaticData::hintTextTable[RHT_YOUR_POCKET];
} else {
return StaticData::hintTextTable[Rando::StaticData::areaNames[areas[slot]]];
}
}
const CustomMessage Hint::GetItemName(uint8_t slot, bool mysterious) const {
uint8_t nameNum = 0;
if (itemNamesChosen.size() > slot){
nameNum = itemNamesChosen[slot];
}
return GetItemHintText(slot, mysterious).GetHintMessage(nameNum);
}
const CustomMessage Hint::GetAreaName(uint8_t slot) const {
uint8_t nameNum = 0;
if (areaNamesChosen.size() > slot){
nameNum = areaNamesChosen[slot];
}
return GetAreaHintText(slot).GetHintMessage(nameNum);
}
CustomMessage Hint::GetBridgeReqsText() {
auto ctx = Rando::Context::GetInstance();
CustomMessage bridgeMessage;
if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_ALWAYS_OPEN)) {
return StaticData::hintTextTable[RHT_BRIDGE_OPEN_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_VANILLA)) {
return StaticData::hintTextTable[RHT_BRIDGE_VANILLA_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_STONES)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_STONES_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_STONE_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_MEDALLIONS)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_MEDALLIONS_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEON_REWARDS)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_REWARDS_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_REWARD_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_DUNGEONS)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_DUNGEONS_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_TOKENS)) {
bridgeMessage = StaticData::hintTextTable[RHT_BRIDGE_TOKENS_HINT].GetHintMessage();
bridgeMessage.InsertNumber(ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_RAINBOW_BRIDGE).Is(RO_BRIDGE_GREG)) {
return StaticData::hintTextTable[RHT_BRIDGE_GREG_HINT].GetHintMessage();
}
return bridgeMessage;
}
CustomMessage Hint::GetGanonBossKeyText() {
auto ctx = Rando::Context::GetInstance();
CustomMessage ganonBossKeyMessage;
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH)) {
return StaticData::hintTextTable[RHT_GANON_BK_START_WITH_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_VANILLA)) {
return StaticData::hintTextTable[RHT_GANON_BK_VANILLA_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_OWN_DUNGEON)) {
return StaticData::hintTextTable[RHT_GANON_BK_OWN_DUNGEON_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANY_DUNGEON)) {
return StaticData::hintTextTable[RHT_GANON_BK_ANY_DUNGEON_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_OVERWORLD)) {
return StaticData::hintTextTable[RHT_GANON_BK_OVERWORLD_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_ANYWHERE)) {
return StaticData::hintTextTable[RHT_GANON_BK_ANYWHERE_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_KAK_TOKENS)) {
return StaticData::hintTextTable[RHT_GANON_BK_SKULLTULA_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_VANILLA)) {
return StaticData::hintTextTable[RHT_LACS_VANILLA_HINT].GetHintMessage();
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_STONES_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_STONE_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_MEDALLIONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_REWARDS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_REWARD_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_DUNGEONS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
ganonBossKeyMessage = StaticData::hintTextTable[RHT_LACS_TOKENS_HINT].GetHintMessage();
ganonBossKeyMessage.InsertNumber(ctx->GetOption(RSK_LACS_TOKEN_COUNT).Value<uint8_t>());
}
else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_TRIFORCE_HUNT)) {
return StaticData::hintTextTable[RHT_GANON_BK_TRIFORCE_HINT].GetHintMessage();
}
return ganonBossKeyMessage;
}
void Hint::AddHintedLocation(RandomizerCheck location) {
locations.push_back(location);
}
std::vector<RandomizerCheck> Hint::GetHintedLocations() const {
return locations;
}
void Hint::SetHintType(HintType type) {
hintType = type;
}
HintType Hint::GetHintType() {
HintType Hint::GetHintType() const {
return hintType;
}
void Hint::SetHintedArea(RandomizerArea area) {
hintedArea = area;
void Hint::AddHintedArea(RandomizerArea area) {
areas.push_back(area);
}
RandomizerArea Hint::GetHintedArea() {
return hintedArea;
std::vector<RandomizerArea> Hint::GetHintedAreas() const {
return areas;
}
void Hint::SetDistribution(std::string distributionName) {
distribution = distributionName;
}
const std::string& Hint::GetDistribution() {
const std::string& Hint::GetDistribution() const {
return distribution;
}
bool Hint::IsEnabled() const{
return enabled;
}
std::vector<RandomizerHintTextKey> Hint::GetHintTextKeys() const{
return hintKeys;
}
std::vector<RandomizerGet> Hint::GetHintedItems() const{
return items;
}
std::vector<uint8_t> Hint::GetItemNamesChosen() const{
return itemNamesChosen;
}
std::vector<uint8_t> Hint::GetHintTextsChosen() const{
return hintTextsChosen;
}
std::vector<uint8_t> Hint::GetAreaTextsChosen() const{
return areaNamesChosen;
}
std::vector<TrialKey> Hint::GetHintedTrials() const{
return trials;
}
int Hint::GetNum(){
return num;
}
void Hint::ResetVariables() {
hintedLocation = RC_UNKNOWN_CHECK;
text = Text{};
distribution = "";
hintedArea = RA_NONE;
hintType = HINT_TYPE_STATIC;
addedToPool = false;
ownKey = RH_NONE;
num = 0;
yourPocket = false;
messages = {};
hintKeys = {};
locations = {};
items = {};
trials = {};
hintType = HINT_TYPE_HINT_KEY;
areas = {};
distribution = "";
enabled = false;
itemNamesChosen = {};
hintTextsChosen = {};
areaNamesChosen = {};
}
}

View File

@ -1,33 +1,82 @@
#pragma once
#include "3drando/text.hpp"
#include "3drando/hints.hpp"
#include "../custom-message/CustomMessageManager.h"
#include "randomizerTypes.h"
#include <vector>
#include "nlohmann/json.hpp"
using oJson = nlohmann::ordered_json;
namespace Rando {
class Hint {
public:
Hint();
Hint(Text text_);
Hint(Text text_, RandomizerCheck hintedLocation_, HintType hintType_, std::string distributionName_, RandomizerArea hintedArea_);
const Text& GetText() const;
RandomizerCheck GetHintedLocation();
void SetHintedLocation (RandomizerCheck location);
HintType GetHintType();
void SetHintType (HintType type);
RandomizerArea GetHintedArea();
void SetHintedArea (RandomizerArea area);
const std::string& GetDistribution();
void SetDistribution (std::string distribution);
void ResetVariables();
bool IsAddedToPool();
void AddToPool();
public:
Hint();
Hint(RandomizerHint ownKey_,
HintType hintType_,
std::string distributionName_,
std::vector<RandomizerHintTextKey> hintKeys_,
std::vector<RandomizerCheck> locations_,
std::vector<RandomizerArea> areas_ = {},
std::vector<TrialKey> trials_ = {});
Hint(RandomizerHint ownKey_,
HintType hintType_,
std::vector<RandomizerHintTextKey> hintKeys_,
std::vector<RandomizerCheck> locations_ = {},
std::vector<RandomizerArea> areas_ = {},
std::vector<TrialKey> trials_ = {},
bool yourPocket_ = false,
int num_ = 0);
Hint(RandomizerHint ownKey_, std::vector<CustomMessage> messages_);
Hint(RandomizerHint ownKey_, nlohmann::json json_);
void FillGapsInData();
void SetLocationsAsHinted() const;
void NamesChosen();
uint8_t GetNumberOfMessages() const;
const std::vector<std::string> GetAllMessageStrings(MessageFormat format = MF_AUTO_FORMAT) const ;
const CustomMessage GetHintMessage(MessageFormat format = MF_AUTO_FORMAT, uint8_t id = 0) const ;
const HintText GetHintText(uint8_t id = 0) const;
oJson toJSON();
void logHint(oJson& jsonData);
const HintText GetItemHintText(uint8_t slot, bool mysterious) const;
const HintText GetAreaHintText(uint8_t slot) const;
const CustomMessage GetItemName(uint8_t slot, bool mysterious = false) const;
const CustomMessage GetAreaName(uint8_t slot) const;
static CustomMessage GetBridgeReqsText();
static CustomMessage GetGanonBossKeyText();
void AddHintedLocation (RandomizerCheck location);
std::vector<RandomizerCheck> GetHintedLocations() const;
void SetHintType (HintType type);
HintType GetHintType() const;
void AddHintedArea (RandomizerArea area);
std::vector<RandomizerArea> GetHintedAreas() const;
void SetDistribution (std::string distribution);
const std::string& GetDistribution() const;
bool IsEnabled() const;
std::vector<RandomizerHintTextKey> GetHintTextKeys() const;
std::vector<RandomizerGet> GetHintedItems() const;
std::vector<uint8_t> GetItemNamesChosen() const;
std::vector<uint8_t> GetHintTextsChosen() const;
std::vector<uint8_t> GetAreaTextsChosen() const;
std::vector<TrialKey> GetHintedTrials() const;
int GetNum();
void ResetVariables();
private:
Text text = Text();
RandomizerCheck hintedLocation = RC_UNKNOWN_CHECK;
HintType hintType = HINT_TYPE_STATIC;
RandomizerArea hintedArea = RA_NONE;
std::string distribution = "";
bool addedToPool = false;
private:
RandomizerHint ownKey = RH_NONE;
HintType hintType = HINT_TYPE_HINT_KEY;
std::string distribution = "";
std::vector<RandomizerHintTextKey> hintKeys = {};
std::vector<RandomizerCheck> locations = {};
std::vector<RandomizerArea> areas = {};
std::vector<TrialKey> trials = {};
bool yourPocket = false;
int num = 0;
std::vector<CustomMessage> messages = {};
std::vector<RandomizerGet> items = {};
bool enabled = false;
std::vector<uint8_t> itemNamesChosen = {};
std::vector<uint8_t> hintTextsChosen = {};
std::vector<uint8_t> areaNamesChosen = {};
};
}

View File

@ -378,7 +378,7 @@ RandomizerHintTextKey Item::GetHintKey() const {
}
const HintText& Item::GetHint() const {
return ::Hint(hintKey);
return StaticData::hintTextTable[hintKey];
}
bool Item::operator==(const Item& right) const {

View File

@ -5,9 +5,9 @@
#include <memory>
#include "3drando/text.hpp"
#include "3drando/hint_list.hpp"
#include "randomizerTypes.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include "3drando/hints.hpp"
enum ItemType {
ITEMTYPE_ITEM,

View File

@ -8,7 +8,7 @@
using namespace Rando;
std::array<Item, RG_MAX> Rando::StaticData::itemTable;
std::unordered_map<std::string, RandomizerGet> Rando::StaticData::SpoilerfileItemNameToEnum;
std::unordered_map<std::string, RandomizerGet> Rando::StaticData::itemNameToEnum;
void Rando::StaticData::InitItemTable() {
auto logic = Context::GetInstance()->GetLogic();
@ -322,15 +322,15 @@ void Rando::StaticData::InitItemTable() {
itemTable[RG_MAGIC_DOUBLE] = Item(RG_MAGIC_DOUBLE, Text{ "Enhanced Magic Meter", "Jauge de Magie améliorée", "Verbesserte Magieanzeige" }, ITEMTYPE_ITEM, 0x8A, true, &logic->ProgressiveMagic, RHT_MAGIC_DOUBLE, RG_MAGIC_DOUBLE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER);
itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Triforce Piece", "Triforce Piece" }, ITEMTYPE_ITEM, 0xDF, true, &logic->TriforcePieces, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
// Init SpoilerfileItemNameToEnum
// Init itemNameToEnum
for (auto& item : itemTable) {
// Easiest way to filter out all the empty values from the array, since we still technically want the 0/RG_NONE
// entry
if (item.GetName().english.empty()) {
continue;
}
SpoilerfileItemNameToEnum[item.GetName().english] = item.GetRandomizerGet();
SpoilerfileItemNameToEnum[item.GetName().french] = item.GetRandomizerGet();
itemNameToEnum[item.GetName().english] = item.GetRandomizerGet();
itemNameToEnum[item.GetName().french] = item.GetRandomizerGet();
}
}

View File

@ -109,19 +109,19 @@ void ItemLocation::SetAsHintable() {
isHintable = true;
}
bool ItemLocation::IsHintedAt() const {
return hintedAt;
bool ItemLocation::IsAHintAccessible() const {
return hintAccesible;
}
void ItemLocation::SetAsHinted() {
hintedAt = true;
void ItemLocation::SetHintAccesible() {
hintAccesible = true;
}
const std::vector<RandomizerHintKey>& ItemLocation::GetHintedBy() const {
const std::vector<RandomizerHint>& ItemLocation::GetHintedBy() const {
return hintedBy;
}
void ItemLocation::AddHintedBy(const RandomizerHintKey hintKey) {
void ItemLocation::AddHintedBy(const RandomizerHint hintKey) {
hintedBy.push_back(hintKey);
}
@ -193,7 +193,7 @@ void ItemLocation::ResetVariables() {
placedItem = RG_NONE;
delayedItem = RG_NONE;
isHintable = false;
hintedAt = false;
hintAccesible = false;
hintedBy = {};
price = 0;
hasCustomPrice = false;

View File

@ -33,10 +33,10 @@ class ItemLocation {
void SetCustomPrice(uint16_t price_);
bool IsHintable() const;
void SetAsHintable();
bool IsHintedAt() const;
void SetAsHinted();
const std::vector<RandomizerHintKey>& GetHintedBy() const;
void AddHintedBy(RandomizerHintKey hintKey);
bool IsAHintAccessible() const;
void SetHintAccesible();
const std::vector<RandomizerHint>& GetHintedBy() const;
void AddHintedBy(RandomizerHint hintKey);
bool IsHidden() const;
bool IsExcluded() const;
void AddExcludeOption();
@ -52,8 +52,8 @@ class ItemLocation {
private:
RandomizerCheck rc;
std::vector<RandomizerHintKey> hintedBy = {};
bool hintedAt = false;
std::vector<RandomizerHint> hintedBy = {};
bool hintAccesible = false;
bool isHintable = false;
bool addedToPool = false;
RandomizerGet placedItem = RG_NONE;

View File

@ -1,5 +1,5 @@
#include "location.h"
#include "3drando/hint_list.hpp"
#include "static_data.h"
#include <algorithm>
RandomizerCheck Rando::Location::GetRandomizerCheck() const {
@ -47,7 +47,7 @@ RandomizerHintTextKey Rando::Location::GetHintKey() const {
}
HintText* Rando::Location::GetHint() {
return &hintTable[hintKey];
return &StaticData::hintTextTable[hintKey];
}
const std::string& Rando::Location::GetName() const {
@ -90,7 +90,7 @@ uint32_t Rando::Location::Getuint32_t() const {
}
const HintText& Rando::Location::GetHint() const {
return Hint(hintKey);
return StaticData::hintTextTable[hintKey];
}
RandomizerGet Rando::Location::GetVanillaItem() const {

View File

@ -3,7 +3,6 @@
#define TWO_ACTOR_PARAMS(a, b) (abs(a) << 16) | abs(b)
std::array<Rando::Location, RC_MAX> Rando::StaticData::locationTable;
std::unordered_map<std::string, RandomizerCheck> Rando::StaticData::SpoilerfileCheckNameToEnum;
std::multimap<std::tuple<s16, s16, s32>, RandomizerCheck> Rando::StaticData::CheckFromActorMultimap;
std::vector<RandomizerCheck> KF_ShopLocations = {
@ -501,49 +500,49 @@ std::vector<RandomizerCheck> Rando::StaticData::overworldLocations = {
};
std::vector<RandomizerCheck> Rando::StaticData::gossipStoneLocations = {
RC_COLOSSUS_GOSSIP_STONE,
RC_DMC_GOSSIP_STONE,
RC_DMC_UPPER_GROTTO_GOSSIP_STONE,
RC_DMT_GOSSIP_STONE,
RC_DMT_STORMS_GROTTO_GOSSIP_STONE,
RC_DODONGOS_CAVERN_GOSSIP_STONE,
RC_FAIRY_GOSSIP_STONE,
RC_GC_MAZE_GOSSIP_STONE,
RC_GC_MEDIGORON_GOSSIP_STONE,
RC_GV_GOSSIP_STONE,
RC_GY_GOSSIP_STONE,
RC_HC_MALON_GOSSIP_STONE,
RC_HC_ROCK_WALL_GOSSIP_STONE,
RC_HC_STORMS_GROTTO_GOSSIP_STONE,
RC_HF_COW_GROTTO_GOSSIP_STONE,
RC_HF_NEAR_MARKET_GOSSIP_STONE,
RC_HF_OPEN_GROTTO_GOSSIP_STONE,
RC_HF_SOUTHEAST_GOSSIP_STONE,
RC_JABU_GOSSIP_STONE,
RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE,
RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE,
RC_KF_GOSSIP_STONE,
RC_KF_STORMS_GOSSIP_STONE,
RC_KAK_OPEN_GROTTO_GOSSIP_STONE,
RC_LH_LAB_GOSSIP_STONE,
RC_LH_SOUTHEAST_GOSSIP_STONE,
RC_LH_SOUTHWEST_GOSSIP_STONE,
RC_KF_STORMS_GROTTO_GOSSIP_STONE,
RC_LW_GOSSIP_STONE,
RC_LW_NEAR_SHORTCUTS_GOSSIP_STONE,
RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE,
RC_SFM_MAZE_LOWER_GOSSIP_STONE,
RC_SFM_MAZE_UPPER_GOSSIP_STONE,
RC_SFM_SARIA_GOSSIP_STONE,
RC_HF_COW_GROTTO_GOSSIP_STONE,
RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE,
RC_HF_OPEN_GROTTO_GOSSIP_STONE,
RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE,
RC_TOT_LEFT_CENTER_GOSSIP_STONE,
RC_TOT_LEFT_GOSSIP_STONE,
RC_TOT_LEFTMOST_GOSSIP_STONE,
RC_TOT_RIGHT_CENTER_GOSSIP_STONE,
RC_TOT_RIGHT_GOSSIP_STONE,
RC_ZD_GOSSIP_STONE,
RC_TOT_RIGHTMOST_GOSSIP_STONE,
RC_HC_MALON_GOSSIP_STONE,
RC_HC_ROCK_WALL_GOSSIP_STONE,
RC_HC_STORMS_GROTTO_GOSSIP_STONE,
RC_KAK_OPEN_GROTTO_GOSSIP_STONE,
RC_GRAVEYARD_GOSSIP_STONE,
RC_DMT_GOSSIP_STONE,
RC_DMT_STORMS_GROTTO_GOSSIP_STONE,
RC_GC_MAZE_GOSSIP_STONE,
RC_GC_MEDIGORON_GOSSIP_STONE,
RC_DMC_GOSSIP_STONE,
RC_DMC_UPPER_GROTTO_GOSSIP_STONE,
RC_ZR_NEAR_DOMAIN_GOSSIP_STONE,
RC_ZR_NEAR_GROTTOS_GOSSIP_STONE,
RC_ZR_OPEN_GROTTO_GOSSIP_STONE,
RC_ZD_GOSSIP_STONE,
RC_ZF_JABU_GOSSIP_STONE,
RC_ZF_FAIRY_GOSSIP_STONE,
RC_LH_LAB_GOSSIP_STONE,
RC_LH_SOUTHEAST_GOSSIP_STONE,
RC_LH_SOUTHWEST_GOSSIP_STONE,
RC_GV_GOSSIP_STONE,
RC_COLOSSUS_GOSSIP_STONE,
RC_DODONGOS_CAVERN_GOSSIP_STONE,
};
std::vector<RandomizerCheck> Rando::StaticData::otherHintLocations = {
std::vector<RandomizerCheck> Rando::StaticData::staticHintLocations = {
RC_GANONDORF_HINT,
RC_SHEIK_HINT_GC,
RC_SHEIK_HINT_MQ_GC,
@ -1515,48 +1514,48 @@ void Rando::StaticData::InitLocationTable() {
locationTable[RC_ZD_FISH_5] = Location::Base(RC_ZD_FISH_5, RCQUEST_BOTH, RCTYPE_FISH, RCAREA_ZORAS_DOMAIN, ACTOR_EN_FISH, SCENE_ZORAS_DOMAIN, -1 ^ 4, 0x00, "Fish 5", "ZD Fish 5", RHT_ZD_FISH, RG_FISH, { Category::cFish }, SpoilerCollectionCheck::Fish(0xFF, SCENE_ZORAS_DOMAIN), SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN);
// Gossip Stones
// RandomizerCheck Randomizer Check Quest Area Scene Params Flag Short Name Spoiler name Categories
locationTable[RC_DMC_GOSSIP_STONE] = Location::HintStone(RC_DMC_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_DEATH_MOUNTAIN_CRATER, 14341, 0x05, "Gossip Stone", "DMC Gossip Stone", {});
locationTable[RC_DMT_GOSSIP_STONE] = Location::HintStone(RC_DMT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, 14340, 0x04, "Gossip Stone", "DMT Gossip Stone", {});
locationTable[RC_COLOSSUS_GOSSIP_STONE] = Location::HintStone(RC_COLOSSUS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, 14362, 0x1A, "Gossip Stone", "Colossus Gossip Stone", {});
locationTable[RC_DODONGOS_CAVERN_GOSSIP_STONE] = Location::HintStone(RC_DODONGOS_CAVERN_GOSSIP_STONE, RCQUEST_VANILLA, RCAREA_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN, 4372, 0x14, "Gossip Stone", "Dodongo's Cavern Gossip Stone", {});
locationTable[RC_GV_GOSSIP_STONE] = Location::HintStone(RC_GV_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, 14353, 0x11, "Gossip Stone", "GV Gossip Stone", {});
locationTable[RC_GC_MAZE_GOSSIP_STONE] = Location::HintStone(RC_GC_MAZE_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, 14357, 0x15, "Maze Gossip Stone", "GC Maze Gossip Stone", {});
locationTable[RC_GC_MEDIGORON_GOSSIP_STONE] = Location::HintStone(RC_GC_MEDIGORON_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, 14873, 0x19, "Medigoron Gossip Stone", "GC Medigoron Gossip Stone", {});
locationTable[RC_GY_GOSSIP_STONE] = Location::HintStone(RC_GY_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, 14346, 0x0A, "Gossip Stone", "GY Gossip Stone", {});
locationTable[RC_HC_MALON_GOSSIP_STONE] = Location::HintStone(RC_HC_MALON_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, 14610, 0x12, "Malon Gossip Stone", "HC Malon Gossip Stone", {});
locationTable[RC_HC_ROCK_WALL_GOSSIP_STONE] = Location::HintStone(RC_HC_ROCK_WALL_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, 14347, 0x0B, "Rock Wall Gossip Stone", "HC Rock Wall Gossip Stone", {});
locationTable[RC_HC_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HC_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, 14355, 0x13, "Storms Grotto Gossip Stone", "HC Storms Grotto Gossip Stone", {});
locationTable[RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE] = Location::HintStone(RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14623, 0x1F, "Deku Tree Left Gossip Stone", "KF Deku Tree Left Gossip Stone", {});
locationTable[RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE] = Location::HintStone(RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14880, 0x20, "Deku Tree Right Gossip Stone", "KF Deku Tree Right Gossip Stone", {});
locationTable[RC_KF_GOSSIP_STONE] = Location::HintStone(RC_KF_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14366, 0x1E, "Gossip Stone", "KF Gossip Stone", {});
locationTable[RC_KF_STORMS_GOSSIP_STONE] = Location::HintStone(RC_KF_STORMS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_GROTTOS, -22988, 0x3C, "Storms Gossip Stone", "KF Storms Gossip Stone", {});
locationTable[RC_LH_LAB_GOSSIP_STONE] = Location::HintStone(RC_LH_LAB_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14339, 0x03, "Lab Gossip Stone", "LH Lab Gossip Stone", {});
locationTable[RC_LH_SOUTHEAST_GOSSIP_STONE] = Location::HintStone(RC_LH_SOUTHEAST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14863, 0x0F, "Southeast Gossip Stone", "LH Southeast Gossip Stone", {});
locationTable[RC_LH_SOUTHWEST_GOSSIP_STONE] = Location::HintStone(RC_LH_SOUTHWEST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14600, 0x08, "Southwest Gossip Stone", "LH Southwest Gossip Stone", {});
locationTable[RC_LW_GOSSIP_STONE] = Location::HintStone(RC_LW_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, 14365, 0x1D, "Gossip Stone", "LW Gossip Stone", {});
locationTable[RC_SFM_MAZE_LOWER_GOSSIP_STONE] = Location::HintStone(RC_SFM_MAZE_LOWER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14358, 0x16, "Maze Lower Gossip Stone", "SFM Maze Lower Gossip Stone", {});
locationTable[RC_SFM_MAZE_UPPER_GOSSIP_STONE] = Location::HintStone(RC_SFM_MAZE_UPPER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14615, 0x17, "Maze Upper Gossip Stone", "SFM Maze Upper Gossip Stone", {});
locationTable[RC_SFM_SARIA_GOSSIP_STONE] = Location::HintStone(RC_SFM_SARIA_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14876, 0x1C, "Saria Gossip Stone", "SFM Saria Gossip Stone", {});
locationTable[RC_TOT_LEFT_GOSSIP_STONE] = Location::HintStone(RC_TOT_LEFT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x06, "ToT Left Gossip Stone", "ToT Left Gossip Stone", {});
locationTable[RC_TOT_RIGHT_GOSSIP_STONE] = Location::HintStone(RC_TOT_RIGHT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x07, "ToT Right Gossip Stone", "ToT Right Gossip Stone", {});
locationTable[RC_TOT_RIGHT_CENTER_GOSSIP_STONE] = Location::HintStone(RC_TOT_RIGHT_CENTER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x10, "ToT Right Center Gossip Stone", "ToT Right Center Gossip Stone", {});
locationTable[RC_TOT_LEFT_CENTER_GOSSIP_STONE] = Location::HintStone(RC_TOT_LEFT_CENTER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x0E, "ToT Left Center Gossip Stone", "ToT Left Center Gossip Stone", {});
locationTable[RC_ZD_GOSSIP_STONE] = Location::HintStone(RC_ZD_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, 14345, 0x09, "Gossip Stone", "ZD Gossip Stone", {});
locationTable[RC_FAIRY_GOSSIP_STONE] = Location::HintStone(RC_FAIRY_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, 0x00, 0x01, "Fairy Gossip Stone", "Fairy Gossip Stone", {});
locationTable[RC_JABU_GOSSIP_STONE] = Location::HintStone(RC_JABU_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, 0x00, 0x02, "Jabu Gossip Stone", "Jabu Gossip Stone", {});
locationTable[RC_ZR_NEAR_GROTTOS_GOSSIP_STONE] = Location::HintStone(RC_ZR_NEAR_GROTTOS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, 14605, 0x0D, "Near Grottos Gossip Stone", "ZR Near Grottos Gossip Stone", {});
locationTable[RC_ZR_NEAR_DOMAIN_GOSSIP_STONE] = Location::HintStone(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, 14860, 0x0C, "Near Domain Gossip Stone", "ZR Near Domain Gossip Stone", {});
locationTable[RC_HF_COW_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_COW_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, 14363, 0x1B, "Cow Grotto Gossip Stone", "HF Cow Grotto Gossip Stone", {});
locationTable[RC_HF_NEAR_MARKET_GOSSIP_STONE] = Location::HintStone(RC_HF_NEAR_MARKET_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22944, 0x30, "Near Market Gossip Stone", "HF Near Market Gossip Stone", {});
locationTable[RC_HF_SOUTHEAST_GOSSIP_STONE] = Location::HintStone(RC_HF_SOUTHEAST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22978, 0x32, "Southeast Gossip Stone", "HF Southeast Gossip Stone", {});
locationTable[RC_HF_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22947, 0x33, "Open Grotto Gossip Stone", "HF Open Grotto Gossip Stone", {});
locationTable[RC_KAK_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_KAK_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_GROTTOS, -22984, 0x38, "Open Grotto Gossip Stone", "Kak Open Grotto Gossip Stone", {});
locationTable[RC_ZR_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_ZR_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_GROTTOS, -22985, 0x39, "Open Grotto Gossip Stone", "ZR Open Grotto Gossip Stone", {});
locationTable[RC_LW_NEAR_SHORTCUTS_GOSSIP_STONE] = Location::HintStone(RC_LW_NEAR_SHORTCUTS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, -22964, 0x34, "Near Shortcuts Gossip Stone", "LW Near Shortcuts Gossip Stone", {});
locationTable[RC_DMT_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMT_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_GROTTOS, -23255, 0x37, "Storms Grotto Gossip Stone", "DMT Storms Grotto Gossip Stone", {});
locationTable[RC_DMC_UPPER_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_GROTTOS, -23802, 0x3A, "Upper Grotto Gossip Stone", "DMC Upper Grotto Gossip Stone", {});
// Gossip Stones RANDOTODO work towards removing and replacing with event access
// RandomizerCheck Randomizer Check Quest Area Scene Params Flag Short Name Spoiler name Categories
locationTable[RC_DMC_GOSSIP_STONE] = Location::HintStone(RC_DMC_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_DEATH_MOUNTAIN_CRATER, 14341, 0x05, "Gossip Stone", "DMC Gossip Stone", {});
locationTable[RC_DMT_GOSSIP_STONE] = Location::HintStone(RC_DMT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, 14340, 0x04, "Gossip Stone", "DMT Gossip Stone", {});
locationTable[RC_COLOSSUS_GOSSIP_STONE] = Location::HintStone(RC_COLOSSUS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, 14362, 0x1A, "Gossip Stone", "Colossus Gossip Stone", {});
locationTable[RC_DODONGOS_CAVERN_GOSSIP_STONE] = Location::HintStone(RC_DODONGOS_CAVERN_GOSSIP_STONE, RCQUEST_VANILLA, RCAREA_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN, 4372, 0x14, "Gossip Stone", "Dodongo's Cavern Gossip Stone", {});
locationTable[RC_GV_GOSSIP_STONE] = Location::HintStone(RC_GV_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, 14353, 0x11, "Gossip Stone", "GV Gossip Stone", {});
locationTable[RC_GC_MAZE_GOSSIP_STONE] = Location::HintStone(RC_GC_MAZE_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, 14357, 0x15, "Maze Gossip Stone", "GC Maze Gossip Stone", {});
locationTable[RC_GC_MEDIGORON_GOSSIP_STONE] = Location::HintStone(RC_GC_MEDIGORON_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, 14873, 0x19, "Medigoron Gossip Stone", "GC Medigoron Gossip Stone", {});
locationTable[RC_GRAVEYARD_GOSSIP_STONE] = Location::HintStone(RC_GRAVEYARD_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, 14346, 0x0A, "Gossip Stone", "GY Gossip Stone", {});
locationTable[RC_HC_MALON_GOSSIP_STONE] = Location::HintStone(RC_HC_MALON_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, 14610, 0x12, "Malon Gossip Stone", "HC Malon Gossip Stone", {});
locationTable[RC_HC_ROCK_WALL_GOSSIP_STONE] = Location::HintStone(RC_HC_ROCK_WALL_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, 14347, 0x0B, "Rock Wall Gossip Stone", "HC Rock Wall Gossip Stone", {});
locationTable[RC_HC_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HC_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_GROTTOS, 14355, 0x13, "Storms Grotto Gossip Stone", "HC Storms Grotto Gossip Stone", {});
locationTable[RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE] = Location::HintStone(RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14623, 0x1F, "Deku Tree Left Gossip Stone", "KF Deku Tree Left Gossip Stone", {});
locationTable[RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE] = Location::HintStone(RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14880, 0x20, "Deku Tree Right Gossip Stone", "KF Deku Tree Right Gossip Stone", {});
locationTable[RC_KF_GOSSIP_STONE] = Location::HintStone(RC_KF_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, 14366, 0x1E, "Gossip Stone", "KF Gossip Stone", {});
locationTable[RC_KF_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_KF_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_GROTTOS, -22988, 0x3C, "Storms Gossip Stone", "KF Storms Gossip Stone", {});
locationTable[RC_LH_LAB_GOSSIP_STONE] = Location::HintStone(RC_LH_LAB_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14339, 0x03, "Lab Gossip Stone", "LH Lab Gossip Stone", {});
locationTable[RC_LH_SOUTHEAST_GOSSIP_STONE] = Location::HintStone(RC_LH_SOUTHEAST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14863, 0x0F, "Southeast Gossip Stone", "LH Southeast Gossip Stone", {});
locationTable[RC_LH_SOUTHWEST_GOSSIP_STONE] = Location::HintStone(RC_LH_SOUTHWEST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, 14600, 0x08, "Southwest Gossip Stone", "LH Southwest Gossip Stone", {});
locationTable[RC_LW_GOSSIP_STONE] = Location::HintStone(RC_LW_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, 14365, 0x1D, "Gossip Stone", "LW Gossip Stone", {});
locationTable[RC_SFM_MAZE_LOWER_GOSSIP_STONE] = Location::HintStone(RC_SFM_MAZE_LOWER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14358, 0x16, "Maze Lower Gossip Stone", "SFM Maze Lower Gossip Stone", {});
locationTable[RC_SFM_MAZE_UPPER_GOSSIP_STONE] = Location::HintStone(RC_SFM_MAZE_UPPER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14615, 0x17, "Maze Upper Gossip Stone", "SFM Maze Upper Gossip Stone", {});
locationTable[RC_SFM_SARIA_GOSSIP_STONE] = Location::HintStone(RC_SFM_SARIA_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, 14876, 0x1C, "Saria Gossip Stone", "SFM Saria Gossip Stone", {});
locationTable[RC_TOT_LEFTMOST_GOSSIP_STONE] = Location::HintStone(RC_TOT_LEFTMOST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x06, "ToT Left Gossip Stone", "ToT Left Gossip Stone", {});
locationTable[RC_TOT_RIGHTMOST_GOSSIP_STONE] = Location::HintStone(RC_TOT_RIGHTMOST_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x07, "ToT Right Gossip Stone", "ToT Right Gossip Stone", {});
locationTable[RC_TOT_RIGHT_CENTER_GOSSIP_STONE] = Location::HintStone(RC_TOT_RIGHT_CENTER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x10, "ToT Right Center Gossip Stone", "ToT Right Center Gossip Stone", {});
locationTable[RC_TOT_LEFT_CENTER_GOSSIP_STONE] = Location::HintStone(RC_TOT_LEFT_CENTER_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_MARKET, SCENE_ID_MAX, 0x00, 0x0E, "ToT Left Center Gossip Stone", "ToT Left Center Gossip Stone", {});
locationTable[RC_ZD_GOSSIP_STONE] = Location::HintStone(RC_ZD_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, 14345, 0x09, "Gossip Stone", "ZD Gossip Stone", {});
locationTable[RC_ZF_FAIRY_GOSSIP_STONE] = Location::HintStone(RC_ZF_FAIRY_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, 0x00, 0x01, "Fairy Gossip Stone", "Fairy Gossip Stone", {});
locationTable[RC_ZF_JABU_GOSSIP_STONE] = Location::HintStone(RC_ZF_JABU_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, 0x00, 0x02, "Jabu Gossip Stone", "Jabu Gossip Stone", {});
locationTable[RC_ZR_NEAR_GROTTOS_GOSSIP_STONE] = Location::HintStone(RC_ZR_NEAR_GROTTOS_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, 14605, 0x0D, "Near Grottos Gossip Stone", "ZR Near Grottos Gossip Stone", {});
locationTable[RC_ZR_NEAR_DOMAIN_GOSSIP_STONE] = Location::HintStone(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, 14860, 0x0C, "Near Domain Gossip Stone", "ZR Near Domain Gossip Stone", {});
locationTable[RC_HF_COW_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_COW_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, 14363, 0x1B, "Cow Grotto Gossip Stone", "HF Cow Grotto Gossip Stone", {});
locationTable[RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22944, 0x30, "Near Market Gossip Stone", "HF Near Market Gossip Stone", {});
locationTable[RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22978, 0x32, "Southeast Gossip Stone", "HF Southeast Gossip Stone", {});
locationTable[RC_HF_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_HF_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_GROTTOS, -22947, 0x33, "Open Grotto Gossip Stone", "HF Open Grotto Gossip Stone", {});
locationTable[RC_KAK_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_KAK_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_GROTTOS, -22984, 0x38, "Open Grotto Gossip Stone", "Kak Open Grotto Gossip Stone", {});
locationTable[RC_ZR_OPEN_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_ZR_OPEN_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_GROTTOS, -22985, 0x39, "Open Grotto Gossip Stone", "ZR Open Grotto Gossip Stone", {});
locationTable[RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, -22964, 0x34, "Near Shortcuts Gossip Stone", "LW Near Shortcuts Gossip Stone", {});
locationTable[RC_DMT_STORMS_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMT_STORMS_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_GROTTOS, -23255, 0x37, "Storms Grotto Gossip Stone", "DMT Storms Grotto Gossip Stone", {});
locationTable[RC_DMC_UPPER_GROTTO_GOSSIP_STONE] = Location::HintStone(RC_DMC_UPPER_GROTTO_GOSSIP_STONE, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_GROTTOS, -23802, 0x3A, "Upper Grotto Gossip Stone", "DMC Upper Grotto Gossip Stone", {});
// Other Hints
locationTable[RC_GANONDORF_HINT] = Location::OtherHint(RC_GANONDORF_HINT, RCQUEST_BOTH, RCTYPE_GOSSIP_STONE, RCAREA_GANONS_CASTLE, ACTOR_EN_GANON_MANT, SCENE_GANON_BOSS, "Ganondorf Hint", "Ganondorf Hint");
@ -1572,12 +1571,12 @@ void Rando::StaticData::InitLocationTable() {
locationTable[RC_TRIFORCE_COMPLETED] = Location::Reward(RC_TRIFORCE_COMPLETED, RCQUEST_BOTH, RCTYPE_STANDARD, RCAREA_MARKET, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, 0x00, "Completed Triforce", "Completed Triforce", RHT_NONE, RG_NONE, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_NO_GROUP);
// clang-format on
// Init SpoilerfileCheckNameToEnum
SpoilerfileCheckNameToEnum["Invalid Location"] = RC_UNKNOWN_CHECK;
SpoilerfileCheckNameToEnum["Link's Pocket"] = RC_LINKS_POCKET;
// Init locationNameToEnum
locationNameToEnum["Invalid Location"] = RC_UNKNOWN_CHECK;
locationNameToEnum["Link's Pocket"] = RC_LINKS_POCKET;
for (auto& location : locationTable) {
SpoilerfileCheckNameToEnum[location.GetName()] = location.GetRandomizerCheck();
locationNameToEnum[location.GetName()] = location.GetRandomizerCheck();
CheckFromActorMultimap.emplace(std::make_tuple((int16_t)location.GetActorID(), (int16_t)location.GetScene(), location.GetActorParams()), location.GetRandomizerCheck());
}
}

View File

@ -503,10 +503,13 @@ void Settings::CreateOptionDescriptions() {
"Reading the Temple of Time altar as child will tell you the locations of the Spiritual Stones.\n"
"Reading the Temple of Time altar as adult will tell you the locations of the Medallions, as well as the "
"conditions for building the Rainbow Bridge and getting the Boss Key for Ganon's Castle.";
mOptionDescriptions[RSK_LIGHT_ARROWS_HINT] =
"Talking to Ganondorf in his boss room or Sheik inside Ganon's Castle (when trials are enabled) will tell you "
"the location of the Light Arrows."
"If this option is enabled and Ganondorf is reachable without Light Arrows, Gossip Stones will never hint the "
mOptionDescriptions[RSK_GANONDORF_HINT] =
"Talking to Ganondorf in his boss room will tell you the location of the Light Arrows and Master Sword."
"If this option is enabled and Ganondorf is reachable without these items, Gossip Stones will never hint the "
"appropriote items.";//RANDOTODO make this hint text about no dupe hints a global hint for static hints. Add to navi?
mOptionDescriptions[RSK_SHEIK_LA_HINT] =
"Talking to Sheik inside Ganon's Castle will tell you the location of the Light Arrows."
"If this option is enabled and Sheik is reachable without Light Arrows, Gossip Stones will never hint the "
"Light Arrows.";
mOptionDescriptions[RSK_DAMPES_DIARY_HINT] =
"Reading the diary of Dampé the gravekeeper as adult will tell you the location of one of the Hookshots.";
@ -546,7 +549,7 @@ void Settings::CreateOptionDescriptions() {
mOptionDescriptions[RSK_BLUE_FIRE_ARROWS] =
"Ice Arrows act like Blue Fire, making them able to melt red ice. "
"Item placement logic will respect this option, so it might be required to use this to progress.";
mOptionDescriptions[RSK_LIGHT_ARROWS_HINT] =
mOptionDescriptions[RSK_SUNLIGHT_ARROWS] =
"Light Arrows can be used to light up the sun switches instead of using the Mirror Shield. "
"Item placement logic will respect this option, so it might be required to use this to progress.";
mOptionDescriptions[RSK_LOGIC_RULES] =

View File

@ -10,6 +10,7 @@
#include <textures/icon_item_24_static/icon_item_24_static.h>
#include "3drando/rando_main.hpp"
#include "3drando/random.hpp"
#include "3drando/custom_messages.hpp"
#include "../../UIWidgets.hpp"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
@ -37,12 +38,12 @@
#include "settings.h"
#include "soh/util.h"
#include "fishsanity.h"
#include "randomizerTypes.h"
extern "C" uint32_t ResourceMgr_IsGameMasterQuest();
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
extern std::map<RandomizerCheckArea, std::string> rcAreaNames;
extern std::array<std::string, HINT_TYPE_MAX> hintTypeNames;
using json = nlohmann::json;
using namespace std::literals::string_literals;
@ -136,8 +137,8 @@ Randomizer::Randomizer() {
SpoilerfileAreaNameToEnum["the Graveyard"] = RCAREA_GRAVEYARD;
SpoilerfileAreaNameToEnum["Haunted Wasteland"] = RCAREA_WASTELAND;
SpoilerfileAreaNameToEnum["outside Ganon's Castle"] = RCAREA_HYRULE_CASTLE;
for (int c = 0; c < hintTypeNames.size(); c++) {
SpoilerfileHintTypeNameToEnum[hintTypeNames[c]] = (HintType)c;
for (int c = 0; c < Rando::StaticData::hintTypeNames.size(); c++) {
SpoilerfileHintTypeNameToEnum[Rando::StaticData::hintTypeNames[(HintType)c].GetEnglish(MF_CLEAN)] = (HintType)c;
}
}
@ -240,187 +241,10 @@ void Randomizer::LoadHintMessages() {
CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD,
CustomMessage(ctx->GetHint(RH_ALTAR_CHILD)->GetText(), TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT,
CustomMessage(ctx->GetHint(RH_ALTAR_ADULT)->GetText(), TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF,
CustomMessage(ctx->GetHint(RH_GANONDORF_HINT)->GetText()));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT,
CustomMessage(ctx->GetHint(RH_GANONDORF_NOHINT)->GetText()));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_SHEIK_NEED_HOOK,
CustomMessage("{{message}}", "{{message}}", "{{message}}"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_SHEIK_HAVE_HOOK,
CustomMessage("{{message}}", "{{message}}", "{{message}}"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_SARIAS_SONG_FACE_TO_FACE,
CustomMessage(ctx->GetHint(RH_SARIA)->GetText(), TEXTBOX_TYPE_BLUE));
for (int i : Rando::StaticData::gossipStoneLocations) {
RandomizerHintKey rhk = RandomizerHintKey(i - RC_COLOSSUS_GOSSIP_STONE + 1);
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, i, CustomMessage(ctx->GetHint(rhk)->GetText()));
}
//Extra Hints
CustomMessageManager::Instance->ClearMessageTable(Randomizer::randoMiscHintsTableID);
CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::randoMiscHintsTableID);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_SKULLTULA_PEOPLE_IM_CURSED,
CustomMessage("Yeaaarrgh! I'm cursed!!^Please save me by destroying&%r{{params}} Spiders of the Curse%w&and I will give you my&%g{{item1}}%w!",
"Yeaaarrgh! Ich bin verflucht!^Bitte rette mich, indem du %r{{params}} Skulltulas&%wzerstörst und ich werde dir dafür&%g{{item1}} %wgeben!",
"Yeaaarrgh! Je suis maudit!^Détruit encore %r{{params}} Araignées de&la Malédiction%w et j'aurai quelque&chose à te donner!&%g({{item1}})")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_DAMPES_DIARY,
CustomMessage(ctx->GetHint(RH_DAMPES_DIARY)->GetText())
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_CHEST_GAME_PROCEED,
CustomMessage(ctx->GetHint(RH_GREG_RUPEE)->GetText())
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_FROGS_UNDERWATER,
CustomMessage(ctx->GetHint(RH_FROGS)->GetText().GetEnglish(),
ctx->GetHint(RH_FROGS)->GetText().GetGerman(),
ctx->GetHint(RH_FROGS)->GetText().GetFrench(), TEXTBOX_TYPE_BLUE)
);
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_MINUET_OF_FOREST,
CustomMessage(ctx->GetHint(RH_MINUET_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_MINUET_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_MINUET_WARP_LOC)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_BOLERO_OF_FIRE,
CustomMessage(ctx->GetHint(RH_BOLERO_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_BOLERO_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_BOLERO_WARP_LOC)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_SERENADE_OF_WATER,
CustomMessage(ctx->GetHint(RH_SERENADE_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_SERENADE_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_SERENADE_WARP_LOC)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_REQUIEM_OF_SPIRIT,
CustomMessage(ctx->GetHint(RH_REQUIEM_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_REQUIEM_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_REQUIEM_WARP_LOC)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_SARIAS_SONG_FOREST_SOUNDS,
CustomMessage("{{message}}", "{{message}}", "{{message}}", TEXTBOX_TYPE_BLUE)
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_BIGGORON_BETTER_AT_SMITHING,
CustomMessage("Arrrrrre you here to claim my finest&%g{{item1}}%w?&Shoooooow me your %rClaim Check.%w",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_GHOST_SHOP_CARD_HAS_POINTS,
CustomMessage("You have %g\x1E\x01%r Poe Points%w!&Reach 1000 and you'll get a&%g{{item1}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_ANJU_PLEASE_BRING_MY_CUCCOS_BACK,
CustomMessage("You! Please!&Bring my Cucco's back to my pen!&I'll give you my %g{{item1}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_OBSTICLE_COURSE,
CustomMessage("How about trying the %rObsticle Course?%w&If you beat my time I'll let you keep&my favourite cow Elsie and&her toy %g{{item1}}%w!^"
"Challenge the %rObsticle Course?&\x1B&%gLet's go&No thanks%w",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_HOW_IS_EPONA_DOING,
CustomMessage("@! You should come back &with Epona and try to beat my time&on the %rObsticle Course%w!^If you beat my time, I'll give you&my favourite %rcow%w Elsie and&her toy %g{{item1}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_EVERYONE_TURNING_EVIL,
CustomMessage("@? Is that you? ^If I ran the ranch, I'd build an &%rObsticle Course%w, and whoever gets&the best time would win a %rcow%w!^Elsie loves sharing her %g{{item1}}%w&with new people, It'll be fun!^...But Ingo won't let me...",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED,
CustomMessage("@! You should come back in&the morning and try to beat my time&on the %rObsticle Course%w!^If you beat my time, I'll give you&my favourite %rcow%w Elsie and&her toy %g{{item1}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_HBA_NOT_ON_HORSE,
CustomMessage("Hey, rookie!&Come back on your %rhorse%w&and take on the&%rHorseback Archery%w challenge!^Impress me with a high score of 1000&to win a %g{{item1}}%w&or score 1500 for my&%g{{item2}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_HBA_INITIAL_EXPLAINATION,
CustomMessage("Hey, rookie!&Want to take on the&%rHorseback Archery%w challenge?^Impress me with a high score of 1000&to win a %g{{item1}}%w&or score 1500 for my&%g{{item2}}%w!\x0B",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_HBA_ALREADY_HAVE_1000,
CustomMessage("Hey, newcomer!&Want to take on the&%rHorseback Archery%w challenge?^Prove yourself to be a horsemaster&by scoring 1500 points to win &my %g{{item1}}%w!\x0B",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_GF_HBA_SIGN,
CustomMessage("%rHorseback Archery%w Range Prizes:&1000: %g{{item1}}%w&1500: %g{{item2}}%w^@'s Record: %g\x1E\x00%w",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_OBSTICLE_COURSE,
CustomMessage("How about trying your skill on the %rObsticle Course?%w& If you beat my time I'll let you keep my favourite cow Elsie and& her toy %g{{item1}}%w!&x1B&%gLet's go&No thanks%w",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_EVERYONE_TURNING_EVIL,
CustomMessage("@? Is that you? &If I ran the ranch, I'd build an %rObsticle Course%w, and whoever gets the best time would win a cow!& Elsie loves sharing her %g{{item1}}%w&with new people, It'll be fun!&...But Ingo won't let me...",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED,
CustomMessage("@! You should come back in the morning and try to beat my time on the %rObsticle Course%w!&If you beat my time, I'll let you keep Elsie and& her toy %g{{item1}}%w!",
"",
"")
);
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_NOCTURNE_OF_SHADOW,
CustomMessage(ctx->GetHint(RH_NOCTURNE_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_NOCTURNE_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_NOCTURNE_WARP_LOC)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(Randomizer::randoMiscHintsTableID, TEXT_WARP_PRELUDE_OF_LIGHT,
CustomMessage(ctx->GetHint(RH_PRELUDE_WARP_LOC)->GetText().GetEnglish(),
ctx->GetHint(RH_PRELUDE_WARP_LOC)->GetText().GetGerman(),
ctx->GetHint(RH_PRELUDE_WARP_LOC)->GetText().GetFrench()));
// Bow Shooting Gallery reminder
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW,
@ -428,19 +252,12 @@ void Randomizer::LoadHintMessages() {
"Komm wieder sobald du deinen eigenen&Bogen hast, um einen %rspeziellen Preis%w zu&erhalten!",
"J'aurai %rune autre récompense%w pour toi&lorsque tu auras ton propre arc."));
// Fishing pond pole hint
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_FISHING_POND_START,
CustomMessage(ctx->GetHint(RH_FISHING_POLE)->GetText().GetEnglish(),
ctx->GetHint(RH_FISHING_POLE)->GetText().GetEnglish(),
ctx->GetHint(RH_FISHING_POLE)->GetText().GetFrench())
);
CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_FISHING_POND_START_MET,
CustomMessage(ctx->GetHint(RH_FISHING_POLE)->GetText().GetEnglish(),
ctx->GetHint(RH_FISHING_POLE)->GetText().GetEnglish(),
ctx->GetHint(RH_FISHING_POLE)->GetText().GetFrench())
);
// Warp Song Mysterious text
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_WARP_MINUET_OF_FOREST,
CustomMessage("Warp to&#a mysterious place?#&" + CustomMessages::TWO_WAY_CHOICE() + "#OK&No#",
"Zu&#ein mysteriöser Ort#?&" + CustomMessages::TWO_WAY_CHOICE() + "#OK&No#",
"Se téléporter vers&#un endroit mystérieux#?&" + CustomMessages::TWO_WAY_CHOICE() + "#OK!&Non#",
{QM_RED, QM_GREEN}));
// Lake Hylia water level system
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
@ -493,67 +310,38 @@ void Randomizer::LoadMerchantMessages() {
// Prices have a chance of being 0, and the "sell" message below doesn't really make sense for a free item, so adding a "free" variation here
CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE,
CustomMessage("\x12\x38\x82" "All right! You win! In return for&sparing me, I will give you a&%g{{item}}%w!&Please, take it!\x07\x10\xA3",
"\x12\x38\x82" "In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen &%g{{item}}%w geben!\x07\x10\xA3",
"\x12\x38\x82" "J'me rends! Laisse-moi partir et en&échange, je te donne un &%g{{item}}%w! Vas-y prends le!\x07\x10\xA3"));
CustomMessage("\x12\x38\x82" "All right! You win! In return for&sparing me, I will give you a&%g[[item]]%w!&Please, take it!\x07\x10\xA3",
"\x12\x38\x82" "In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen &%g[[item]]%w geben!\x07\x10\xA3",
"\x12\x38\x82" "J'me rends! Laisse-moi partir et en&échange, je te donne un &%g[[item]]%w! Vas-y prends le!\x07\x10\xA3"));
CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM,
CustomMessage("\x12\x38\x82" "All right! You win! In return for&sparing me, I will sell you a&%g{{item}}%w!&%r{{price}} Rupees%w it is!\x07\x10\xA3",
"\x12\x38\x82" "Aufgeben! Ich verkaufe dir einen&%g{{item}}%w&für %r{{price}} Rubine%w!\x07\x10\xA3",
"\x12\x38\x82" "J'abandonne! Tu veux bien m'acheter&un %g{{item}}%w?&Ça fera %r{{price}} Rubis%w!\x07\x10\xA3"));
CustomMessage("\x12\x38\x82" "All right! You win! In return for&sparing me, I will sell you a&%g[[item]]%w!&%r[[price]] Rupees%w it is!\x07\x10\xA3",
"\x12\x38\x82" "Aufgeben! Ich verkaufe dir einen&%g[[item]]%w&für %r[[price]] Rubine%w!\x07\x10\xA3",
"\x12\x38\x82" "J'abandonne! Tu veux bien m'acheter&un %g[[item]]%w?&Ça fera %r[[price]] Rubis%w!\x07\x10\xA3"));
//Carpet Salesman
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_10,
CustomMessage(ctx->GetHint(RH_BEAN_SALESMAN)->GetText().GetEnglish(),
ctx->GetHint(RH_BEAN_SALESMAN)->GetText().GetGerman(),
ctx->GetHint(RH_BEAN_SALESMAN)->GetText().GetFrench()));
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2,
CustomMessage("Finally! Now I can go back to being &an %rarms dealer%w!",
/*german*/"Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!",
/*french*/ "Squalala! Je vais enfin pouvoir &%rprendre des vacances%w!"));
// Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are
// prompted buy/don't buy
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM,
CustomMessage("\x08%r[[item]] [[price]] Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02",
"\x08%r[[item]] [[price]] Rubine&%wSonderangebot! NUR NOCH EINES VERFÜGBAR!&Beeilen Sie sich!\x0A\x02",
"\x08%r[[item]] [[price]] Rubis&%wOffre spéciale! DERNIER EN STOCK!&Faites vite!\x0A\x02"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM,
CustomMessage("\x08[[item]] [[price]] Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02",
"\x08[[item]] [[price]] Rubine\x09&&\x1B%gKaufen&Nicht kaufen%w\x09\x02",
"\x08[[item]] [[price]] Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_100,
CustomMessage("I never thought I'd say this, but I'm &selling the last %rMagic Bean%w. %r99%w Rupees...\x1B&%gYes&No%w",
"\x1B&%gJa&Nein%w",
"Je te vends mon dernier %rHaricot&magique%w pour %r99 Rubis%w.\x1B&%gAcheter&Ne pas acheter%w"));
//Setup for merchant text boxes
//Medigoron
//RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_MEDIGORON,
CustomMessage(ctx->GetHint(RH_MEDIGORON)->GetText().GetEnglish(),
ctx->GetHint(RH_MEDIGORON)->GetText().GetGerman(),
ctx->GetHint(RH_MEDIGORON)->GetText().GetFrench()));
//Granny Shop
//RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_GRANNYS_SHOP,
CustomMessage(ctx->GetHint(RH_GRANNYS_SHOP)->GetText().GetEnglish(),
ctx->GetHint(RH_GRANNYS_SHOP)->GetText().GetGerman(),
ctx->GetHint(RH_GRANNYS_SHOP)->GetText().GetFrench()));
//Carpet Salesman
//RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1,
CustomMessage(ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN)->GetText().GetEnglish(),
ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN)->GetText().GetGerman(),
ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN)->GetText().GetFrench()));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2,
CustomMessage(ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN_POST)->GetText().GetEnglish(),
ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN_POST)->GetText().GetGerman(),
ctx->GetHint(RH_WASTELAND_BOMBCHU_SALESMAN_POST)->GetText().GetFrench()));
// Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are
// prompted buy/don't buy
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM,
CustomMessage("\x08%r{{item}} {{price}} Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02",
"\x08%r{{item}} {{price}} Rubine&%wSonderangebot! NUR NOCH EINES VERFÜGBAR!&Beeilen Sie sich!\x0A\x02",
"\x08%r{{item}} {{price}} Rubis&%wOffre spéciale! DERNIER EN STOCK!&Faites vite!\x0A\x02"));
CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM,
CustomMessage("\x08{{item}} {{price}} Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02",
"\x08{{item}} {{price}} Rubine\x09&&\x1B%gKaufen&Nicht kaufen%w\x09\x02",
"\x08{{item}} {{price}} Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02"));
"\x1B&%gJa&Nein%w",
"Je te vends mon dernier %rHaricot&magique%w pour %r99 Rubis%w.\x1B&%gAcheter&Ne pas acheter%w"));
}
bool Randomizer::IsTrialRequired(RandomizerInf trial) {
@ -1654,7 +1442,7 @@ Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum,
case SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS:
switch (actorParams) {
case 14342:
specialRc = RC_TOT_LEFT_GOSSIP_STONE;
specialRc = RC_TOT_LEFTMOST_GOSSIP_STONE;
break;
case 14599:
specialRc = RC_TOT_LEFT_CENTER_GOSSIP_STONE;
@ -1663,7 +1451,7 @@ Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum,
specialRc = RC_TOT_RIGHT_CENTER_GOSSIP_STONE;
break;
case 15120:
specialRc = RC_TOT_RIGHT_GOSSIP_STONE;
specialRc = RC_TOT_RIGHTMOST_GOSSIP_STONE;
break;
}
break;
@ -1702,11 +1490,11 @@ Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum,
switch (actorParams) {
case 15362:
case 14594:
specialRc = RC_JABU_GOSSIP_STONE;
specialRc = RC_ZF_JABU_GOSSIP_STONE;
break;
case 14849:
case 14337:
specialRc = RC_FAIRY_GOSSIP_STONE;
specialRc = RC_ZF_FAIRY_GOSSIP_STONE;
break;
}
break;
@ -2601,109 +2389,51 @@ void RandomizerSettingsWindow::UpdateElement() {
}
}
CustomMessage Randomizer::ReplaceWithItemName(CustomMessage message, std::string&& toReplace, RandomizerCheck hintedCheck){
auto ctx = Rando::Context::GetInstance();
RandomizerGet targetRG = ctx->GetItemLocation(hintedCheck)->GetPlacedRandomizerGet();
std::array<std::string, LANGUAGE_MAX> itemName;
if (targetRG == RG_ICE_TRAP) {
targetRG = ctx->overrides[hintedCheck].LooksLike();
itemName = {
ctx->overrides[hintedCheck].GetTrickName().english,
ctx->overrides[hintedCheck].GetTrickName().french,
ctx->overrides[hintedCheck].GetTrickName().english
};
} else {
itemName = {
Rando::StaticData::RetrieveItem(targetRG).GetName().english,
Rando::StaticData::RetrieveItem(targetRG).GetName().french,
Rando::StaticData::RetrieveItem(targetRG).GetName().english,
};
}
message.Replace(std::move(toReplace), std::move(itemName[0]), std::move(itemName[1]), std::move(itemName[2]));
return message;
}
CustomMessage Randomizer::GetMiscHintMessage(TextIDs textToGet, RandomizerCheck hintedCheck, RandomizerCheck otherCheck) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, textToGet);
messageEntry = ReplaceWithItemName(messageEntry, "{{item1}}", hintedCheck);
if (otherCheck != RC_UNKNOWN_CHECK){
messageEntry = ReplaceWithItemName(messageEntry, "{{item2}}", otherCheck);
}
return messageEntry;
}
CustomMessage Randomizer::GetCursedSkullMessage(s16 params, RandomizerCheck hintedCheck) {
auto ctx = Rando::Context::GetInstance();
CustomMessage messageEntry = GetMiscHintMessage(TEXT_SKULLTULA_PEOPLE_IM_CURSED, hintedCheck);
messageEntry.Replace("{{params}}", std::to_string(params*10));
return messageEntry;
}
CustomMessage Randomizer::GetSheikMessage(s16 scene, u16 originalTextId) {
auto ctx = Rando::Context::GetInstance();
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, originalTextId);
CustomMessage messageEntry;
switch (scene) {
case SCENE_TEMPLE_OF_TIME:
if (originalTextId == TEXT_SHEIK_NEED_HOOK) {
messageEntry.Replace("{{message}}",
if (!CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_GANONS_TOWER)) {
messageEntry = CustomMessage(
"@,&meet me at %gGanon's Castle%w&once you obtain the %rkey to his lair%w.",
"@, wir treffen uns bei %gGanons Schloß%w,&sobald Du den %rSchlüssel zu&seinem Verließ%w hast.",
"Retrouve-moi au %gChâteau de Ganon%w une&fois que tu auras obtenu la&Mrclé de son repaire%w.");
} else {
messageEntry.Replace("{{message}}",
messageEntry = CustomMessage(
"The time has come. Prepare yourself.",
"Die Zeit ist gekommen.&Mach Dich bereit.",
"Le moment est venu @.&Tu ferais bien de te préparer.");
}
break;
case SCENE_INSIDE_GANONS_CASTLE:
if (originalTextId == TEXT_SHEIK_NEED_HOOK) {
//If MS shuffle is on, Sheik will hint both MS and LA as long as Link doesn't have both, to prevent hint lockout.
//Otherwise, she'll only give LA hint so only LA is required to move on.
bool needRequirements = GetRandoSettingValue(RSK_SHUFFLE_MASTER_SWORD) ?
(!CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER) || INV_CONTENT(ITEM_ARROW_LIGHT) != ITEM_ARROW_LIGHT) :
(INV_CONTENT(ITEM_ARROW_LIGHT) != ITEM_ARROW_LIGHT);
if (needRequirements) {
messageEntry.Replace("{{message}}", ctx->GetHint(RH_SHEIK_LIGHT_ARROWS)->GetText().GetEnglish().c_str(), ctx->GetHint(RH_SHEIK_LIGHT_ARROWS)->GetText().GetEnglish().c_str(), ctx->GetHint(RH_SHEIK_LIGHT_ARROWS)->GetText().GetFrench().c_str());
} else {
messageEntry.Replace("{{message}}", "You are still ill-equipped to&face %rGanondorf%w."
if (ctx->GetOption(RSK_SHEIK_LA_HINT) && INV_CONTENT(ITEM_ARROW_LIGHT) != ITEM_ARROW_LIGHT) {
messageEntry = ctx->GetHint(RH_SHEIK_HINT)->GetHintMessage(MF_AUTO_FORMAT);
} else if (!(CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER) && INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT &&
CUR_CAPACITY(UPG_QUIVER) >= 30 && gSaveContext.isMagicAcquired)) {
messageEntry = CustomMessage("You are still ill-equipped to&face %rGanondorf%w."
"^Seek out the %cMaster Sword%w,&%rsomething to hold your arrows%w,&and %gmagic%w to summon the %ylight%w.",
"Du bist noch nicht gewappnet um Dich&%rGanondorf%w stellen zu können.^"
"Begib Dich auf die Suche nach dem&%cMaster-Schwert%w, %retwas um deine Pfeilen&einen Sinn zu geben%w,^sowie %gdie Magie%w, um das %yLicht%w&herauf beschwören zu können.",
"@, tu n'es toujours pas prêt à affronter&%rGanondorf%w.^"
"Cherche l'%cÉpée de Légende%w,&%rquelque chose pour ranger tes flèches%w&et de la %gmagie%w pour invoquer la&%ylumière%w.");
}
} else {
if (!Flags_GetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER)) {
messageEntry.Replace("{{message}}",
} else if (!Flags_GetEventChkInf(EVENTCHKINF_DISPELLED_GANONS_TOWER_BARRIER)){
messageEntry = CustomMessage(
"You may have what you need to defeat&%rthe Evil King%w, but the %cbarrier%w still&stands.^Complete the remaining %gtrials%w&to destroy it."
);
} else {
messageEntry.Replace("{{message}}",
);
} else {
messageEntry = CustomMessage(
"If you're ready, then proceed.^Good luck.",
"Wenn Du bereit bist, so schreite&voran.^Viel Glück.",
"Si tu es prêt, tu peux y aller.^Bonne chance.");
}
}
break;
}
return messageEntry;
}
CustomMessage Randomizer::GetSariaMessage(u16 originalTextId) {
if (originalTextId == TEXT_SARIA_SFM || (originalTextId >= TEXT_SARIAS_SONG_FACE_TO_FACE && originalTextId <= TEXT_SARIAS_SONG_CHANNELING_POWER)) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SARIAS_SONG_FACE_TO_FACE);
CustomMessage messageEntry2 = messageEntry;
std::string code = originalTextId == TEXT_SARIA_SFM ? "" : "\x0B";
messageEntry2.Replace("$C", std::move(code));
return messageEntry2;
}
}
CustomMessage Randomizer::GetFishingPondOwnerMessage(u16 originalTextId) {
CustomMessage hintMessageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_FISHING_POND_START);
auto ctx = Rando::Context::GetInstance();
CustomMessage messageEntry = CustomMessage(
"Sorry, but the pond is closed.&I've lost my good %rfishing pole%w...&Can't go fishing without it!",
"",
@ -2711,7 +2441,7 @@ CustomMessage Randomizer::GetFishingPondOwnerMessage(u16 originalTextId) {
);
if (Rando::Context::GetInstance()->GetOption(RSK_FISHING_POLE_HINT)) {
messageEntry = messageEntry + hintMessageEntry;
messageEntry = messageEntry + CustomMessage(ctx->GetHint(RH_FISHING_POLE)->GetHintMessage());
}
// if the fishing pond guy doesnt remember me i will cry :(
@ -2723,61 +2453,38 @@ CustomMessage Randomizer::GetFishingPondOwnerMessage(u16 originalTextId) {
) + messageEntry;
}
messageEntry.Format();
messageEntry.Format(); //RANDOTODO why is this needed when it's not elsewhere....
return messageEntry;
}
CustomMessage Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) {
auto ctx = Rando::Context::GetInstance();
auto ctx = Rando::Context::GetInstance(); //RANDOTODO If scrubs are allowed to have ambiguous hints, they need to be RandomiserHint objects for logging
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId);
RandomizerCheck rc = GetCheckFromRandomizerInf(randomizerInf);
RandomizerGet shopItemGet = ctx->GetItemLocation(rc)->GetPlacedRandomizerGet();
std::array<std::string, LANGUAGE_MAX> shopItemName;
CustomMessage shopItemName;
if (mysterious) {
shopItemName = {
"mysterious item",
"mysteriösen Gegenstand",
"objet mystérieux"
};
shopItemName = Rando::StaticData::hintTextTable[RHT_MYSTERIOUS_ITEM].GetHintMessage();
// TODO: This should eventually be replaced with a full fledged trick model & trick name system
} else if (shopItemGet == RG_ICE_TRAP) {
shopItemGet = ctx->overrides[rc].LooksLike();
shopItemName = {
std::string(ctx->overrides[rc].GetTrickName().english),
std::string(ctx->overrides[rc].GetTrickName().french),
std::string(ctx->overrides[rc].GetTrickName().english)
};
shopItemName = CustomMessage(ctx->overrides[rc].GetTrickName());
} else {
auto shopItem = Rando::StaticData::RetrieveItem(shopItemGet);
shopItemName = {
shopItem.GetName().english,
shopItem.GetName().french,
shopItem.GetName().english,
};
shopItemName = {shopItem.GetName()};
}
u16 shopItemPrice = ctx->GetItemLocation(rc)->GetPrice();
if (textId == TEXT_SCRUB_RANDOM && shopItemPrice == 0) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE);
} else if (textId == TEXT_GRANNYS_SHOP) {
// Capitalize the first letter for the item in Granny's text as the item is the first word presented
for (auto &itemName : shopItemName) {
itemName[0] = std::toupper(itemName[0]);
}
}
messageEntry.Replace("{{item}}", std::move(shopItemName[0]), std::move(shopItemName[1]), std::move(shopItemName[2]));
messageEntry.Replace("{{price}}", std::to_string(shopItemPrice));
messageEntry.Replace("[[item]]", shopItemName);
messageEntry.Replace("[[price]]", std::to_string(shopItemPrice));
return messageEntry;
}
static const char* mapGetItemHints[3][2] = {
{ " It's ordinary.", " It's masterful!" },
{ "&Sieht aus wie immer.", " &Man kann darauf die Worte&%r\"Master Quest\"%w entziffern..." },
{ "&Elle vous semble %rordinaire%w.", "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus." },
};
CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId);
int sceneNum;
@ -2818,11 +2525,11 @@ CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
(GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER &&
GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12)
) {
messageEntry.Replace("{{typeHint}}", "");
messageEntry.Replace("[[typeHint]]", "");
} else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) {
messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]);
messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_MASTERFUL].GetHintMessage());
} else {
messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][0], mapGetItemHints[1][0], mapGetItemHints[2][0]);
messageEntry.Replace("[[typeHint]]", Rando::StaticData::hintTextTable[RHT_DUNGEON_ORDINARY].GetHintMessage());
}
return messageEntry;
@ -2848,16 +2555,16 @@ void CreateRupeeMessages() {
for (u8 rupee : rupees) {
switch (rupee) {
case TEXT_BLUE_RUPEE:
rupeeText = "\x05\x03 5 {{rupee}}\x05\x00";
rupeeText = "\x05\x03 5 [[rupee]]\x05\x00";
break;
case TEXT_RED_RUPEE:
rupeeText = "\x05\x01 20 {{rupee}}\x05\x00";
rupeeText = "\x05\x01 20 [[rupee]]\x05\x00";
break;
case TEXT_PURPLE_RUPEE:
rupeeText = "\x05\x05 50 {{rupee}}\x05\x00";
rupeeText = "\x05\x05 50 [[rupee]]\x05\x00";
break;
case TEXT_HUGE_RUPEE:
rupeeText = "\x05\x06 200 {{rupee}}\x05\x00";
rupeeText = "\x05\x06 200 [[rupee]]\x05\x00";
break;
}
customMessageManager->CreateMessage(
@ -2869,37 +2576,38 @@ void CreateRupeeMessages() {
CustomMessage Randomizer::GetRupeeMessage(u16 rupeeTextId) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId);
messageEntry.Replace("{{rupee}}", RandomElement(englishRupeeNames),
RandomElement(germanRupeeNames), RandomElement(frenchRupeeNames));
messageEntry.Replace("[[rupee]]", CustomMessage(RandomElement(englishRupeeNames),
RandomElement(germanRupeeNames),
RandomElement(frenchRupeeNames)));
return messageEntry;
}
void CreateTriforcePieceMessages() {
CustomMessage TriforcePieceMessages[NUM_TRIFORCE_PIECE_MESSAGES] = {
{ "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. It's a start!",
"Ein %yTriforce-Splitter%w! Du hast&%g{{current}}%w von %c{{required}}%w gefunden. Es ist ein&Anfang!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. C'est un début!" },
{ "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. It's a start!",
"Ein %yTriforce-Splitter%w! Du hast&%g[[current]]%w von %c[[required]]%w gefunden. Es ist ein&Anfang!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste %c[[remaining]]%w à trouver. C'est un début!" },
{ "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Progress!",
"Ein %yTriforce-Splitter%w! Du hast&%g{{current}}%w von %c{{required}}%w gefunden. Es geht voran!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. Ça avance!" },
{ "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Progress!",
"Ein %yTriforce-Splitter%w! Du hast&%g[[current]]%w von %c[[required]]%w gefunden. Es geht voran!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste %c[[remaining]]%w à trouver. Ça avance!" },
{ "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Over half-way&there!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g{{current}}%w von %c{{required}}%w gefunden. Schon&über die Hälfte!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. Il en reste un&peu moins que la moitié!" },
{ "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Over half-way&there!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g[[current]]%w von %c[[required]]%w gefunden. Schon&über die Hälfte!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste %c[[remaining]]%w à trouver. Il en reste un&peu moins que la moitié!" },
{ "You found a %yTriforce Piece%w!&%g{{current}}%w down, %c{{remaining}}%w to go. Almost done!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g{{current}}%w von %c{{required}}%w gefunden. Fast&geschafft!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g{{current}}%w, il en&reste %c{{remaining}}%w à trouver. C'est presque&terminé!" },
{ "You found a %yTriforce Piece%w!&%g[[current]]%w down, %c[[remaining]]%w to go. Almost done!",
"Ein %yTriforce-Splitter%w! Du hast&schon %g[[current]]%w von %c[[required]]%w gefunden. Fast&geschafft!",
"Vous trouvez un %yFragment de la&Triforce%w! Vous en avez %g[[current]]%w, il en&reste %c[[remaining]]%w à trouver. C'est presque&terminé!" },
{ "You completed the %yTriforce of&Courage%w! %gGG%w!",
"Das %yTriforce des Mutes%w! Du hast&alle Splitter gefunden. %gGut gemacht%w!",
"Vous avez complété la %yTriforce&du Courage%w! %gFélicitations%w!" },
{ "You found a spare %yTriforce Piece%w!&You only needed %c{{required}}%w, but you have %g{{current}}%w!",
"Ein übriger %yTriforce-Splitter%w! Du&hast nun %g{{current}}%w von %c{{required}}%w nötigen gefunden.",
"Vous avez trouvé un %yFragment de&Triforce%w en plus! Vous n'aviez besoin&que de %c{{required}}%w, mais vous en avez %g{{current}}%w en&tout!" },
{ "You found a spare %yTriforce Piece%w!&You only needed %c[[required]]%w, but you have %g[[current]]%w!",
"Ein übriger %yTriforce-Splitter%w! Du&hast nun %g[[current]]%w von %c[[required]]%w nötigen gefunden.",
"Vous avez trouvé un %yFragment de&Triforce%w en plus! Vous n'aviez besoin&que de %c[[required]]%w, mais vous en avez %g[[current]]%w en&tout!" },
};
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::triforcePieceMessageTableID);
@ -2932,9 +2640,9 @@ CustomMessage Randomizer::GetTriforcePieceMessage() {
CustomMessage messageEntry =
CustomMessageManager::Instance->RetrieveMessage(Randomizer::triforcePieceMessageTableID, messageIndex);
messageEntry.Replace("{{current}}", std::to_string(current), std::to_string(current), std::to_string(current));
messageEntry.Replace("{{remaining}}", std::to_string(remaining), std::to_string(remaining), std::to_string(remaining));
messageEntry.Replace("{{required}}", std::to_string(required), std::to_string(required), std::to_string(required));
messageEntry.Replace("[[current]]", std::to_string(current));
messageEntry.Replace("[[remaining]]", std::to_string(remaining));
messageEntry.Replace("[[required]]", std::to_string(required));
return messageEntry;
}
@ -3156,11 +2864,11 @@ void CreateFireTempleGoronMessages() {
"je&ressemble à n'importe quel Goron?",
},
{
"How long has it been, do you know?^%r{{days}}%w days!?^Oh no, and it's %r\x1F%w?&I have to check on my "
"How long has it been, do you know?^%r[[days]]%w days!?^Oh no, and it's %r\x1F%w?&I have to check on my "
"cake!!",
"Weißt du zufällig, wie viele Tage&vergangen sind?^%r{{days}}%w Tage!?^Oh je, und es ist %r\x1F%w Uhr? "
"Weißt du zufällig, wie viele Tage&vergangen sind?^%r[[days]]%w Tage!?^Oh je, und es ist %r\x1F%w Uhr? "
"Ich&muss dringend nach meinem Kuchen&sehen!!!",
"Cela fait combien de temps que&je suis enfermé ici?&Non mais je ne vais pas crier.^COMBIEN?! %r{{days}}%w "
"Cela fait combien de temps que&je suis enfermé ici?&Non mais je ne vais pas crier.^COMBIEN?! %r[[days]]%w "
"JOURS!?^En plus il est %r\x1F%w...&Il faut vraiment que je rentre...",
},
{
@ -3182,11 +2890,11 @@ void CreateFireTempleGoronMessages() {
"aller.&A plus tard.",
},
{ "Do you know about %b\x9f%w?&It's this weird symbol that's been&in my dreams lately...^Apparently, you "
"pressed it %b{{a_btn}}%w times.^Wow.",
"pressed it %b[[a_btn]]%w times.^Wow.",
"Weißt du über %b\x9f%w bescheid?&Es sind Symbole, die mir&in letzter Zeit öfter in&meinen Träumen "
"erschienen sind...^Es scheint, dass du sie schon&%b{{a_btn}}%w mal betätigt hast.^Faszinierend...",
"erschienen sind...^Es scheint, dass du sie schon&%b[[a_btn]]%w mal betätigt hast.^Faszinierend...",
"Tu as déjà entendu parler du&symbole %b\x9f%w?&C'est un symbole bizarre qui est&apparu dans mes rêves "
"dernièrement...^Apparemment, tu as appuyé dessus&%b{{a_btn}}%w fois.^Wow..." },
"dernièrement...^Apparemment, tu as appuyé dessus&%b[[a_btn]]%w fois.^Wow..." },
{
"\x13\x1A"
"Boy, you must be hot!&Get yourself a bottle of&%rLon Lon Milk%w right away and cool&down, for only %g30%w "
@ -3223,8 +2931,8 @@ void CreateFireTempleGoronMessages() {
CustomMessage Randomizer::GetGoronMessage(u16 index) {
CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, goronIDs[index]);
messageEntry.Replace("{{days}}", std::to_string(gSaveContext.totalDays));
messageEntry.Replace("{{a_btn}}", std::to_string(gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]));
messageEntry.Replace("[[days]]", std::to_string(gSaveContext.totalDays));
messageEntry.Replace("[[a_btn]]", std::to_string(gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]));
return messageEntry;
}
@ -3377,45 +3085,45 @@ void Randomizer::CreateCustomMessages() {
"Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"),
GIMESSAGE(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP,
"You found the %gDeku Tree &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%gDeku-Baum%w!{{typeHint}}",
"Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!{{typeHint}}"),
"You found the %gDeku Tree &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%gDeku-Baum%w![[typeHint]]",
"Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w![[typeHint]]"),
GIMESSAGE(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP,
"You found the %rDodongo's Cavern &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für&%rDodongos Höhle%w!{{typeHint}}",
"Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!{{typeHint}}"),
"You found the %rDodongo's Cavern &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für&%rDodongos Höhle%w![[typeHint]]",
"Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w![[typeHint]]"),
GIMESSAGE(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP,
"You found the %bJabu Jabu's Belly &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für&%bJabu-Jabus Bauch%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!{{typeHint}}"),
"You found the %bJabu Jabu's Belly &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für&%bJabu-Jabus Bauch%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w![[typeHint]]"),
GIMESSAGE(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP,
"You found the %gForest Temple &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%gWaldtempel%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!{{typeHint}}"),
"You found the %gForest Temple &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%gWaldtempel%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w![[typeHint]]"),
GIMESSAGE(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP,
"You found the %rFire Temple &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%rFeuertempel%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!{{typeHint}}"),
"You found the %rFire Temple &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%rFeuertempel%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%rTemple du Feu%w![[typeHint]]"),
GIMESSAGE(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP,
"You found the %bWater Temple &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%bWassertempel%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!{{typeHint}}"),
"You found the %bWater Temple &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%bWassertempel%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w![[typeHint]]"),
GIMESSAGE(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP,
"You found the %ySpirit Temple &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%yGeistertempel%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!{{typeHint}}"),
"You found the %ySpirit Temple &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%yGeistertempel%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w![[typeHint]]"),
GIMESSAGE(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP,
"You found the %pShadow Temple &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%pSchattentempel%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!{{typeHint}}"),
"You found the %pShadow Temple &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%pSchattentempel%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w![[typeHint]]"),
GIMESSAGE(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP,
"You found the %pBottom of the &Well %wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für den&%pGrund des Brunnens%w!{{typeHint}}",
"Vous obtenez la %rCarte %wdu &%pPuits%w!{{typeHint}}"),
"You found the %pBottom of the &Well %wMap![[typeHint]]",
"Du erhältst die %rKarte%w für den&%pGrund des Brunnens%w![[typeHint]]",
"Vous obtenez la %rCarte %wdu &%pPuits%w![[typeHint]]"),
GIMESSAGE(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP,
"You found the %cIce Cavern &%wMap!{{typeHint}}",
"Du erhältst die %rKarte%w für die&%cEishöhle%w!{{typeHint}}",
"Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!{{typeHint}}"),
"You found the %cIce Cavern &%wMap![[typeHint]]",
"Du erhältst die %rKarte%w für die&%cEishöhle%w![[typeHint]]",
"Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w![[typeHint]]"),
GIMESSAGE(RG_DEKU_TREE_COMPASS, ITEM_COMPASS,
"You found the %gDeku Tree &%wCompass!",

View File

@ -59,13 +59,9 @@ class Randomizer {
ItemObtainability GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck);
ItemObtainability GetItemObtainabilityFromRandomizerGet(RandomizerGet randomizerCheck);
CustomMessage GetSheikMessage(s16 scene, u16 originalTextId);
CustomMessage ReplaceWithItemName(CustomMessage message, std::string&& toReplace, RandomizerCheck hintedCheck);
CustomMessage GetMiscHintMessage(TextIDs textToGet, RandomizerCheck hintedCheck, RandomizerCheck otherCheck = RC_UNKNOWN_CHECK);
CustomMessage GetSariaMessage(u16 originalTextId);
CustomMessage GetFishingPondOwnerMessage(u16 originalTextId);
CustomMessage GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious = false);
RandomizerCheck GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
CustomMessage GetCursedSkullMessage(s16 params, RandomizerCheck hintedCheck);
CustomMessage GetGoronMessage(u16 index);
CustomMessage GetMapGetItemMessageWithHint(GetItemEntry itemEntry);
static void CreateCustomMessages();

View File

@ -38,14 +38,18 @@ typedef enum {
} RandomizerCheckStatus;
typedef enum {
HINT_TYPE_STATIC,
HINT_TYPE_HINT_KEY,
HINT_TYPE_AREA,
HINT_TYPE_ITEM,
HINT_TYPE_TRIAL,
HINT_TYPE_WOTH, // Way of the Hero
HINT_TYPE_BARREN,
HINT_TYPE_ENTRANCE,
HINT_TYPE_ITEM_AREA,
HINT_TYPE_ITEM_LOCATION,
HINT_TYPE_JUNK,
HINT_TYPE_MERCHANT,
HINT_TYPE_ALTAR_CHILD,
HINT_TYPE_ALTAR_ADULT,
HINT_TYPE_WOTH, // Way of the Hero
HINT_TYPE_FOOLISH,
HINT_TYPE_MESSAGE,
HINT_TYPE_MAX
} HintType;
@ -90,24 +94,34 @@ typedef enum {
RA_MAX
} RandomizerArea;
typedef enum {
TK_LIGHT_TRIAL,
TK_FOREST_TRIAL,
TK_FIRE_TRIAL,
TK_WATER_TRIAL,
TK_SPIRIT_TRIAL,
TK_SHADOW_TRIAL,
TK_MAX
} TrialKey;
// 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_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, // RANDOTODO replace this once we implement it, just using it to exclude for now
RCTYPE_LINKS_POCKET, // RANDOTODO this feels hacky, replace with better starting items
RCTYPE_GOSSIP_STONE, // RANDOTODO make these into event access
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)
@ -1388,46 +1402,46 @@ typedef enum {
RC_PIERRE,
RC_DELIVER_RUTOS_LETTER,
RC_MASTER_SWORD_PEDESTAL,
RC_COLOSSUS_GOSSIP_STONE,
RC_DMC_GOSSIP_STONE,
RC_DMC_UPPER_GROTTO_GOSSIP_STONE,
RC_DMT_GOSSIP_STONE,
RC_DMT_STORMS_GROTTO_GOSSIP_STONE,
RC_DODONGOS_CAVERN_GOSSIP_STONE,
RC_FAIRY_GOSSIP_STONE,
RC_GC_MAZE_GOSSIP_STONE,
RC_GC_MEDIGORON_GOSSIP_STONE,
RC_GV_GOSSIP_STONE,
RC_GY_GOSSIP_STONE,
RC_HC_MALON_GOSSIP_STONE,
RC_HC_ROCK_WALL_GOSSIP_STONE,
RC_HC_STORMS_GROTTO_GOSSIP_STONE,
RC_HF_COW_GROTTO_GOSSIP_STONE,
RC_HF_NEAR_MARKET_GOSSIP_STONE,
RC_HF_OPEN_GROTTO_GOSSIP_STONE,
RC_HF_SOUTHEAST_GOSSIP_STONE,
RC_JABU_GOSSIP_STONE,
RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE,
RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE,
RC_KF_GOSSIP_STONE,
RC_KF_STORMS_GOSSIP_STONE,
RC_KAK_OPEN_GROTTO_GOSSIP_STONE,
RC_LH_LAB_GOSSIP_STONE,
RC_LH_SOUTHEAST_GOSSIP_STONE,
RC_LH_SOUTHWEST_GOSSIP_STONE,
RC_KF_STORMS_GROTTO_GOSSIP_STONE,
RC_LW_GOSSIP_STONE,
RC_LW_NEAR_SHORTCUTS_GOSSIP_STONE,
RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE,
RC_SFM_MAZE_LOWER_GOSSIP_STONE,
RC_SFM_MAZE_UPPER_GOSSIP_STONE,
RC_SFM_SARIA_GOSSIP_STONE,
RC_HF_COW_GROTTO_GOSSIP_STONE,
RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE,
RC_HF_OPEN_GROTTO_GOSSIP_STONE,
RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE,
RC_TOT_LEFT_CENTER_GOSSIP_STONE,
RC_TOT_LEFT_GOSSIP_STONE,
RC_TOT_LEFTMOST_GOSSIP_STONE,
RC_TOT_RIGHT_CENTER_GOSSIP_STONE,
RC_TOT_RIGHT_GOSSIP_STONE,
RC_ZD_GOSSIP_STONE,
RC_TOT_RIGHTMOST_GOSSIP_STONE,
RC_HC_MALON_GOSSIP_STONE,
RC_HC_ROCK_WALL_GOSSIP_STONE,
RC_HC_STORMS_GROTTO_GOSSIP_STONE,
RC_KAK_OPEN_GROTTO_GOSSIP_STONE,
RC_GRAVEYARD_GOSSIP_STONE,
RC_DMT_GOSSIP_STONE,
RC_DMT_STORMS_GROTTO_GOSSIP_STONE,
RC_GC_MAZE_GOSSIP_STONE,
RC_GC_MEDIGORON_GOSSIP_STONE,
RC_DMC_GOSSIP_STONE,
RC_DMC_UPPER_GROTTO_GOSSIP_STONE,
RC_ZR_NEAR_DOMAIN_GOSSIP_STONE,
RC_ZR_NEAR_GROTTOS_GOSSIP_STONE,
RC_ZR_OPEN_GROTTO_GOSSIP_STONE,
RC_ZD_GOSSIP_STONE,
RC_ZF_JABU_GOSSIP_STONE,
RC_ZF_FAIRY_GOSSIP_STONE,
RC_LH_LAB_GOSSIP_STONE,
RC_LH_SOUTHEAST_GOSSIP_STONE,
RC_LH_SOUTHWEST_GOSSIP_STONE,
RC_GV_GOSSIP_STONE,
RC_COLOSSUS_GOSSIP_STONE,
RC_DODONGOS_CAVERN_GOSSIP_STONE,
RC_KF_STORMS_GROTTO_BEEHIVE_LEFT,
RC_KF_STORMS_GROTTO_BEEHIVE_RIGHT,
RC_LW_NEAR_SHORTCUTS_GROTTO_BEEHIVE_LEFT,
@ -1996,75 +2010,83 @@ typedef enum {
typedef enum {
RH_NONE,
RH_COLOSSUS_GOSSIP_STONE,
RH_DMC_GOSSIP_STONE,
RH_DMC_UPPER_GROTTO_GOSSIP_STONE,
RH_DMT_GOSSIP_STONE,
RH_DMT_STORMS_GROTTO_GOSSIP_STONE,
RH_DODONGOS_CAVERN_GOSSIP_STONE,
RH_ZF_FAIRY_GOSSIP_STONE,
RH_GC_MAZE_GOSSIP_STONE,
RH_GC_MEDIGORON_GOSSIP_STONE,
RH_GV_GOSSIP_STONE,
RH_GRAVEYARD_GOSSIP_STONE,
RH_HC_MALON_GOSSIP_STONE,
RH_HC_ROCK_WALL_GOSSIP_STONE,
RH_HC_STORMS_GROTTO_GOSSIP_STONE,
RH_KF_DEKU_TREE_LEFT_GOSSIP_STONE,
RH_KF_DEKU_TREE_RIGHT_GOSSIP_STONE,
RH_KF_GOSSIP_STONE,
RH_KF_STORMS_GROTTO_GOSSIP_STONE,
RH_LW_GOSSIP_STONE,
RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE,
RH_SFM_MAZE_NEAR_LW_GOSSIP_STONE,
RH_SFM_MAZE_CENTER_GOSSIP_STONE,
RH_SFM_SARIA_GOSSIP_STONE,
RH_HF_COW_GROTTO_GOSSIP_STONE,
RH_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE,
RH_HF_OPEN_GROTTO_GOSSIP_STONE,
RH_HF_SOUTHEAST_GROTTO_GOSSIP_STONE,
RH_ZF_JABU_GOSSIP_STONE,
RH_KF_DEKU_TREE_GOSSIP_STONE_LEFT,
RH_KF_DEKU_TREE_GOSSIP_STONE_RIGHT,
RH_KF_GOSSIP_STONE,
RH_KF_STORMS_GROTTO_GOSSIP_STONE,
RH_TOT_LEFT_CENTER_GOSSIP_STONE,
RH_TOT_LEFTMOST_GOSSIP_STONE,
RH_TOT_RIGHT_CENTER_GOSSIP_STONE,
RH_TOT_RIGHTMOST_GOSSIP_STONE,
RH_HC_MALON_GOSSIP_STONE,
RH_HC_ROCK_WALL_GOSSIP_STONE,
RH_HC_STORMS_GROTTO_GOSSIP_STONE,
RH_KAK_OPEN_GROTTO_GOSSIP_STONE,
RH_LH_LAB_GOSSIP_STONE,
RH_LH_SOUTHEAST_GOSSIP_STONE,
RH_LH_SOUTHWEST_GOSSIP_STONE,
RH_LW_GOSSIP_STONE,
RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE,
RH_SFM_MAZE_GOSSIP_STONE_LOWER,
RH_SFM_MAZE_GOSSIP_STONE_UPPER,
RH_SFM_SARIA_GOSSIP_STONE,
RH_TOT_GOSSIP_STONE_LEFT_CENTER,
RH_TOT_GOSSIP_STONE_LEFT,
RH_TOT_GOSSIP_STONE_RIGHT_CENTER,
RH_TOT_GOSSIP_STONE_RIGHT,
RH_ZD_GOSSIP_STONE,
RH_GRAVEYARD_GOSSIP_STONE,
RH_DMT_GOSSIP_STONE,
RH_DMT_STORMS_GROTTO_GOSSIP_STONE,
RH_GC_MAZE_GOSSIP_STONE,
RH_GC_MEDIGORON_GOSSIP_STONE,
RH_DMC_GOSSIP_STONE,
RH_DMC_UPPER_GROTTO_GOSSIP_STONE,
RH_ZR_NEAR_DOMAIN_GOSSIP_STONE,
RH_ZR_NEAR_GROTTOS_GOSSIP_STONE,
RH_ZR_OPEN_GROTTO_GOSSIP_STONE,
RH_ZD_GOSSIP_STONE,
RH_ZF_JABU_GOSSIP_STONE,
RH_ZF_FAIRY_GOSSIP_STONE,
RH_LH_LAB_GOSSIP_STONE,
RH_LH_SOUTHEAST_GOSSIP_STONE,
RH_LH_SOUTHWEST_GOSSIP_STONE,
RH_GV_GOSSIP_STONE,
RH_COLOSSUS_GOSSIP_STONE,
RH_DODONGOS_CAVERN_GOSSIP_STONE,
RH_GANONDORF_HINT,
RH_GANONDORF_NOHINT,
RH_GANONDORF_JOKE,
RH_SHEIK_HINT,
RH_DAMPES_DIARY,
RH_GREG_RUPEE,
RH_BEAN_SALESMAN,
RH_MEDIGORON,
RH_GRANNYS_SHOP,
RH_WASTELAND_BOMBCHU_SALESMAN,
RH_WASTELAND_BOMBCHU_SALESMAN_POST,
RH_ALTAR_CHILD,
RH_ALTAR_ADULT,
RH_SARIA,
RH_SARIA_HINT,
RH_FISHING_POLE,
RH_SHEIK_LIGHT_ARROWS,
RH_MINUET_WARP_LOC,
RH_BOLERO_WARP_LOC,
RH_SERENADE_WARP_LOC,
RH_REQUIEM_WARP_LOC,
RH_NOCTURNE_WARP_LOC,
RH_PRELUDE_WARP_LOC,
RH_FROGS,
RH_MEDIGORON,
RH_CARPET_SALESMAN,
RH_BEAN_SALESMAN,
RH_GRANNY,
RH_HBA_HINT,
RH_MALON_HINT,
RH_CHICKENS_HINT,
RH_BIG_POES_HINT,
RH_BIGGORON_HINT,
RH_FROGS_HINT,
RH_KAK_10_SKULLS_HINT,
RH_KAK_20_SKULLS_HINT,
RH_KAK_30_SKULLS_HINT,
RH_KAK_40_SKULLS_HINT,
RH_KAK_50_SKULLS_HINT,
RH_KAK_100_SKULLS_HINT,
RH_MAX,
} RandomizerHintKey;
} RandomizerHint;
typedef enum {
RHT_NONE,
RHT_PREFIX,
RHT_WAY_OF_THE_HERO,
RHT_PLUNDERING,
RHT_FOOLISH,
RHT_CAN_BE_FOUND_AT,
RHT_HOARDS,
@ -3163,7 +3185,6 @@ typedef enum {
RHT_BUY_RED_POTION_50,
RHT_TRIFORCE,
RHT_HINT,
RHT_HINT_MYSTERIOUS,
RHT_TYCOON_WALLET,
RHT_CHILD_WALLET,
RHT_HOOKSHOT,
@ -3208,6 +3229,8 @@ typedef enum {
RHT_BRONZE_SCALE,
RHT_FISHING_POLE,
RHT_EPONA,
RHT_HINT_MYSTERIOUS,
RHT_MYSTERIOUS_ITEM,
// Entrances
RHT_DESERT_COLOSSUS_TO_COLOSSUS_GROTTO,
RHT_GV_GROTTO_LEDGE_TO_GV_OCTOROK_GROTTO,
@ -3367,60 +3390,69 @@ typedef enum {
// Trials
RHT_SIX_TRIALS,
RHT_ZERO_TRIALS,
RHT_FOUR_TO_FIVE_TRIALS,
RHT_ONE_TO_THREE_TRIALS,
RHT_TRIAL_OFF,
RHT_TRIAL_ON,
RHT_LIGHT_TRIAL,
RHT_FOREST_TRIAL,
RHT_FIRE_TRIAL,
RHT_WATER_TRIAL,
RHT_SPIRIT_TRIAL,
RHT_SHADOW_TRIAL,
// Altar
RHT_SPIRITUAL_STONE_TEXT_START,
RHT_CHILD_ALTAR_STONES,
RHT_CHILD_ALTAR_TEXT_END_DOTOPEN,
RHT_CHILD_ALTAR_TEXT_END_DOTSONGONLY,
RHT_CHILD_ALTAR_TEXT_END_DOTCLOSED,
RHT_ADULT_ALTAR_TEXT_START,
RHT_ADULT_ALTAR_MEDALLIONS,
RHT_ADULT_ALTAR_TEXT_END,
// Validation Line
RHT_VALIDATION_LINE,
// Light Arrow Location
RHT_LIGHT_ARROW_LOCATION_HINT,
RHT_SHEIK_LIGHT_ARROW_HINT,
// Master Sword Location
RHT_MASTER_SWORD_LOCATION_HINT,
RHT_SHEIK_MASTER_SWORD_LOCATION_HINT,
// Your Pocket
RHT_YOUR_POCKET,
// Other Hints
RHT_DAMPE_DIARY01,
RHT_DAMPE_DIARY02,
RHT_GREG_HINT01,
RHT_GREG_HINT02,
RHT_SARIA_TEXT01,
RHT_SARIA_TEXT02,
RHT_WARP_TO,
RHT_WARP_CHOICE,
RHT_FROGS_HINT01,
RHT_FROGS_HINT02,
RHT_FISHING_POLE_HINT01,
RHT_FISHING_POLE_HINT02,
// Ganon Line
RHT_GANON_LINE01,
RHT_GANON_LINE02,
RHT_GANON_LINE03,
RHT_GANON_LINE04,
RHT_GANON_LINE05,
RHT_GANON_LINE06,
RHT_GANON_LINE07,
RHT_GANON_LINE08,
RHT_GANON_LINE09,
RHT_GANON_LINE10,
RHT_GANON_LINE11,
// Merchants
RHT_BEAN_SALESMAN_FIRST,
RHT_BEAN_SALESMAN_SECOND,
RHT_MEDIGORON_DIALOG_FIRST,
RHT_MEDIGORON_DIALOG_SECOND,
// Static Item Hints
RHT_GANONDORF_HINT_LA_ONLY,
RHT_GANONDORF_HINT_MS_ONLY,
RHT_GANONDORF_HINT_LA_AND_MS,
RHT_SHEIK_HINT_LA_ONLY,
RHT_DAMPE_DIARY,
RHT_GREG_HINT,
RHT_SARIA_TALK_HINT,
RHT_SARIA_SONG_HINT,
RHT_FISHING_POLE_HINT,
// Static Entrance Hints
RHT_WARP_SONG,
// Static Location Hints
RHT_MEDIGORON_HINT,
RHT_CARPET_SALESMAN_DIALOG_FIRST,
RHT_CARPET_SALESMAN_DIALOG_MYSTERIOUS,
RHT_CARPET_SALESMAN_DIALOG_HINTED,
RHT_CARPET_SALESMAN_DIALOG_FINAL,
RHT_GRANNY_DIALOG,
RHT_BEAN_SALESMAN_HINT,
RHT_GRANNY_HINT,
RHT_HBA_HINT_SIGN,
RHT_HBA_HINT_NOT_ON_HORSE,
RHT_HBA_HINT_INITIAL,
RHT_HBA_HINT_HAVE_1000,
RHT_MALON_HINT_HOW_IS_EPONA,
RHT_MALON_HINT_OBSTICLE_COURSE,
RHT_MALON_HINT_TURNING_EVIL,
RHT_MALON_HINT_INGO_TEMPTED,
RHT_CHICKENS_HINT,
RHT_BIG_POES_HINT,
RHT_BIGGORON_HINT,
RHT_FROGS_HINT,
RHT_SKULLS_HINT,
// Ganon Line
RHT_GANON_JOKE01,
RHT_GANON_JOKE02,
RHT_GANON_JOKE03,
RHT_GANON_JOKE04,
RHT_GANON_JOKE05,
RHT_GANON_JOKE06,
RHT_GANON_JOKE07,
RHT_GANON_JOKE08,
RHT_GANON_JOKE09,
RHT_GANON_JOKE10,
RHT_GANON_JOKE11,
// Misc utilities
RHT_YOUR_POCKET,
RHT_DUNGEON_ORDINARY,
RHT_DUNGEON_MASTERFUL,
RHT_MAX
} RandomizerHintTextKey;
@ -3553,7 +3585,8 @@ typedef enum {
RSK_ICE_TRAPS,
RSK_GOSSIP_STONE_HINTS,
RSK_TOT_ALTAR_HINT,
RSK_LIGHT_ARROWS_HINT,
RSK_GANONDORF_HINT,
RSK_SHEIK_LA_HINT,
RSK_DAMPES_DIARY_HINT,
RSK_GREG_HINT,
RSK_SARIA_HINT,
@ -3583,7 +3616,6 @@ typedef enum {
RSK_SKIP_CHILD_ZELDA,
RSK_STARTING_CONSUMABLES,
RSK_FULL_WALLETS,
RSK_LANGUAGE,
RSK_SHUFFLE_CHEST_MINIGAME,
RSK_CUCCO_COUNT,
RSK_BIG_POE_COUNT,
@ -4095,3 +4127,5 @@ typedef enum {
TH_MESSAGE_FINISHED,
TH_MESSAGE_SURPLUS,
} TriforceHuntMessages;

View File

@ -158,7 +158,8 @@ void Settings::CreateOptions() {
mOptions[RSK_HINT_CLARITY] = Option::U8("Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, OptionCategory::Setting, "gRandomizeHintClarity", mOptionDescriptions[RSK_HINT_CLARITY], WidgetType::Combobox, RO_HINT_CLARITY_CLEAR, true, IMFLAG_INDENT);
mOptions[RSK_HINT_DISTRIBUTION] = Option::U8("Hint Distribution", {"Useless", "Balanced", "Strong", "Very Strong"}, OptionCategory::Setting, "gRandomizeHintDistribution", mOptionDescriptions[RSK_HINT_DISTRIBUTION], WidgetType::Combobox, RO_HINT_DIST_BALANCED, true, IMFLAG_UNINDENT);
mOptions[RSK_TOT_ALTAR_HINT] = Option::Bool("ToT Altar Hint", {"Off", "On"}, OptionCategory::Setting, "gRandomizeAltarHint", mOptionDescriptions[RSK_TOT_ALTAR_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_INDENT);
mOptions[RSK_LIGHT_ARROWS_HINT] = Option::Bool("Light Arrow Hint", {"Off", "On"}, OptionCategory::Setting, "gRandomizeLAHint", mOptionDescriptions[RSK_LIGHT_ARROWS_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_NONE);
mOptions[RSK_GANONDORF_HINT] = Option::Bool("Ganondorf Hint", {"Off", "On"}, OptionCategory::Setting, "gRandomizeGanondorfHint", mOptionDescriptions[RSK_GANONDORF_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_NONE);
mOptions[RSK_SHEIK_LA_HINT] = Option::Bool("Sheik Light Arrow Hint", {"Off", "On"}, OptionCategory::Setting, "gRandomizeSheikLAHint", mOptionDescriptions[RSK_SHEIK_LA_HINT], WidgetType::Checkbox, RO_GENERIC_ON, false, IMFLAG_NONE);
mOptions[RSK_DAMPES_DIARY_HINT] = Option::Bool("Dampe's Diary Hint", "gRandomizeDampeHint", mOptionDescriptions[RSK_DAMPES_DIARY_HINT], IMFLAG_NONE);
mOptions[RSK_GREG_HINT] = Option::Bool("Greg the Green Rupee Hint", "gRandomizeGregHint", mOptionDescriptions[RSK_GREG_HINT], IMFLAG_NONE);
mOptions[RSK_SARIA_HINT] = Option::Bool("Saria's Hint", "gRandomizeSariaHint", mOptionDescriptions[RSK_SARIA_HINT], IMFLAG_NONE);
@ -208,8 +209,6 @@ void Settings::CreateOptions() {
mOptions[RSK_ALL_LOCATIONS_REACHABLE] = Option::Bool("All Locations Reachable", {"Off", "On"}, OptionCategory::Setting, "gRandomizeAllLocationsReachable", mOptionDescriptions[RSK_ALL_LOCATIONS_REACHABLE], WidgetType::Checkbox, RO_GENERIC_ON);
mOptions[RSK_SKULLS_SUNS_SONG] = Option::Bool("Night Skulltula's Expect Sun's Song", "gRandomizeGsExpectSunsSong", mOptionDescriptions[RSK_SKULLS_SUNS_SONG]);
mOptions[RSK_DAMAGE_MULTIPLIER] = Option::U8("Damage Multiplier", {"x1/2", "x1", "x2", "x4", "x8", "x16", "OHKO"}, OptionCategory::Setting, "", "", WidgetType::Slider, RO_DAMAGE_MULTIPLIER_DEFAULT);
mOptions[RSK_LANGUAGE] = Option::U8("Language", {"English", "German", "French"}, OptionCategory::Setting, "gLanguages", "", WidgetType::Combobox, LANGUAGE_ENG);
// clang-format on
mExcludeLocationsOptionsGroups.reserve(SPOILER_COLLECTION_GROUP_COUNT);
@ -729,7 +728,8 @@ void Settings::CreateOptions() {
}, false, WidgetContainerType::SECTION);
mOptionGroups[RSG_EXTRA_HINTS_IMGUI] = OptionGroup::SubGroup("Extra Hints", {
&mOptions[RSK_TOT_ALTAR_HINT],
&mOptions[RSK_LIGHT_ARROWS_HINT],
&mOptions[RSK_GANONDORF_HINT],
&mOptions[RSK_SHEIK_LA_HINT],
&mOptions[RSK_DAMPES_DIARY_HINT],
&mOptions[RSK_GREG_HINT],
&mOptions[RSK_SARIA_HINT],
@ -956,7 +956,8 @@ void Settings::CreateOptions() {
&mOptions[RSK_HINT_CLARITY],
&mOptions[RSK_HINT_DISTRIBUTION],
&mOptions[RSK_TOT_ALTAR_HINT],
&mOptions[RSK_LIGHT_ARROWS_HINT],
&mOptions[RSK_GANONDORF_HINT],
&mOptions[RSK_SHEIK_LA_HINT],
&mOptions[RSK_DAMPES_DIARY_HINT],
&mOptions[RSK_GREG_HINT],
&mOptions[RSK_SARIA_HINT],
@ -1173,7 +1174,8 @@ void Settings::CreateOptions() {
{ "Miscellaneous Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS },
{ "Miscellaneous Settings:Hint Clarity", RSK_HINT_CLARITY },
{ "Miscellaneous Settings:ToT Altar Hint", RSK_TOT_ALTAR_HINT },
{ "Miscellaneous Settings:Light Arrow Hint", RSK_LIGHT_ARROWS_HINT },
{ "Miscellaneous Settings:Ganondorf Hint", RSK_GANONDORF_HINT },
{ "Miscellaneous Settings:Sheik Light Arrow Hint", RSK_SHEIK_LA_HINT },
{ "Miscellaneous Settings:Dampe's Diary Hint", RSK_DAMPES_DIARY_HINT },
{ "Miscellaneous Settings:Greg the Rupee Hint", RSK_GREG_HINT },
{ "Miscellaneous Settings:Saria's Hint", RSK_SARIA_HINT },
@ -2280,7 +2282,8 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
case RSK_SUNLIGHT_ARROWS:
case RSK_BOMBCHUS_IN_LOGIC:
case RSK_TOT_ALTAR_HINT:
case RSK_LIGHT_ARROWS_HINT:
case RSK_GANONDORF_HINT:
case RSK_SHEIK_LA_HINT:
case RSK_DAMPES_DIARY_HINT:
case RSK_GREG_HINT:
case RSK_SARIA_HINT:
@ -2644,7 +2647,7 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
ctx->AddExcludedOptions();
for (auto it = jsonExcludedLocations.begin(); it != jsonExcludedLocations.end(); ++it) {
const RandomizerCheck rc = StaticData::SpoilerfileCheckNameToEnum[it.value()];
const RandomizerCheck rc = Rando::StaticData::locationNameToEnum[it.value()];
ctx->GetItemLocation(rc)->GetExcludedOption()->SetSelectedIndex(RO_GENERIC_ON);
}

View File

@ -0,0 +1,307 @@
#include <unordered_map>
#include "static_data.h"
#include <spdlog/spdlog.h>
namespace Rando {
std::unordered_map<uint32_t, CustomMessage> StaticData::hintTypeNames = {
{HINT_TYPE_HINT_KEY, CustomMessage("Message")},
{HINT_TYPE_AREA, CustomMessage("Area")},
{HINT_TYPE_ITEM, CustomMessage("Item")},
{HINT_TYPE_TRIAL, CustomMessage("Trial")},
{HINT_TYPE_ENTRANCE, CustomMessage("Entrance")},
{HINT_TYPE_ITEM_AREA, CustomMessage("Item Area")},
{HINT_TYPE_MERCHANT, CustomMessage("Merchant")},
{HINT_TYPE_ALTAR_CHILD, CustomMessage("Child Altar")},
{HINT_TYPE_ALTAR_ADULT, CustomMessage("Adult Altar")},
{HINT_TYPE_WOTH, CustomMessage("Way of the Hero")},
{HINT_TYPE_FOOLISH, CustomMessage("Foolish")},
{HINT_TYPE_MESSAGE, CustomMessage("Hardcoded Message")}
};
//RANDOTODO When dynamic grotto check names are done, apply it to these hints
//RANDOTODO Translations
std::unordered_map<uint32_t, CustomMessage> StaticData::hintNames = {
{RH_NONE, CustomMessage("ERROR HINT")},
{RH_KF_DEKU_TREE_LEFT_GOSSIP_STONE, CustomMessage("KF Left Near Deku Gossip Stone")}, //RANDOTODO find cardinal direction
{RH_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, CustomMessage("KF Right Near Deku Gossip Stone")},
{RH_KF_GOSSIP_STONE, CustomMessage("KF Gossip Stone")},
{RH_KF_STORMS_GROTTO_GOSSIP_STONE, CustomMessage("KF Storms Grotto Gossip Stone")},
{RH_LW_GOSSIP_STONE, CustomMessage("LW Gossip Stone")},
{RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, CustomMessage("LW Near Shortcuts Grotto Gossip Stone")},
{RH_SFM_MAZE_NEAR_LW_GOSSIP_STONE, CustomMessage("SFM Near LW Gossip Stone")},
{RH_SFM_MAZE_CENTER_GOSSIP_STONE, CustomMessage("SFM Center Gossip Stone")},
{RH_SFM_SARIA_GOSSIP_STONE, CustomMessage("SFM Near Saria Gossip Stone")},
{RH_HF_COW_GROTTO_GOSSIP_STONE, CustomMessage("HF Cow Grotto Gossip Stone")},
{RH_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE, CustomMessage("HF Near Market Grotto Gossip Stone")},
{RH_HF_OPEN_GROTTO_GOSSIP_STONE, CustomMessage("HF Open Grotto Gossip Stone")},
{RH_HF_SOUTHEAST_GROTTO_GOSSIP_STONE, CustomMessage("HF Southeast Grotto Gossip Stone")},
{RH_TOT_LEFT_CENTER_GOSSIP_STONE, CustomMessage("Market Left Center Gossip Stone")},
{RH_TOT_LEFTMOST_GOSSIP_STONE, CustomMessage("Market Leftmost Center Gossip Stone")},
{RH_TOT_RIGHT_CENTER_GOSSIP_STONE, CustomMessage("Market Right Center Gossip Stone")},
{RH_TOT_RIGHTMOST_GOSSIP_STONE, CustomMessage("Market Rightmost Gossip Stone")},
{RH_HC_MALON_GOSSIP_STONE, CustomMessage("HC Near Malon Gossip Stone")},
{RH_HC_ROCK_WALL_GOSSIP_STONE, CustomMessage("HC Rock Wall Gossip Stone")},
{RH_HC_STORMS_GROTTO_GOSSIP_STONE, CustomMessage("HC Storm Grotto Gossip Stone")},
{RH_KAK_OPEN_GROTTO_GOSSIP_STONE, CustomMessage("Kak Open Grotto Gossip Stone")},
{RH_GRAVEYARD_GOSSIP_STONE, CustomMessage("Graveyard Gossip Stone")},
{RH_DMT_GOSSIP_STONE, CustomMessage("DMT Gossip Stone")},
{RH_DMT_STORMS_GROTTO_GOSSIP_STONE, CustomMessage("DMT Storms Grotto Gossip Stone")},
{RH_GC_MAZE_GOSSIP_STONE, CustomMessage("GC Maze Gossip Stone")},
{RH_GC_MEDIGORON_GOSSIP_STONE, CustomMessage("GC Medigoron Gossip Stone")},
{RH_DMC_GOSSIP_STONE, CustomMessage("DMC Gossip Stone")},
{RH_DMC_UPPER_GROTTO_GOSSIP_STONE, CustomMessage("DMC Upper Grotto Gossip Stone")},
{RH_ZR_NEAR_DOMAIN_GOSSIP_STONE, CustomMessage("ZR Near Domain Gossip Stone")},
{RH_ZR_NEAR_GROTTOS_GOSSIP_STONE, CustomMessage("ZR Near Grottos Gossip Stone")},
{RH_ZR_OPEN_GROTTO_GOSSIP_STONE, CustomMessage("ZR Open Grotto Gossip Stone")},
{RH_ZD_GOSSIP_STONE, CustomMessage("ZD Gossip Stone")},
{RH_ZF_JABU_GOSSIP_STONE, CustomMessage("ZF Near Jabu Gossip Stone")},
{RH_ZF_FAIRY_GOSSIP_STONE, CustomMessage("ZF Near Fairy Gossip Stone")},
{RH_LH_LAB_GOSSIP_STONE, CustomMessage("LH Near Lab Gossip Stone")},
{RH_LH_SOUTHEAST_GOSSIP_STONE, CustomMessage("LH Southeast Gossip Stone")},
{RH_LH_SOUTHWEST_GOSSIP_STONE, CustomMessage("LH Southwest Gossip Stone")},
{RH_GV_GOSSIP_STONE, CustomMessage("Gerudo Valley Gossip Stone")},
{RH_COLOSSUS_GOSSIP_STONE, CustomMessage("Desert Collosus Gossip Stone")},
{RH_DODONGOS_CAVERN_GOSSIP_STONE, CustomMessage("Dodongo's Cavern Gossip Stone")},
{RH_GANONDORF_HINT, CustomMessage("Ganondorf Hint")},
{RH_GANONDORF_JOKE, CustomMessage("Ganondorf Joke")},
{RH_SHEIK_HINT, CustomMessage("Sheik in Ganons Castle Hint")},
{RH_DAMPES_DIARY, CustomMessage("Dampe's Diary Hint")},
{RH_GREG_RUPEE, CustomMessage("Treasure Chest Game Greg Hint")},
{RH_ALTAR_CHILD, CustomMessage("ToT Altar as Child")},
{RH_ALTAR_ADULT, CustomMessage("ToT Altar as Adult")},
{RH_SARIA_HINT, CustomMessage("Saria's Magic Hint")},
{RH_FISHING_POLE, CustomMessage("Fishing Pole Hint")},
{RH_MINUET_WARP_LOC, CustomMessage("Minuet of Forest Destination")},
{RH_BOLERO_WARP_LOC, CustomMessage("Bolero of Fire Destination")},
{RH_SERENADE_WARP_LOC, CustomMessage("Serenade of Water Destination")},
{RH_REQUIEM_WARP_LOC, CustomMessage("Requiem of Spirit Destination")},
{RH_NOCTURNE_WARP_LOC, CustomMessage("Nocturne of Shadow Destination")},
{RH_PRELUDE_WARP_LOC, CustomMessage("Prelude of Light Destination")},
{RH_MEDIGORON, CustomMessage("Medigoron Hint")},
{RH_CARPET_SALESMAN, CustomMessage("Carpet Salseman Hint")},
{RH_BEAN_SALESMAN, CustomMessage("Bean Salseman Hint")},
{RH_GRANNY, CustomMessage("Granny Hint")},
{RH_HBA_HINT, CustomMessage("Horseback Archery Hint")},
{RH_MALON_HINT, CustomMessage("Malon Hint")},
{RH_CHICKENS_HINT, CustomMessage("Anju's Child Chickens Hint")},
{RH_BIG_POES_HINT, CustomMessage("Big Poe Reward Hint")},
{RH_BIGGORON_HINT, CustomMessage("Biggoron Claim Check Hint")},
{RH_FROGS_HINT, CustomMessage("Final Frogs in River Hint")},
{RH_KAK_10_SKULLS_HINT, CustomMessage("10 Skulls Hint")},
{RH_KAK_20_SKULLS_HINT, CustomMessage("20 Skulls Hint")},
{RH_KAK_30_SKULLS_HINT, CustomMessage("30 Skulls Hint")},
{RH_KAK_40_SKULLS_HINT, CustomMessage("40 Skulls Hint")},
{RH_KAK_50_SKULLS_HINT, CustomMessage("50 Skulls Hint")},
{RH_KAK_100_SKULLS_HINT, CustomMessage("100 Skulls Hint")}
};
std::unordered_map<RandomizerCheck, RandomizerHint> StaticData::gossipStoneCheckToHint {
{RC_COLOSSUS_GOSSIP_STONE, RH_COLOSSUS_GOSSIP_STONE},
{RC_DMC_GOSSIP_STONE, RH_DMC_GOSSIP_STONE},
{RC_DMC_UPPER_GROTTO_GOSSIP_STONE, RH_DMC_UPPER_GROTTO_GOSSIP_STONE},
{RC_DMT_GOSSIP_STONE, RH_DMT_GOSSIP_STONE},
{RC_DMT_STORMS_GROTTO_GOSSIP_STONE, RH_DMT_STORMS_GROTTO_GOSSIP_STONE},
{RC_DODONGOS_CAVERN_GOSSIP_STONE, RH_DODONGOS_CAVERN_GOSSIP_STONE},
{RC_ZF_FAIRY_GOSSIP_STONE, RH_ZF_FAIRY_GOSSIP_STONE},
{RC_GC_MAZE_GOSSIP_STONE, RH_GC_MAZE_GOSSIP_STONE},
{RC_GC_MEDIGORON_GOSSIP_STONE, RH_GC_MEDIGORON_GOSSIP_STONE},
{RC_GV_GOSSIP_STONE, RH_GV_GOSSIP_STONE},
{RC_GRAVEYARD_GOSSIP_STONE, RH_GRAVEYARD_GOSSIP_STONE},
{RC_HC_MALON_GOSSIP_STONE, RH_HC_MALON_GOSSIP_STONE},
{RC_HC_ROCK_WALL_GOSSIP_STONE, RH_HC_ROCK_WALL_GOSSIP_STONE},
{RC_HC_STORMS_GROTTO_GOSSIP_STONE, RH_HC_STORMS_GROTTO_GOSSIP_STONE},
{RC_HF_COW_GROTTO_GOSSIP_STONE, RH_HF_COW_GROTTO_GOSSIP_STONE},
{RC_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE, RH_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE},
{RC_HF_OPEN_GROTTO_GOSSIP_STONE, RH_HF_OPEN_GROTTO_GOSSIP_STONE},
{RC_HF_SOUTHEAST_GROTTO_GOSSIP_STONE, RH_HF_SOUTHEAST_GROTTO_GOSSIP_STONE},
{RC_ZF_JABU_GOSSIP_STONE, RH_ZF_JABU_GOSSIP_STONE},
{RC_KF_DEKU_TREE_LEFT_GOSSIP_STONE, RH_KF_DEKU_TREE_LEFT_GOSSIP_STONE},
{RC_KF_DEKU_TREE_RIGHT_GOSSIP_STONE, RH_KF_DEKU_TREE_RIGHT_GOSSIP_STONE},
{RC_KF_GOSSIP_STONE, RH_KF_GOSSIP_STONE},
{RC_KF_STORMS_GROTTO_GOSSIP_STONE, RH_KF_STORMS_GROTTO_GOSSIP_STONE},
{RC_KAK_OPEN_GROTTO_GOSSIP_STONE, RH_KAK_OPEN_GROTTO_GOSSIP_STONE},
{RC_LH_LAB_GOSSIP_STONE, RH_LH_LAB_GOSSIP_STONE},
{RC_LH_SOUTHEAST_GOSSIP_STONE, RH_LH_SOUTHEAST_GOSSIP_STONE},
{RC_LH_SOUTHWEST_GOSSIP_STONE, RH_LH_SOUTHWEST_GOSSIP_STONE},
{RC_LW_GOSSIP_STONE, RH_LW_GOSSIP_STONE},
{RC_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE, RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE},
{RC_SFM_MAZE_LOWER_GOSSIP_STONE, RH_SFM_MAZE_NEAR_LW_GOSSIP_STONE},
{RC_SFM_MAZE_UPPER_GOSSIP_STONE, RH_SFM_MAZE_CENTER_GOSSIP_STONE},
{RC_SFM_SARIA_GOSSIP_STONE, RH_SFM_SARIA_GOSSIP_STONE},
{RC_TOT_LEFT_CENTER_GOSSIP_STONE, RH_TOT_LEFT_CENTER_GOSSIP_STONE},
{RC_TOT_LEFTMOST_GOSSIP_STONE, RH_TOT_LEFTMOST_GOSSIP_STONE},
{RC_TOT_RIGHT_CENTER_GOSSIP_STONE, RH_TOT_RIGHT_CENTER_GOSSIP_STONE},
{RC_TOT_RIGHTMOST_GOSSIP_STONE, RH_TOT_RIGHTMOST_GOSSIP_STONE},
{RC_ZD_GOSSIP_STONE, RH_ZD_GOSSIP_STONE},
{RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, RH_ZR_NEAR_DOMAIN_GOSSIP_STONE},
{RC_ZR_NEAR_GROTTOS_GOSSIP_STONE, RH_ZR_NEAR_GROTTOS_GOSSIP_STONE},
{RC_ZR_OPEN_GROTTO_GOSSIP_STONE, RH_ZR_OPEN_GROTTO_GOSSIP_STONE}
};
std::unordered_map<uint32_t, RandomizerHintTextKey> StaticData::areaNames = { //RANDOTODO resolve None in area
{RA_NONE, RHT_LINKS_POCKET}, //explicit none in area hints usually means it's a starting item, so say Link's pocket
{RA_LINKS_POCKET, RHT_LINKS_POCKET},
{RA_KOKIRI_FOREST, RHT_KOKIRI_FOREST},
{RA_THE_LOST_WOODS, RHT_THE_LOST_WOODS},
{RA_SACRED_FOREST_MEADOW, RHT_SACRED_FOREST_MEADOW},
{RA_HYRULE_FIELD, RHT_HYRULE_FIELD},
{RA_LAKE_HYLIA, RHT_LAKE_HYLIA},
{RA_GERUDO_VALLEY, RHT_GERUDO_VALLEY},
{RA_GERUDO_FORTRESS, RHT_GERUDO_FORTRESS},
{RA_HAUNTED_WASTELAND, RHT_HAUNTED_WASTELAND},
{RA_DESERT_COLOSSUS, RHT_DESERT_COLOSSUS},
{RA_THE_MARKET, RHT_THE_MARKET},
{RA_TEMPLE_OF_TIME, RHT_TEMPLE_OF_TIME},
{RA_HYRULE_CASTLE, RHT_HYRULE_CASTLE},
{RA_OUTSIDE_GANONS_CASTLE, RHT_OUTSIDE_GANONS_CASTLE},
{RA_CASTLE_GROUNDS, RHT_CASTLE_GROUNDS},
{RA_KAKARIKO_VILLAGE, RHT_KAKARIKO_VILLAGE},
{RA_THE_GRAVEYARD, RHT_THE_GRAVEYARD},
{RA_DEATH_MOUNTAIN_TRAIL, RHT_DEATH_MOUNTAIN_TRAIL},
{RA_GORON_CITY, RHT_GORON_CITY},
{RA_DEATH_MOUNTAIN_CRATER, RHT_DEATH_MOUNTAIN_CRATER},
{RA_ZORAS_RIVER, RHT_ZORAS_RIVER},
{RA_ZORAS_DOMAIN, RHT_ZORAS_DOMAIN},
{RA_ZORAS_FOUNTAIN, RHT_ZORAS_FOUNTAIN},
{RA_LON_LON_RANCH, RHT_LON_LON_RANCH},
{RA_DEKU_TREE, RHT_DEKU_TREE},
{RA_DODONGOS_CAVERN, RHT_DODONGOS_CAVERN},
{RA_JABU_JABUS_BELLY, RHT_JABU_JABUS_BELLY},
{RA_FOREST_TEMPLE, RHT_FOREST_TEMPLE},
{RA_FIRE_TEMPLE, RHT_FIRE_TEMPLE},
{RA_WATER_TEMPLE, RHT_WATER_TEMPLE},
{RA_SPIRIT_TEMPLE, RHT_SPIRIT_TEMPLE},
{RA_SHADOW_TEMPLE, RHT_SHADOW_TEMPLE},
{RA_BOTTOM_OF_THE_WELL, RHT_BOTTOM_OF_THE_WELL},
{RA_ICE_CAVERN, RHT_ICE_CAVERN},
{RA_GERUDO_TRAINING_GROUND, RHT_GERUDO_TRAINING_GROUND},
{RA_GANONS_CASTLE, RHT_GANONS_CASTLE}
};
std::unordered_map<uint32_t, RandomizerHintTextKey> StaticData::trialData = {
{TK_LIGHT_TRIAL, RHT_LIGHT_TRIAL},
{TK_FOREST_TRIAL, RHT_FOREST_TRIAL},
{TK_FIRE_TRIAL, RHT_FIRE_TRIAL},
{TK_WATER_TRIAL, RHT_WATER_TRIAL},
{TK_SHADOW_TRIAL, RHT_SHADOW_TRIAL},
{TK_SPIRIT_TRIAL, RHT_SPIRIT_TRIAL}
};
std::unordered_map<RandomizerHint, StaticHintInfo> StaticData::staticHintInfoMap = {
// RH_GANONDORF_HINT is special cased due to being different based on master sword shuffle
// Altar hints are special cased due to special hint marking rules
// warp song hints are special cased due to entrences not being done properly yet
// Ganondorf Joke is special Cased as the text is random
{RH_SHEIK_HINT, StaticHintInfo(HINT_TYPE_AREA, {RHT_SHEIK_HINT_LA_ONLY}, RSK_SHEIK_LA_HINT, true, {}, {RG_LIGHT_ARROWS}, {RC_SHEIK_HINT_GC, RC_SHEIK_HINT_MQ_GC}, true)},
{RH_DAMPES_DIARY, StaticHintInfo(HINT_TYPE_AREA, {RHT_DAMPE_DIARY}, RSK_DAMPES_DIARY_HINT, true, {}, {RG_PROGRESSIVE_HOOKSHOT}, {RC_DAMPE_HINT})},
{RH_GREG_RUPEE, StaticHintInfo(HINT_TYPE_AREA, {RHT_GREG_HINT}, RSK_GREG_HINT, true, {}, {RG_GREG_RUPEE}, {RC_GREG_HINT})},
{RH_SARIA_HINT, StaticHintInfo(HINT_TYPE_AREA, {RHT_SARIA_TALK_HINT, RHT_SARIA_SONG_HINT}, RSK_SARIA_HINT, true, {}, {RG_PROGRESSIVE_MAGIC_METER}, {RC_SARIA_SONG_HINT, RC_SONG_FROM_SARIA}, true)},
{RH_FISHING_POLE, StaticHintInfo(HINT_TYPE_AREA, {RHT_FISHING_POLE_HINT}, RSK_FISHING_POLE_HINT, true, {}, {RG_FISHING_POLE}, {RC_FISHING_POLE_HINT}, true)},
{RH_MEDIGORON, StaticHintInfo(HINT_TYPE_MERCHANT, {RHT_MEDIGORON_HINT}, RSK_SHUFFLE_MERCHANTS, (uint8_t)RO_SHUFFLE_MERCHANTS_ON_HINT, {RC_GC_MEDIGORON})},
{RH_GRANNY, StaticHintInfo(HINT_TYPE_MERCHANT, {RHT_GRANNY_HINT}, RSK_SHUFFLE_MERCHANTS, (uint8_t)RO_SHUFFLE_MERCHANTS_ON_HINT, {RC_KAK_GRANNYS_SHOP})},
{RH_CARPET_SALESMAN, StaticHintInfo(HINT_TYPE_MERCHANT, {RHT_CARPET_SALESMAN_DIALOG_HINTED}, RSK_SHUFFLE_MERCHANTS, (uint8_t)RO_SHUFFLE_MERCHANTS_ON_HINT, {RC_WASTELAND_BOMBCHU_SALESMAN})},
{RH_BEAN_SALESMAN, StaticHintInfo(HINT_TYPE_MERCHANT, {RHT_BEAN_SALESMAN_HINT}, RSK_SHUFFLE_MAGIC_BEANS, true, {RC_ZR_MAGIC_BEAN_SALESMAN})},
{RH_HBA_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_HBA_HINT_SIGN, RHT_HBA_HINT_NOT_ON_HORSE, RHT_HBA_HINT_INITIAL, RHT_HBA_HINT_HAVE_1000}, RSK_HBA_HINT, true, {RC_GF_HBA_1000_POINTS, RC_GF_HBA_1500_POINTS})},
{RH_MALON_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_MALON_HINT_TURNING_EVIL, RHT_MALON_HINT_HOW_IS_EPONA, RHT_MALON_HINT_OBSTICLE_COURSE, RHT_MALON_HINT_INGO_TEMPTED},RSK_MALON_HINT, true, {RC_KF_LINKS_HOUSE_COW})},
{RH_BIG_POES_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_BIG_POES_HINT}, RSK_BIG_POES_HINT, true, {RC_MARKET_10_BIG_POES})},
{RH_CHICKENS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_CHICKENS_HINT}, RSK_CHICKENS_HINT, true, {RC_KAK_ANJU_AS_CHILD})},
{RH_BIGGORON_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_BIGGORON_HINT}, RSK_BIGGORON_HINT, true, {RC_DMT_TRADE_CLAIM_CHECK})},
{RH_FROGS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_FROGS_HINT}, RSK_FROGS_HINT, true, {RC_ZR_FROGS_OCARINA_GAME})},
{RH_KAK_10_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_10_SKULLS_HINT, true, {RC_KAK_10_GOLD_SKULLTULA_REWARD}, {}, {}, false, 10)},
{RH_KAK_20_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_20_SKULLS_HINT, true, {RC_KAK_20_GOLD_SKULLTULA_REWARD}, {}, {}, false, 20)},
{RH_KAK_30_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_30_SKULLS_HINT, true, {RC_KAK_30_GOLD_SKULLTULA_REWARD}, {}, {}, false, 30)},
{RH_KAK_40_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_40_SKULLS_HINT, true, {RC_KAK_40_GOLD_SKULLTULA_REWARD}, {}, {}, false, 40)},
{RH_KAK_50_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_50_SKULLS_HINT, true, {RC_KAK_50_GOLD_SKULLTULA_REWARD}, {}, {}, false, 50)},
{RH_KAK_100_SKULLS_HINT, StaticHintInfo(HINT_TYPE_ITEM, {RHT_SKULLS_HINT}, RSK_KAK_100_SKULLS_HINT, true, {RC_KAK_100_GOLD_SKULLTULA_REWARD}, {}, {}, false, 100)}
};
std::unordered_map<std::string, uint32_t> StaticData::PopulateTranslationMap(std::unordered_map<uint32_t, CustomMessage> input){
std::unordered_map<std::string, uint32_t> output = {};
for (const auto& [key, message] : input) {
std::vector<std::string> strings = message.GetAllMessages();
for (std::string string: strings){
if (output.contains(string)){
if (output[string] != key){
SPDLOG_DEBUG("\tREPEATED STRING IN " + message.GetEnglish(MF_CLEAN) + "\n\n"); //RANDOTODO should this cause an error of some kind?
}
} else {
output[string] = key;
}
}
}
return output;
}
std::unordered_map<std::string, uint32_t> StaticData::PopulateTranslationMap(std::unordered_map<uint32_t, RandomizerHintTextKey> input){
std::unordered_map<std::string, uint32_t> output = {};
for (const auto& [key, text] : input) {
std::vector<std::string> strings = hintTextTable[text].GetClear().GetAllMessages();
for (std::string string: strings){
if (output.contains(string)){
if (output[string] != key){
SPDLOG_DEBUG("\tREPEATED STRING WITH " + string + "\n\n"); //RANDOTODO should this cause an error of some kind?
}
} else {
output[string] = key;
}
}
}
return output;
}
std::unordered_map<std::string, uint32_t> StaticData::hintNameToEnum = {};
std::unordered_map<std::string, uint32_t> StaticData::hintTypeNameToEnum = {};
std::unordered_map<std::string, uint32_t> StaticData::areaNameToEnum = {};
std::unordered_map<std::string, uint32_t> StaticData::trialNameToEnum = {};
std::unordered_map<std::string, RandomizerCheck> StaticData::locationNameToEnum = {}; //is filled in context based on location table, not touching that because of VB
std::unordered_map<u32, RandomizerHint> StaticData::stoneParamsToHint{
{0x1, RH_ZF_FAIRY_GOSSIP_STONE},
{0x2, RH_ZF_JABU_GOSSIP_STONE},
{0x3, RH_LH_LAB_GOSSIP_STONE},
{0x4, RH_DMT_GOSSIP_STONE},
{0x5, RH_DMC_GOSSIP_STONE},
{0x6, RH_TOT_LEFTMOST_GOSSIP_STONE},
{0x7, RH_TOT_LEFT_CENTER_GOSSIP_STONE},
{0x8, RH_LH_SOUTHWEST_GOSSIP_STONE},
{0x9, RH_ZD_GOSSIP_STONE},
{0xA, RH_GRAVEYARD_GOSSIP_STONE},
{0xB, RH_HC_ROCK_WALL_GOSSIP_STONE},
{0xC, RH_ZR_NEAR_DOMAIN_GOSSIP_STONE},
{0xD, RH_ZR_NEAR_GROTTOS_GOSSIP_STONE},
{0xE, RH_TOT_RIGHT_CENTER_GOSSIP_STONE },
{0xF, RH_LH_SOUTHEAST_GOSSIP_STONE},
{0x10, RH_TOT_RIGHTMOST_GOSSIP_STONE },
{0x11, RH_GV_GOSSIP_STONE},
{0x12, RH_HC_MALON_GOSSIP_STONE},
{0x13, RH_HC_STORMS_GROTTO_GOSSIP_STONE},
{0x14, RH_DODONGOS_CAVERN_GOSSIP_STONE},
{0x15, RH_GC_MAZE_GOSSIP_STONE},
{0x16, RH_SFM_MAZE_NEAR_LW_GOSSIP_STONE},
{0x17, RH_SFM_MAZE_CENTER_GOSSIP_STONE},
//generic grottos all use 0x18, grottoChestParamsToHint is used for conversion
{0x19, RH_GC_MEDIGORON_GOSSIP_STONE},
{0x1A, RH_COLOSSUS_GOSSIP_STONE},
{0x1B, RH_HF_COW_GROTTO_GOSSIP_STONE},
{0x1C, RH_SFM_SARIA_GOSSIP_STONE},
{0x1D, RH_LW_GOSSIP_STONE},
{0x1E, RH_KF_GOSSIP_STONE},
{0x1F, RH_KF_DEKU_TREE_LEFT_GOSSIP_STONE},
{0x20, RH_KF_DEKU_TREE_RIGHT_GOSSIP_STONE},
};
std::unordered_map<u32, RandomizerHint> StaticData::grottoChestParamsToHint{
{22944, RH_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE},
{22978, RH_HF_SOUTHEAST_GROTTO_GOSSIP_STONE},
{22947, RH_HF_OPEN_GROTTO_GOSSIP_STONE},
{22964, RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE},
{23255, RH_DMT_STORMS_GROTTO_GOSSIP_STONE},
{22984, RH_KAK_OPEN_GROTTO_GOSSIP_STONE},
{22985, RH_ZR_OPEN_GROTTO_GOSSIP_STONE},
{23802, RH_DMC_UPPER_GROTTO_GOSSIP_STONE},
{22988, RH_KF_STORMS_GROTTO_GOSSIP_STONE}
};
std::array<HintText, RHT_MAX> StaticData::hintTextTable = {};
}

View File

@ -22,25 +22,47 @@ class StaticData {
public:
static void InitItemTable();
static void HintTable_Init();
static void HintTable_Init_Item();
static void HintTable_Init_Exclude_Overworld();
static void HintTable_Init_Exclude_Dungeon();
static Item& RetrieveItem(const RandomizerGet rgid);
static Item& ItemFromGIID(const int giid);
static std::array<Item, RG_MAX>& GetItemTable();
static std::array<Item, RG_MAX>& GetItemTable();// is there a reason this is a function and not just an exposed table?
static void InitLocationTable();
static Location* GetLocation(RandomizerCheck locKey);
static std::array<Rando::Location, RC_MAX>& GetLocationTable();
static std::unordered_map<std::string, RandomizerCheck> SpoilerfileCheckNameToEnum;
static std::unordered_map<std::string, RandomizerGet> SpoilerfileItemNameToEnum;
static std::unordered_map<std::string, uint32_t> PopulateTranslationMap(std::unordered_map<uint32_t, CustomMessage> input);
static std::unordered_map<std::string, uint32_t> PopulateTranslationMap(std::unordered_map<uint32_t, RandomizerHintTextKey> input);
static std::multimap<std::tuple<s16, s16, s32>, RandomizerCheck> CheckFromActorMultimap;
static std::vector<RandomizerCheck> overworldLocations;
static std::vector<RandomizerCheck> dungeonRewardLocations;
static std::vector<std::vector<RandomizerCheck>> shopLocationLists;
static std::vector<RandomizerCheck> scrubLocations;
static std::vector<RandomizerCheck> gossipStoneLocations;
static std::vector<RandomizerCheck> otherHintLocations;
static std::vector<RandomizerCheck> staticHintLocations;
static std::vector<RandomizerCheck> pondFishLocations;
static std::vector<RandomizerCheck> overworldFishLocations;
static std::array<std::pair<RandomizerCheck, RandomizerCheck>, 17> randomizerFishingPondFish;
static std::unordered_map<int8_t, RandomizerCheck> randomizerGrottoFishMap;
static std::vector<RandomizerHint> oldVerHintOrder;
static uint16_t oldVerGossipStoneStart;
static std::unordered_map<std::string, RandomizerCheck> locationNameToEnum;
static std::unordered_map<std::string, RandomizerGet> itemNameToEnum;
static std::unordered_map<uint32_t, CustomMessage> hintNames;
static std::unordered_map<std::string, uint32_t> hintNameToEnum;
static std::unordered_map<uint32_t, CustomMessage> hintTypeNames;
static std::unordered_map<std::string, uint32_t> hintTypeNameToEnum;
static std::unordered_map<RandomizerCheck, RandomizerHint> gossipStoneCheckToHint;
static std::unordered_map<uint32_t, RandomizerHintTextKey> areaNames;
static std::unordered_map<std::string, uint32_t> areaNameToEnum;
static std::unordered_map<uint32_t, RandomizerHintTextKey> trialData;
static std::unordered_map<std::string, uint32_t> trialNameToEnum;
static std::unordered_map<RandomizerHint, StaticHintInfo> staticHintInfoMap;
static std::unordered_map<u32, RandomizerHint> stoneParamsToHint;
static std::unordered_map<u32, RandomizerHint> grottoChestParamsToHint;
static std::array<HintText, RHT_MAX> hintTextTable;
StaticData();
~StaticData();
};

View File

@ -1,12 +1,21 @@
#include "trial.h"
#include "static_data.h"
namespace Rando {
TrialInfo::TrialInfo(Text name_) : name(std::move(name_)) {}
TrialInfo::TrialInfo(RandomizerHintTextKey nameKey_, TrialKey trialKey_) : nameKey(std::move(nameKey_)), trialKey(std::move(trialKey_)) {}
TrialInfo::TrialInfo() = default;
TrialInfo::~TrialInfo() = default;
Text TrialInfo::GetName() const {
return name;
CustomMessage TrialInfo::GetName() const {
return StaticData::hintTextTable[nameKey].GetHintMessage();
}
RandomizerHintTextKey TrialInfo::GetNameKey() const {
return nameKey;
}
TrialKey TrialInfo::GetTrialKey() const {
return trialKey;
}
bool TrialInfo::IsSkipped() const {
@ -26,12 +35,9 @@ void TrialInfo::SetAsSkipped() {
}
Trials::Trials() {
mTrials[FOREST_TRIAL] = TrialInfo(Text{"the Forest Trial", "l'épreuve de la Forêt", "la prueba del bosque"});
mTrials[FIRE_TRIAL] = TrialInfo(Text{"the Fire Trial", "l'épreuve du Feu", "la prueba del fuego"});
mTrials[WATER_TRIAL] = TrialInfo(Text{"the Water Trial", "l'épreuve de l'Eau", "la prueba del agua"});
mTrials[SPIRIT_TRIAL] = TrialInfo(Text{"the Spirit Trial", "l'épreuve de l'Esprit", "la prueba del espíritu"});
mTrials[SHADOW_TRIAL] = TrialInfo(Text{"the Shadow Trial", "l'épreuve de l'Ombre", "la prueba de las sombras"});
mTrials[LIGHT_TRIAL] = TrialInfo(Text{"the Light Trial", "l'épreuve de la Lumière", "la prueba de la luz"});
for (const auto trial : StaticData::trialData){
mTrials[trial.first] = TrialInfo(trial.second, static_cast<TrialKey>(trial.first));
}
}
Trials::~Trials() = default;
@ -51,10 +57,10 @@ void Trials::RequireAll() {
}
}
std::array<TrialInfo*, 6> Trials::GetTrialList() {
std::array<TrialInfo*, 6> trialList{};
std::vector<TrialInfo*> Trials::GetTrialList() {
std::vector<TrialInfo*> trialList{};
for (size_t i = 0; i < mTrials.size(); i++) {
trialList[i] = &mTrials[i];
trialList.push_back(&mTrials[i]);
}
return trialList;
}
@ -62,6 +68,7 @@ std::array<TrialInfo*, 6> Trials::GetTrialList() {
size_t Trials::GetTrialListSize() const {
return mTrials.size();
}
void Trials::ParseJson(nlohmann::json spoilerFileJson) {
nlohmann::json trialsJson = spoilerFileJson["requiredTrials"];
for (auto it = trialsJson.begin(); it != trialsJson.end(); ++it) {
@ -75,4 +82,13 @@ void Trials::ParseJson(nlohmann::json spoilerFileJson) {
}
}
}
std::unordered_map<uint32_t, RandomizerHintTextKey> Trials::GetAllTrialHintHeys() const {
std::unordered_map<uint32_t, RandomizerHintTextKey> output = {};
for (auto trial: mTrials){
output[(uint32_t)trial.GetTrialKey()] = trial.GetNameKey();
}
return output;
}
} // namespace Rando

View File

@ -1,36 +1,31 @@
#pragma once
#include "3drando/text.hpp"
#include <array>
#include "randomizerTypes.h"
#include "../custom-message/CustomMessageManager.h"
#include <nlohmann/json.hpp>
#include "static_data.h"
#include <unordered_map>
namespace Rando {
class TrialInfo {
public:
explicit TrialInfo(Text name_);
explicit TrialInfo(RandomizerHintTextKey nameKey_, TrialKey key_);
TrialInfo();
~TrialInfo();
Text GetName() const;
CustomMessage GetName() const;
RandomizerHintTextKey GetNameKey() const;
TrialKey GetTrialKey() const;
bool IsSkipped() const;
bool IsRequired() const;
void SetAsRequired();
void SetAsSkipped();
private:
Text name;
RandomizerHintTextKey nameKey;
TrialKey trialKey;
bool skipped = true;
};
typedef enum {
LIGHT_TRIAL,
FOREST_TRIAL,
FIRE_TRIAL,
WATER_TRIAL,
SPIRIT_TRIAL,
SHADOW_TRIAL,
} TrialKey;
class Trials {
public:
Trials();
@ -38,10 +33,11 @@ class Trials {
TrialInfo* GetTrial(TrialKey key);
void SkipAll();
void RequireAll();
std::array<TrialInfo*, 6> GetTrialList();
std::vector<TrialInfo*> GetTrialList();
size_t GetTrialListSize() const;
void ParseJson(nlohmann::json spoilerFileJson);
std::unordered_map<uint32_t, RandomizerHintTextKey> GetAllTrialHintHeys() const;
private:
std::array<TrialInfo, 6> mTrials;
std::array<TrialInfo, TK_MAX> mTrials;
};
}

View File

@ -348,8 +348,9 @@ OTRGlobals::OTRGlobals() {
loader->RegisterResourceFactory(std::make_shared<SOH::ResourceFactoryBinaryRawJsonV0>(), RESOURCE_FORMAT_BINARY, "RawJson", static_cast<uint32_t>(SOH::ResourceType::SOH_RawJson), 0);
gSaveStateMgr = std::make_shared<SaveStateMgr>();
gRandoContext = Rando::Context::CreateInstance();
gRandoContext->InitStaticData();
gRandoContext = Rando::Context::CreateInstance();
Rando::StaticData::InitItemTable();//RANDOTODO make this not rely on context's logic so it can be initialised in InitStaticData
gRandoContext->AddExcludedOptions();
gRandoContext->GetSettings()->CreateOptions();
gRandomizer = std::make_shared<Randomizer>();
@ -2245,8 +2246,8 @@ extern "C" float OTRGetDimensionFromRightEdge(float v) {
return (SCREEN_WIDTH / 2 + SCREEN_HEIGHT / 2 * OTRGetAspectRatio() - (SCREEN_WIDTH - v));
}
f32 floorf(f32 x);
f32 ceilf(f32 x);
f32 floorf(f32 x);// RANDOTODO False positive error "allowing all exceptions is incompatible with previous function"
f32 ceilf(f32 x); // This gets annoying
extern "C" int16_t OTRGetRectDimensionFromLeftEdge(float v) {
return ((int)floorf(OTRGetDimensionFromLeftEdge(v)));
@ -2316,15 +2317,7 @@ extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSi
"%w\x02");
customMessage.Format();
std::string postfix;
if (gSaveContext.language == LANGUAGE_FRA) {
postfix = customMessage.GetFrench();
} else if (gSaveContext.language == LANGUAGE_GER) {
postfix = customMessage.GetGerman();
} else {
postfix = customMessage.GetEnglish();
}
std::string postfix = customMessage.GetForCurrentLanguage();
std::string str;
std::string FixedBaseStr(src);
int RemoveControlChar = FixedBaseStr.find_first_of("\x02");
@ -2478,6 +2471,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
CustomMessage messageEntry;
s16 actorParams = 0;
if (IS_RANDO) {
auto ctx = Rando::Context::GetInstance();
Player* player = GET_PLAYER(play);
if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) {
if (player->getItemEntry.getItemId == RG_ICE_TRAP) {
@ -2526,39 +2520,45 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == RO_GOSSIP_STONES_NEED_STONE && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
Actor* stone = GET_PLAYER(play)->targetActor;
actorParams = stone->params;
// if we're in a generic grotto
if (play->sceneNum == SCENE_GROTTOS && actorParams == 14360) {
RandomizerHint stoneHint = RH_NONE;
s16 hintParams = stone->params & 0xFF;
if (Rando::StaticData::stoneParamsToHint.contains(hintParams)){
stoneHint = Rando::StaticData::stoneParamsToHint[hintParams];
} else if (hintParams == 0x18){
// look for the chest in the actorlist to determine
// which grotto we're in
int numOfActorLists =
sizeof(play->actorCtx.actorLists) / sizeof(play->actorCtx.actorLists[0]);
for (int i = 0; i < numOfActorLists; i++) {
if (play->actorCtx.actorLists[i].length) {
if (play->actorCtx.actorLists[i].head->id == 10) {
// set the params for the hint check to be negative chest params
actorParams = 0 - play->actorCtx.actorLists[i].head->params;
if (play->actorCtx.actorLists[i].head->id == 10 &&
Rando::StaticData::grottoChestParamsToHint.contains(play->actorCtx.actorLists[i].head->params)) {
//use the chest params to find the stone hint
stoneHint = Rando::StaticData::grottoChestParamsToHint[play->actorCtx.actorLists[i].head->params];
}
}
}
}
RandomizerCheck hintCheck =
Randomizer_GetCheckFromActor(stone->id, play->sceneNum, actorParams);
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, hintCheck);
if (stoneHint == RH_NONE){
messageEntry = CustomMessage("INVALID STONE. PARAMS: " + std::to_string(hintParams));
} else {
messageEntry = ctx->GetHint(stoneHint)->GetHintMessage(MF_AUTO_FORMAT);
}
} else if ((textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT)) {
// rando hints at altar
messageEntry = (LINK_IS_ADULT)
? CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT)
: CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD);
messageEntry = (LINK_IS_ADULT) ? ctx->GetHint(RH_ALTAR_ADULT)->GetHintMessage() : ctx->GetHint(RH_ALTAR_CHILD)->GetHintMessage(MF_AUTO_FORMAT);
} else if (textId == TEXT_GANONDORF) {
if ((INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT && CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER)) ||
!Randomizer_GetSettingValue(RSK_LIGHT_ARROWS_HINT)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT);
} else {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF);
if (ctx->GetOption(RSK_GANONDORF_HINT)){
if (ctx->GetOption(RSK_SHUFFLE_MASTER_SWORD) && !CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER)){
messageEntry = INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT ?
ctx->GetHint(RH_GANONDORF_HINT)->GetHintMessage(MF_AUTO_FORMAT, 1):
ctx->GetHint(RH_GANONDORF_HINT)->GetHintMessage(MF_AUTO_FORMAT, 2);
} else {
messageEntry = INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT ?
ctx->GetHint(RH_GANONDORF_JOKE)->GetHintMessage(MF_AUTO_FORMAT):
ctx->GetHint(RH_GANONDORF_HINT)->GetHintMessage(MF_AUTO_FORMAT, 0);
}
}
} else if (textId == TEXT_SHEIK_NEED_HOOK || textId == TEXT_SHEIK_HAVE_HOOK) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetSheikMessage(gPlayState->sceneNum, textId);
@ -2585,74 +2585,131 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
} else if (CVarGetInteger("gRandoRelevantNavi", 1) && textId >= TEXT_NAVI_DEKU_TREE_SUMMONS && textId <= TEXT_NAVI_TRY_TO_KEEP_MOVING) {
u16 naviTextId = Random(0, NUM_NAVI_MESSAGES);
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::NaviRandoMessageTableID, naviTextId);
} else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS) && textId == TEXT_BEAN_SALESMAN_BUY_FOR_10) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_10);
} else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && (textId == TEXT_MEDIGORON ||
(textId == TEXT_GRANNYS_SHOP && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) &&
(Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK)) ||
(textId == TEXT_CARPET_SALESMAN_1 && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)) ||
(textId == TEXT_CARPET_SALESMAN_2 && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)))) {
}
else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_10 && ctx->GetOption(RSK_SHUFFLE_MAGIC_BEANS)) {
messageEntry = ctx->GetHint(RH_BEAN_SALESMAN)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_100) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_100);
}
else if (textId == TEXT_GRANNYS_SHOP && !Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
(ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE) || INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK)){
messageEntry = messageEntry = ctx->GetHint(RH_GRANNY)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_MEDIGORON && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF){
messageEntry = messageEntry = ctx->GetHint(RH_MEDIGORON)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_CARPET_SALESMAN_1 && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF){
messageEntry = messageEntry = ctx->GetHint(RH_CARPET_SALESMAN)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_CARPET_SALESMAN_2 && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF){
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId);
} else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) &&
(textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) {
}
else if ((textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)
&& ctx->GetOption(RSK_BOMBCHUS_IN_LOGIC)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
} else if (textId == TEXT_SKULLTULA_PEOPLE_IM_CURSED) {
}
else if (textId == TEXT_SKULLTULA_PEOPLE_IM_CURSED) {
actorParams = GET_PLAYER(play)->targetActor->params;
RandomizerSettingKey rsk = (RandomizerSettingKey)(RSK_KAK_10_SKULLS_HINT + (actorParams - 1));
if (Randomizer_GetSettingValue(rsk)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetCursedSkullMessage(actorParams,
OTRGlobals::Instance->gRandomizer->GetCheckFromActor(ACTOR_EN_SSH, SCENE_HOUSE_OF_SKULLTULA, actorParams));
if (actorParams == 1 && ctx->GetOption(RSK_KAK_10_SKULLS_HINT)){
messageEntry = ctx->GetHint(RH_KAK_10_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
} else if (actorParams == 2 && ctx->GetOption(RSK_KAK_20_SKULLS_HINT)){
messageEntry = ctx->GetHint(RH_KAK_20_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
} else if (actorParams == 3 && ctx->GetOption(RSK_KAK_30_SKULLS_HINT)){
messageEntry = ctx->GetHint(RH_KAK_30_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
} else if (actorParams == 4 && ctx->GetOption(RSK_KAK_40_SKULLS_HINT)){
messageEntry = ctx->GetHint(RH_KAK_40_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
} else if (ctx->GetOption(RSK_KAK_50_SKULLS_HINT)){
messageEntry = ctx->GetHint(RH_KAK_50_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
}
} else if (Randomizer_GetSettingValue(RSK_DAMPES_DIARY_HINT) && textId == TEXT_DAMPES_DIARY) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_DAMPES_DIARY);
} else if (play->sceneNum == SCENE_TREASURE_BOX_SHOP &&
Randomizer_GetSettingValue(RSK_GREG_HINT) &&
(textId == TEXT_CHEST_GAME_PROCEED || textId == TEXT_CHEST_GAME_REAL_GAMBLER || textId == TEXT_CHEST_GAME_THANKS_A_LOT)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_CHEST_GAME_PROCEED);
} else if (Randomizer_GetSettingValue(RSK_SHUFFLE_WARP_SONGS) &&
(textId >= TEXT_WARP_MINUET_OF_FOREST && textId <= TEXT_WARP_PRELUDE_OF_LIGHT)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, textId);
} else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
} else if (textId == TEXT_DAMPES_DIARY && ctx->GetOption(RSK_DAMPES_DIARY_HINT)) {
messageEntry = ctx->GetHint(RH_DAMPES_DIARY)->GetHintMessage(MF_AUTO_FORMAT);
}
else if ((textId == TEXT_CHEST_GAME_PROCEED || textId == TEXT_CHEST_GAME_REAL_GAMBLER || textId == TEXT_CHEST_GAME_THANKS_A_LOT) &&
play->sceneNum == SCENE_TREASURE_BOX_SHOP && ctx->GetOption(RSK_GREG_HINT)) {
messageEntry = ctx->GetHint(RH_GREG_RUPEE)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_WARP_MINUET_OF_FOREST && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_MINUET_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId == TEXT_WARP_BOLERO_OF_FIRE && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_BOLERO_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId == TEXT_WARP_SERENADE_OF_WATER && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_SERENADE_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId == TEXT_WARP_REQUIEM_OF_SPIRIT && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_REQUIEM_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId == TEXT_WARP_NOCTURNE_OF_SHADOW && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_NOCTURNE_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId == TEXT_WARP_PRELUDE_OF_LIGHT && ctx->GetOption(RSK_WARP_SONG_HINTS)) {
messageEntry = ctx->GetHint(RH_PRELUDE_WARP_LOC)->GetHintMessage(MF_FORMATTED);
}
else if (textId >= TEXT_WARP_MINUET_OF_FOREST && textId <= TEXT_WARP_PRELUDE_OF_LIGHT
&& ctx->GetOption(RSK_SHUFFLE_WARP_SONGS)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_WARP_MINUET_OF_FOREST);
}
else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId);
} else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
}
else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW);
} else if (textId == TEXT_FIRE_TEMPLE_GORON_OWE_YOU_BIG_TIME || (textId >= TEXT_FIRE_TEMPLE_GORON_FALLING_DOORS_SECRET && textId <= TEXT_FIRE_TEMPLE_GORON_SOUNDS_DIFFERENT_SECRET)) {
}
else if (textId == TEXT_FIRE_TEMPLE_GORON_OWE_YOU_BIG_TIME || (textId >= TEXT_FIRE_TEMPLE_GORON_FALLING_DOORS_SECRET && textId <= TEXT_FIRE_TEMPLE_GORON_SOUNDS_DIFFERENT_SECRET)) {
u16 choice = Random(0, NUM_GORON_MESSAGES);
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
} else if (Randomizer_GetSettingValue(RSK_FROGS_HINT) && textId == TEXT_FROGS_UNDERWATER) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_FROGS_UNDERWATER, RC_ZR_FROGS_OCARINA_GAME);
} else if (Randomizer_GetSettingValue(RSK_FISHING_POLE_HINT) && !Flags_GetRandomizerInf(RAND_INF_FISHING_POLE_FOUND) &&
(textId == TEXT_FISHING_POND_START || textId == TEXT_FISHING_POND_START_MET)) {
}
else if (textId == TEXT_FROGS_UNDERWATER && ctx->GetOption(RSK_FROGS_HINT)) {
messageEntry = ctx->GetHint(RH_FROGS_HINT)->GetHintMessage(MF_AUTO_FORMAT), TEXTBOX_TYPE_BLUE;
}
else if ((textId == TEXT_FISHING_POND_START || textId == TEXT_FISHING_POND_START_MET) &&
ctx->GetOption(RSK_SHUFFLE_FISHING_POLE) && !Flags_GetRandomizerInf(RAND_INF_FISHING_POLE_FOUND)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetFishingPondOwnerMessage(textId);
} else if (Randomizer_GetSettingValue(RSK_SARIA_HINT) &&
(gPlayState->sceneNum == SCENE_SACRED_FOREST_MEADOW && textId == TEXT_SARIA_SFM) || (textId >= TEXT_SARIAS_SONG_FACE_TO_FACE && textId <= TEXT_SARIAS_SONG_CHANNELING_POWER)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetSariaMessage(textId);
} else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_100) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_100);
} else if (Randomizer_GetSettingValue(RSK_BIGGORON_HINT) && (textId == TEXT_BIGGORON_BETTER_AT_SMITHING || textId == TEXT_BIGGORON_WAITING_FOR_YOU || textId == TEXT_BIGGORON_RETURN_AFTER_A_FEW_DAYS)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_BIGGORON_BETTER_AT_SMITHING, RC_DMT_TRADE_CLAIM_CHECK);
} else if (Randomizer_GetSettingValue(RSK_BIG_POES_HINT) && (textId == TEXT_GHOST_SHOP_EXPLAINATION || textId == TEXT_GHOST_SHOP_CARD_HAS_POINTS)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_GHOST_SHOP_CARD_HAS_POINTS, RC_MARKET_10_BIG_POES);
} else if (Randomizer_GetSettingValue(RSK_CHICKENS_HINT) && (textId >= TEXT_ANJU_PLEASE_BRING_MY_CUCCOS_BACK && textId <= TEXT_ANJU_PLEASE_BRING_1_CUCCO)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_ANJU_PLEASE_BRING_MY_CUCCOS_BACK, RC_KAK_ANJU_AS_CHILD);
} else if (Randomizer_GetSettingValue(RSK_MALON_HINT) && (textId == TEXT_MALON_EVERYONE_TURNING_EVIL || textId == TEXT_MALON_I_SING_THIS_SONG)) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_MALON_EVERYONE_TURNING_EVIL, RC_KF_LINKS_HOUSE_COW);
} else if (Randomizer_GetSettingValue(RSK_MALON_HINT) && textId == TEXT_MALON_HOW_IS_EPONA_DOING) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_MALON_HOW_IS_EPONA_DOING, RC_KF_LINKS_HOUSE_COW);
} else if (Randomizer_GetSettingValue(RSK_MALON_HINT) && textId == TEXT_MALON_OBSTICLE_COURSE) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_MALON_OBSTICLE_COURSE, RC_KF_LINKS_HOUSE_COW);
} else if (Randomizer_GetSettingValue(RSK_MALON_HINT) && textId == TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED, RC_KF_LINKS_HOUSE_COW);
} else if (Randomizer_GetSettingValue(RSK_KAK_100_SKULLS_HINT) && textId == TEXT_SKULLTULA_PEOPLE_MAKE_YOU_VERY_RICH) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetCursedSkullMessage(10, RC_KAK_100_GOLD_SKULLTULA_REWARD);
} else if (Randomizer_GetSettingValue(RSK_HBA_HINT) && textId == TEXT_GF_HBA_SIGN) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_GF_HBA_SIGN, RC_GF_HBA_1000_POINTS, RC_GF_HBA_1500_POINTS);
} else if (Randomizer_GetSettingValue(RSK_HBA_HINT) && textId == TEXT_HBA_NOT_ON_HORSE) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_HBA_NOT_ON_HORSE, RC_GF_HBA_1000_POINTS, RC_GF_HBA_1500_POINTS);
} else if (Randomizer_GetSettingValue(RSK_HBA_HINT) && textId == TEXT_HBA_INITIAL_EXPLAINATION) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_HBA_INITIAL_EXPLAINATION, RC_GF_HBA_1000_POINTS, RC_GF_HBA_1500_POINTS);
} else if (Randomizer_GetSettingValue(RSK_HBA_HINT) && textId == TEXT_HBA_ALREADY_HAVE_1000) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetMiscHintMessage(TEXT_HBA_ALREADY_HAVE_1000, RC_GF_HBA_1500_POINTS);
}
else if (textId == TEXT_SARIA_SFM && gPlayState->sceneNum == SCENE_SACRED_FOREST_MEADOW && ctx->GetOption(RSK_SARIA_HINT)){
messageEntry = ctx->GetHint(RH_SARIA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 0);
}
else if ((textId >= TEXT_SARIAS_SONG_FACE_TO_FACE && textId <= TEXT_SARIAS_SONG_CHANNELING_POWER) && ctx->GetOption(RSK_SARIA_HINT)){
messageEntry = ctx->GetHint(RH_SARIA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 1);
}
else if (ctx->GetOption(RSK_BIGGORON_HINT) && (textId == TEXT_BIGGORON_BETTER_AT_SMITHING || textId == TEXT_BIGGORON_WAITING_FOR_YOU ||
textId == TEXT_BIGGORON_RETURN_AFTER_A_FEW_DAYS || textId == TEXT_BIGGORON_I_MAAAADE_THISSSS)) {
messageEntry = ctx->GetHint(RH_BIGGORON_HINT)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (ctx->GetOption(RSK_BIG_POES_HINT) && (textId == TEXT_GHOST_SHOP_EXPLAINATION || textId == TEXT_GHOST_SHOP_CARD_HAS_POINTS)) {
messageEntry = ctx->GetHint(RH_BIG_POES_HINT)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (ctx->GetOption(RSK_CHICKENS_HINT) && (textId >= TEXT_ANJU_PLEASE_BRING_MY_CUCCOS_BACK && textId <= TEXT_ANJU_PLEASE_BRING_1_CUCCO)) {
messageEntry = ctx->GetHint(RH_CHICKENS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
}
else if ((textId == TEXT_MALON_EVERYONE_TURNING_EVIL || textId == TEXT_MALON_I_SING_THIS_SONG)&& ctx->GetOption(RSK_MALON_HINT)){
messageEntry = ctx->GetHint(RH_MALON_HINT)->GetHintMessage(MF_AUTO_FORMAT, 0);
}
else if (textId == TEXT_MALON_HOW_IS_EPONA_DOING && ctx->GetOption(RSK_MALON_HINT)){
messageEntry = ctx->GetHint(RH_MALON_HINT)->GetHintMessage(MF_AUTO_FORMAT, 1);
}
else if (textId == TEXT_MALON_OBSTICLE_COURSE && ctx->GetOption(RSK_MALON_HINT)){
messageEntry = ctx->GetHint(RH_MALON_HINT)->GetHintMessage(MF_AUTO_FORMAT, 2);
}
else if (textId == TEXT_MALON_INGO_MUST_HAVE_BEEN_TEMPTED && ctx->GetOption(RSK_MALON_HINT)){
messageEntry = ctx->GetHint(RH_MALON_HINT)->GetHintMessage(MF_AUTO_FORMAT, 3);
}
else if (ctx->GetOption(RSK_KAK_100_SKULLS_HINT) && textId == TEXT_SKULLTULA_PEOPLE_MAKE_YOU_VERY_RICH) {
messageEntry = ctx->GetHint(RH_KAK_100_SKULLS_HINT)->GetHintMessage(MF_AUTO_FORMAT);
}
else if (textId == TEXT_GF_HBA_SIGN && ctx->GetOption(RSK_HBA_HINT)) {
messageEntry = ctx->GetHint(RH_HBA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 0);
}
else if (textId == TEXT_HBA_NOT_ON_HORSE && ctx->GetOption(RSK_HBA_HINT)) {
messageEntry = ctx->GetHint(RH_HBA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 1);
}
else if (textId == TEXT_HBA_INITIAL_EXPLAINATION && ctx->GetOption(RSK_HBA_HINT)) {
messageEntry = ctx->GetHint(RH_HBA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 2);
}
else if (textId == TEXT_HBA_ALREADY_HAVE_1000 && ctx->GetOption(RSK_HBA_HINT)) {
messageEntry = ctx->GetHint(RH_HBA_HINT)->GetHintMessage(MF_AUTO_FORMAT, 3);
}
}
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {
@ -2674,16 +2731,16 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
// In rando we need to bump the token count by one to show the correct count
s16 gsCount = gSaveContext.inventory.gsTokens + (IS_RANDO ? 1 : 0);
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
messageEntry.Replace("{{gsCount}}", std::to_string(gsCount));
messageEntry.Replace("[[gsCount]]", std::to_string(gsCount));
}
}
if (textId == TEXT_HEART_CONTAINER && CVarGetInteger("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_CONTAINER);
messageEntry.Replace("{{heartContainerCount}}", std::to_string(gSaveContext.sohStats.heartContainers + 1));
messageEntry.Replace("[[heartContainerCount]]", std::to_string(gSaveContext.sohStats.heartContainers + 1));
}
if (textId == TEXT_HEART_PIECE && CVarGetInteger("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_PIECE);
messageEntry.Replace("{{heartPieceCount}}", std::to_string(gSaveContext.sohStats.heartPieces + 1));
messageEntry.Replace("[[heartPieceCount]]", std::to_string(gSaveContext.sohStats.heartPieces + 1));
}
if (textId == TEXT_MARKET_GUARD_NIGHT && CVarGetInteger("gMarketSneak", 0) && play->sceneNum == SCENE_MARKET_ENTRANCE_NIGHT) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT);
@ -2695,14 +2752,14 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
switch (gSaveContext.language) {
case LANGUAGE_FRA:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.GetFrench(), buffer, maxBufferSize);
CopyStringToCharBuffer(messageEntry.GetFrench(MF_RAW), buffer, maxBufferSize);
case LANGUAGE_GER:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.GetGerman(), buffer, maxBufferSize);
CopyStringToCharBuffer(messageEntry.GetGerman(MF_RAW), buffer, maxBufferSize);
case LANGUAGE_ENG:
default:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.GetEnglish(), buffer, maxBufferSize);
CopyStringToCharBuffer(messageEntry.GetEnglish(MF_RAW), buffer, maxBufferSize);
}
return false;
}

View File

@ -6,6 +6,8 @@
#include "Enhancements/randomizer/dungeon.h"
#include "Enhancements/randomizer/trial.h"
#include "soh/util.h"
#include "Enhancements/randomizer/hint.h"
#include "Enhancements/randomizer/item.h"
#include "z64.h"
#include "functions.h"
@ -57,6 +59,51 @@ std::filesystem::path SaveManager::GetFileTempName(int fileNum) {
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".temp");
}
std::vector<RandomizerHint> Rando::StaticData::oldVerHintOrder {
RH_COLOSSUS_GOSSIP_STONE,
RH_DMC_GOSSIP_STONE,
RH_DMC_UPPER_GROTTO_GOSSIP_STONE,
RH_DMT_GOSSIP_STONE,
RH_DMT_STORMS_GROTTO_GOSSIP_STONE,
RH_DODONGOS_CAVERN_GOSSIP_STONE,
RH_ZF_FAIRY_GOSSIP_STONE,
RH_GC_MAZE_GOSSIP_STONE,
RH_GC_MEDIGORON_GOSSIP_STONE,
RH_GV_GOSSIP_STONE,
RH_GRAVEYARD_GOSSIP_STONE,
RH_HC_MALON_GOSSIP_STONE,
RH_HC_ROCK_WALL_GOSSIP_STONE,
RH_HC_STORMS_GROTTO_GOSSIP_STONE,
RH_HF_COW_GROTTO_GOSSIP_STONE,
RH_HF_NEAR_MARKET_GROTTO_GOSSIP_STONE,
RH_HF_OPEN_GROTTO_GOSSIP_STONE,
RH_HF_SOUTHEAST_GROTTO_GOSSIP_STONE,
RH_ZF_JABU_GOSSIP_STONE,
RH_KF_DEKU_TREE_LEFT_GOSSIP_STONE,
RH_KF_DEKU_TREE_RIGHT_GOSSIP_STONE,
RH_KF_GOSSIP_STONE,
RH_KF_STORMS_GROTTO_GOSSIP_STONE,
RH_KAK_OPEN_GROTTO_GOSSIP_STONE,
RH_LH_LAB_GOSSIP_STONE,
RH_LH_SOUTHEAST_GOSSIP_STONE,
RH_LH_SOUTHWEST_GOSSIP_STONE,
RH_LW_GOSSIP_STONE,
RH_LW_NEAR_SHORTCUTS_GROTTO_GOSSIP_STONE,
RH_SFM_MAZE_NEAR_LW_GOSSIP_STONE,
RH_SFM_MAZE_CENTER_GOSSIP_STONE,
RH_SFM_SARIA_GOSSIP_STONE,
RH_TOT_LEFT_CENTER_GOSSIP_STONE,
RH_TOT_LEFTMOST_GOSSIP_STONE,
RH_TOT_RIGHT_CENTER_GOSSIP_STONE,
RH_TOT_RIGHTMOST_GOSSIP_STONE,
RH_ZD_GOSSIP_STONE,
RH_ZR_NEAR_DOMAIN_GOSSIP_STONE,
RH_ZR_NEAR_GROTTOS_GOSSIP_STONE,
RH_ZR_OPEN_GROTTO_GOSSIP_STONE,
};
uint16_t Rando::StaticData::oldVerGossipStoneStart = 740;
SaveManager::SaveManager() {
coreSectionIDsByName["base"] = SECTION_ID_BASE;
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
@ -106,6 +153,7 @@ SaveManager::SaveManager() {
}
}
// RANDOTODO should we just have dummy functions that raise warnings instead if these aren't supported?
void SaveManager::LoadRandomizerVersion1() {
auto randoContext = Rando::Context::GetInstance();
RandomizerCheck location = RC_UNKNOWN_CHECK;
@ -140,32 +188,33 @@ void SaveManager::LoadRandomizerVersion1() {
for (int j = 0; j < ARRAY_COUNT(hintText); j++) {
SaveManager::Instance->LoadData("ht" + std::to_string(i) + "-" + std::to_string(j), hintText[j]);
}
randoContext->AddHint(RandomizerHintKey(check - RC_COLOSSUS_GOSSIP_STONE + 1), Text(hintText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[Rando::StaticData::oldVerGossipStoneStart];
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, {CustomMessage(hintText)}));
}
char childAltarText[250];
for (int i = 0; i < ARRAY_COUNT(childAltarText); i++) {
SaveManager::Instance->LoadData("cat" + std::to_string(i), childAltarText[i]);
}
randoContext->AddHint(RH_ALTAR_CHILD, Text(childAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, {CustomMessage(childAltarText)}));
char adultAltarText[750];
for (int i = 0; i < ARRAY_COUNT(adultAltarText); i++) {
SaveManager::Instance->LoadData("aat" + std::to_string(i), adultAltarText[i]);
}
randoContext->AddHint(RH_ALTAR_ADULT, Text(adultAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, {CustomMessage(adultAltarText)}));
char ganonHintText[150];
for (int i = 0; i < ARRAY_COUNT(ganonHintText); i++) {
SaveManager::Instance->LoadData("ght" + std::to_string(i), ganonHintText[i]);
}
randoContext->AddHint(RH_GANONDORF_HINT, Text(ganonHintText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, {CustomMessage(ganonHintText)}));
char ganonText[250];
for (int i = 0; i < ARRAY_COUNT(ganonText); i++) {
SaveManager::Instance->LoadData("gt" + std::to_string(i), ganonText[i]);
}
randoContext->AddHint(RH_GANONDORF_NOHINT, Text(ganonText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, {CustomMessage(ganonText)}));
SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems);
@ -193,6 +242,7 @@ void SaveManager::LoadRandomizerVersion1() {
});
}
//RANDOTODO if we actually support this, be less lazy
void SaveManager::LoadRandomizerVersion2() {
auto randoContext = Rando::Context::GetInstance();
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
@ -244,56 +294,57 @@ void SaveManager::LoadRandomizerVersion2() {
if (rc != RC_UNKNOWN_CHECK) {
std::string hintText;
SaveManager::Instance->LoadData("hintText", hintText);
randoContext->AddHint(RandomizerHintKey(rc - RC_COLOSSUS_GOSSIP_STONE + 1), Text(hintText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
RandomizerHint stoneHint = Rando::StaticData::oldVerHintOrder[Rando::StaticData::oldVerGossipStoneStart];
randoContext->AddHint(stoneHint, Rando::Hint(stoneHint, {CustomMessage(hintText)}));
}
});
});
std::string childAltarText;
SaveManager::Instance->LoadData("childAltarText", childAltarText);
randoContext->AddHint(RH_ALTAR_CHILD, Text(childAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_ALTAR_CHILD, Rando::Hint(RH_ALTAR_CHILD, {CustomMessage(childAltarText)}));
std::string adultAltarText;
SaveManager::Instance->LoadData("adultAltarText", adultAltarText);
randoContext->AddHint(RH_ALTAR_ADULT, Text(adultAltarText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_ALTAR_ADULT, Rando::Hint(RH_ALTAR_ADULT, {CustomMessage(adultAltarText)}));
std::string ganonHintText;
SaveManager::Instance->LoadData("ganonHintText", ganonHintText);
randoContext->AddHint(RH_GANONDORF_HINT, Text(ganonHintText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_GANONDORF_HINT, Rando::Hint(RH_GANONDORF_HINT, {CustomMessage(ganonHintText)}));
std::string ganonText;
SaveManager::Instance->LoadData("ganonText", ganonText);
randoContext->AddHint(RH_GANONDORF_NOHINT, Text(ganonText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_GANONDORF_JOKE, Rando::Hint(RH_GANONDORF_JOKE, {CustomMessage(ganonText)}));
std::string dampeText;
SaveManager::Instance->LoadData("dampeText", dampeText);
randoContext->AddHint(RH_DAMPES_DIARY, Text(dampeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_DAMPES_DIARY, Rando::Hint(RH_DAMPES_DIARY, {CustomMessage(dampeText)}));
std::string gregHintText;
SaveManager::Instance->LoadData("gregHintText", gregHintText);
randoContext->AddHint(RH_GREG_RUPEE, Text(gregHintText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_GREG_RUPEE, Rando::Hint(RH_GREG_RUPEE, {CustomMessage(gregHintText)}));
std::string sheikText;
SaveManager::Instance->LoadData("sheikText", sheikText);
randoContext->AddHint(RH_SHEIK_LIGHT_ARROWS, Text(sheikText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_SHEIK_HINT, Rando::Hint(RH_SHEIK_HINT, {CustomMessage(sheikText)}));
std::string sariaText;
SaveManager::Instance->LoadData("sariaText", sariaText);
randoContext->AddHint(RH_SARIA, Text(sariaText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_SARIA_HINT, Rando::Hint(RH_SARIA_HINT, {CustomMessage(sariaText)}));
std::string fishingPoleText;
SaveManager::Instance->LoadData("fishingPoleText", fishingPoleText);
randoContext->AddHint(RH_FISHING_POLE, Text(fishingPoleText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static");
randoContext->AddHint(RH_FISHING_POLE, Rando::Hint(RH_FISHING_POLE, {CustomMessage(fishingPoleText)}));
std::string warpMinuetText;
SaveManager::Instance->LoadData("warpMinuetText", warpMinuetText);
randoContext->AddHint(RH_MINUET_WARP_LOC, Text(warpMinuetText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpMinuetText));
randoContext->AddHint(RH_MINUET_WARP_LOC, Rando::Hint(RH_MINUET_WARP_LOC, {CustomMessage(warpMinuetText)}));
std::string warpBoleroText;
SaveManager::Instance->LoadData("warpBoleroText", warpBoleroText);
randoContext->AddHint(RH_BOLERO_WARP_LOC, Text(warpBoleroText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpBoleroText));
randoContext->AddHint(RH_BOLERO_WARP_LOC, Rando::Hint(RH_BOLERO_WARP_LOC, {CustomMessage(warpBoleroText)}));
std::string warpSerenadeText;
SaveManager::Instance->LoadData("warpSerenadeText", warpSerenadeText);
randoContext->AddHint(RH_SERENADE_WARP_LOC, Text(warpSerenadeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpSerenadeText));
randoContext->AddHint(RH_SERENADE_WARP_LOC, Rando::Hint(RH_SERENADE_WARP_LOC, {CustomMessage(warpSerenadeText)}));
std::string warpRequiemText;
SaveManager::Instance->LoadData("warpRequiemText", warpRequiemText);
randoContext->AddHint(RH_REQUIEM_WARP_LOC, Text(warpRequiemText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpRequiemText));
randoContext->AddHint(RH_REQUIEM_WARP_LOC, Rando::Hint(RH_REQUIEM_WARP_LOC, {CustomMessage(warpRequiemText)}));
std::string warpNocturneText;
SaveManager::Instance->LoadData("warpNocturneText", warpNocturneText);
randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Text(warpNocturneText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpNocturneText));
randoContext->AddHint(RH_NOCTURNE_WARP_LOC, Rando::Hint(RH_NOCTURNE_WARP_LOC, {CustomMessage(warpNocturneText)}));
std::string warpPreludeText;
SaveManager::Instance->LoadData("warpPreludeText", warpPreludeText);
randoContext->AddHint(RH_PRELUDE_WARP_LOC, Text(warpPreludeText), RC_UNKNOWN_CHECK, HINT_TYPE_STATIC, "Static", randoContext->GetAreaFromString(warpPreludeText));
randoContext->AddHint(RH_PRELUDE_WARP_LOC, Rando::Hint(RH_PRELUDE_WARP_LOC, {CustomMessage(warpPreludeText)}));
SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems);
@ -386,23 +437,10 @@ void SaveManager::LoadRandomizerVersion3() {
});
SaveManager::Instance->LoadArray("hintLocations", RH_MAX, [&](size_t i) {
SaveManager::Instance->LoadStruct("", [&]() {
RandomizerHintKey rhk = RH_NONE;
SaveManager::Instance->LoadData("hintKey", rhk);
std::string english, french, german;
SaveManager::Instance->LoadStruct("hintText", [&]() {
SaveManager::Instance->LoadData("english", english);
SaveManager::Instance->LoadData("french", french);
SaveManager::Instance->LoadData("german", german);
});
RandomizerCheck rc = RC_UNKNOWN_CHECK;
SaveManager::Instance->LoadData("hintedCheck", rc);
HintType ht = HINT_TYPE_STATIC;
SaveManager::Instance->LoadData("hintType", ht);
RandomizerArea savedArea;
SaveManager::Instance->LoadData("hintedArea", savedArea);
randoContext->AddHint(rhk, Text(english, french, /*spanish*/"", german), rc, ht, "Unknown", savedArea);//RANDOTODO, maybe store and load distrabution, but it's a string...
});
auto hint = RandomizerHint(i);
nlohmann::ordered_json json;
SaveManager::Instance->LoadData("", json);
randoContext->AddHint(hint, Rando::Hint(hint, json));
});
SaveManager::Instance->LoadData("adultTradeItems", gSaveContext.adultTradeItems);
@ -477,17 +515,68 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
});
SaveManager::Instance->SaveArray("hintLocations", RH_MAX, [&](size_t i) {
auto hint = randoContext->GetHint(RandomizerHint(i));
// RANDOTODO a way for saveData to accept a raw JSON would make maintaining hint code nicer.
// save manager forces code rewrites between the spoiler log and internal saves, when the code needs to do the exact same thing
// in cases where data needs to be loaded in from the spoiler log for plando mode.
// fails as push_back is ambiguous
// SaveManager::Instance->SaveData(Rando::StaticData::hintNames[(uint32_t)hint].GetEnglish(), hint->toJSON());
SaveManager::Instance->SaveStruct("", [&]() {
auto hint = randoContext->GetHint(RandomizerHintKey(i));
SaveManager::Instance->SaveData("hintKey", RandomizerHintKey(i));
SaveManager::Instance->SaveStruct("hintText", [&]() {
SaveManager::Instance->SaveData("english", hint->GetText().GetEnglish());
SaveManager::Instance->SaveData("french", hint->GetText().GetFrench());
SaveManager::Instance->SaveData("german", hint->GetText().GetGerman());
});
SaveManager::Instance->SaveData("hintedCheck", hint->GetHintedLocation());
SaveManager::Instance->SaveData("hintType", hint->GetHintType());
SaveManager::Instance->SaveData("hintedArea", hint->GetHintedArea());
bool enabled = hint->IsEnabled();
SaveManager::Instance->SaveData("enabled", enabled);
if (enabled){
std::vector<std::string> messages = hint->GetAllMessageStrings(MF_RAW);
SaveManager::Instance->SaveArray("messages", messages.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", messages[i]);
});
SaveManager::Instance->SaveData("distribution", hint->GetDistribution());
SaveManager::Instance->SaveData("type", Rando::StaticData::hintTypeNames[hint->GetHintType()].GetEnglish(MF_CLEAN));
std::vector<RandomizerHintTextKey> hintKeys = hint->GetHintTextKeys();
SaveManager::Instance->SaveArray("hintKeys", hintKeys.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", hintKeys[i]);
});
std::vector<RandomizerCheck> locations = hint->GetHintedLocations();
SaveManager::Instance->SaveArray("locations", locations.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", Rando::StaticData::GetLocation(locations[i])->GetName());
});
std::vector<RandomizerGet> items = hint->GetHintedItems();
SaveManager::Instance->SaveArray("items", items.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", Rando::StaticData::GetItemTable()[items[i]].GetName().GetEnglish());
});
std::vector<uint8_t> itemNamesChosen = hint->GetItemNamesChosen();
SaveManager::Instance->SaveArray("itemNamesChosen", itemNamesChosen.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", itemNamesChosen[i]);
});
std::vector<uint8_t> hintTextsChosen = hint->GetHintTextsChosen();
SaveManager::Instance->SaveArray("hintTextsChosen", hintTextsChosen.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", hintTextsChosen[i]);
});
std::vector<uint8_t> areaTextsChosen = hint->GetAreaTextsChosen();
SaveManager::Instance->SaveArray("areaTextsChosen", areaTextsChosen.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", areaTextsChosen[i]);
});
std::vector<RandomizerArea> areas = hint->GetHintedAreas();
SaveManager::Instance->SaveArray("areas", areas.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", Rando::StaticData::hintTextTable[Rando::StaticData::areaNames[areas[i]]].GetClear().GetForCurrentLanguage(MF_CLEAN));
});
std::vector<TrialKey> trials = hint->GetHintedTrials();
SaveManager::Instance->SaveArray("trials", trials.size(), [&](size_t i) {
SaveManager::Instance->SaveData("", randoContext->GetTrial(trials[i])->GetName().GetForCurrentLanguage(MF_CLEAN));
});
SaveManager::Instance->SaveData("num", hint->GetNum());
}
});
});

View File

@ -183,6 +183,7 @@ class SaveManager {
nlohmann::json* currentJsonContext = nullptr;
nlohmann::json::iterator currentJsonArrayContext;
std::shared_ptr<BS::thread_pool> smThreadPool;
};
#else

View File

@ -137,19 +137,19 @@ extern "C" void OTRMessage_Init()
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN,
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r{{gsCount}}%w tokens&in total!\x0E\x3C",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r{{gsCount}}&%wGoldene "
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r[[gsCount]]%w tokens&in total!\x0E\x3C",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[gsCount]]&%wGoldene "
"Skulltula-Symbole&gesammelt!\x0E\x3C",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r{{gsCount}}%w symboles en "
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[gsCount]]%w symboles en "
"tout!\x0E\x3C",
TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN,
CustomMessage(
"You got a %rGold Skulltula Token%w!&You've collected %r{{gsCount}}%w tokens&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r{{gsCount}}&%wGoldene "
"You got a %rGold Skulltula Token%w!&You've collected %r[[gsCount]]%w tokens&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[gsCount]]&%wGoldene "
"Skulltula-Symbole&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r{{gsCount}}%w symboles en tout!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[gsCount]]%w symboles en tout!",
TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_BUY_BOMBCHU_10_DESC,
@ -168,14 +168,14 @@ extern "C" void OTRMessage_Init()
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_CONTAINER, ITEM_HEART_CONTAINER,
CustomMessage(
"You got a %rHeart Container%w!&You've collected %r{{heartContainerCount}}%w containers&in total!",
"Ein %rHerzcontainer%w!&Du hast nun insgesamt %r{{heartContainerCount}}%w&Herzcontainer gesammelt!",
"Vous obtenez un %rCoeur&d'Energie%w! Vous en avez&collecté %r{{heartContainerCount}}%w en tout!"));
"You got a %rHeart Container%w!&You've collected %r[[heartContainerCount]]%w containers&in total!",
"Ein %rHerzcontainer%w!&Du hast nun insgesamt %r[[heartContainerCount]]%w&Herzcontainer gesammelt!",
"Vous obtenez un %rCoeur&d'Energie%w! Vous en avez&collecté %r[[heartContainerCount]]%w en tout!"));
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_PIECE, ITEM_HEART_PIECE,
CustomMessage("You got a %rHeart Piece%w!&You've collected %r{{heartPieceCount}}%w pieces&in total!",
"Ein %rHerzteil%w!&Du hast nun insgesamt %r{{heartPieceCount}}%w&Herteile gesammelt!",
"Vous obtenez un %rQuart de&Coeur%w! Vous en avez collecté&%r{{heartPieceCount}}%w en tout!",
CustomMessage("You got a %rHeart Piece%w!&You've collected %r[[heartPieceCount]]%w pieces&in total!",
"Ein %rHerzteil%w!&Du hast nun insgesamt %r[[heartPieceCount]]%w&Herteile gesammelt!",
"Vous obtenez un %rQuart de&Coeur%w! Vous en avez collecté&%r[[heartPieceCount]]%w en tout!",
TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_MARKET_GUARD_NIGHT,

View File

@ -16,9 +16,6 @@
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
#define TEXT_SHEIK_NEED_HOOK 0x700F
#define TEXT_SHEIK_HAVE_HOOK 0x7010
void EnXc_Init(Actor* thisx, PlayState* play);
void EnXc_Destroy(Actor* thisx, PlayState* play);
void EnXc_Update(Actor* thisx, PlayState* play);
@ -2239,27 +2236,11 @@ void EnXc_SetupDialogueAction(EnXc* this, PlayState* play) {
if (Actor_ProcessTalkRequest(&this->actor, play)) {
this->action = SHEIK_ACTION_IN_DIALOGUE;
} else {
this->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY;
if (IS_RANDO && gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME) {
if (!CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_GANONS_TOWER)) {
this->actor.textId = TEXT_SHEIK_NEED_HOOK;
} else {
this->actor.textId = TEXT_SHEIK_HAVE_HOOK;
}
} else if (IS_RANDO && gPlayState->sceneNum == SCENE_INSIDE_GANONS_CASTLE) {
if (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_MASTER) && INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT &&
CUR_CAPACITY(UPG_QUIVER) >= 30 && gSaveContext.isMagicAcquired) {
this->actor.textId = TEXT_SHEIK_HAVE_HOOK;
} else {
this->actor.textId = TEXT_SHEIK_NEED_HOOK;
}
}
else {
if (INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE) {
this->actor.textId = 0x7010; //"You have what you need"
} else {
this->actor.textId = 0x700F; //"You need another skill"
}
this->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY; //this arrangment is cute but I would rather handle all message selection in ship code
if (INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE) {
this->actor.textId = 0x7010; //"You have what you need"
} else {
this->actor.textId = 0x700F; //"You need another skill"
}
func_8002F2F4(&this->actor, play);
}