diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp index 222a6c556..7110d25d3 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -4,94 +4,189 @@ using namespace std::literals::string_literals; -CustomMessageManager::CustomMessageManager() { - this->textBoxSpecialCharacters = { { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, - { "È", 0x85 }, { "É", 0x86 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, - { "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, { "Ü", 0x8E }, - { "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, - { "ç", 0x94 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, - { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, { "ù", 0x9C }, { "û", 0x9D }, - { "ü", 0x9E } }; - this->colors = { { "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 textBoxSpecialCharacters = { + { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, { "È", 0x85 }, { "É", 0x86 }, + { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, { "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, + { "Ü", 0x8E }, { "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, { "ç", 0x94 }, + { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, + { "ù", 0x9C }, { "û", 0x9D }, { "ü", 0x9E } +}; +static const std::unordered_map colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN }, + { "b", QM_BLUE }, { "c", QM_LBLUE }, { "p", QM_PINK }, + { "y", QM_YELLOW }, { "B", QM_BLACK } }; + +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_) { } -CustomMessageManager::~CustomMessageManager() { - this->textBoxSpecialCharacters.clear(); - this->colors.clear(); - this->messageTables.clear(); +const std::string& CustomMessage::GetEnglish() const { + return english; } -void CustomMessageManager::ReplaceSpecialCharacters(std::string& string) { +const std::string& CustomMessage::GetFrench() const { + if (french.length() > 0) { + return french; + } + return english; +} +const std::string& CustomMessage::GetGerman() const { + if (german.length() > 0) { + return german; + } + return english; +} + +const TextBoxType& CustomMessage::GetTextBoxType() const { + return type; +} + +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()); +} + +CustomMessage CustomMessage::operator+(const std::string& right) const { + return CustomMessage(english + right, german + right, french + right); +} + +void CustomMessage::operator+=(const std::string& right) { + english += right; + french += right; + german += right; +} + +bool CustomMessage::operator==(const CustomMessage& operand) const { + return english == operand.english; +} + +bool CustomMessage::operator!=(const CustomMessage& operand) const { + return !operator==(operand); +} + +void CustomMessage::Replace(std::string&& oldStr, std::string&& newStr) { + for (std::string* str : { &english, &french, &german }) { + size_t position = str->find(oldStr); + while (position != std::string::npos) { + 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); + } + position = french.find(oldStr); + while (position != std::string::npos) { + french.replace(position, oldStr.length(), newEnglish); + position = french.find(oldStr); + } + position = german.find(oldStr); + while (position != std::string::npos) { + german.replace(position, oldStr.length(), newEnglish); + position = german.find(oldStr); + } + Format(); +} + +void CustomMessage::Format(ItemID iid) { + for (std::string* str : { &english, &french, &german }) { + 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)); + start_pos += 3; + } + std::replace(str->begin(), str->end(), '@', PLAYER_NAME()[0]); + } + ReplaceSpecialCharacters(); + ReplaceColors(); + *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]); + } + ReplaceSpecialCharacters(); + ReplaceColors(); + *this += MESSAGE_END(); +} + +void CustomMessage::Capitalize() { + for (std::string* str : { &english, &french, &german }) { + (*str)[0] = std::toupper((*str)[0]); + } +} + +void CustomMessage::ReplaceSpecialCharacters() { // add special characters - for (auto specialCharacterPair : this->textBoxSpecialCharacters) { - size_t start_pos = 0; - std::string textBoxSpecialCharacterString = ""s; - textBoxSpecialCharacterString += specialCharacterPair.second; - while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) { - string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); - start_pos += textBoxSpecialCharacterString.length(); + 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(); + } } } } -void CustomMessageManager::ReplaceColors(std::string& string) { - for (auto colorPair : colors) { - std::string textToReplace = "%"; - textToReplace += colorPair.first; - size_t start_pos = 0; - while ((start_pos = string.find(textToReplace)) != std::string::npos) { - string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second)); - start_pos += textToReplace.length(); +void CustomMessage::ReplaceColors() { + for (std::string* str : { &english, &french, &german }) { + for (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 ReplaceString(std::string& source, std::string textToReplace, std::string value) { - size_t pos = source.find(textToReplace); - if (pos != std::string::npos) { - source.replace(pos, textToReplace.length(), value); - } - CustomMessageManager::Instance->FormatCustomMessage(source); +const std::string CustomMessage::MESSAGE_END() const { + return "\x02"s; } -void CustomMessageManager::ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string value) { - ReplaceString(messageEntry.english, textToReplace, value); - ReplaceString(messageEntry.german, textToReplace, value); - ReplaceString(messageEntry.french, textToReplace, value); +const std::string CustomMessage::ITEM_OBTAINED(uint8_t x) const { + return "\x13"s + char(x); } -void CustomMessageManager::ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string englishValue, std::string germanValue, std::string frenchValue) { - ReplaceString(messageEntry.english, textToReplace, englishValue); - ReplaceString(messageEntry.german, textToReplace, germanValue); - ReplaceString(messageEntry.french, textToReplace, frenchValue); +const std::string CustomMessage::NEWLINE() const { + return "\x01"s; } -void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) { - message.insert(0, ITEM_OBTAINED(iid)); - size_t start_pos = 0; - std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); - while ((start_pos = message.find('^', start_pos)) != std::string::npos) { - message.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid)); - start_pos += 3; - } - std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); - ReplaceSpecialCharacters(message); - ReplaceColors(message); - message += MESSAGE_END(); +const std::string CustomMessage::COLOR(uint8_t x) const { + return "\x05"s + char(x); } -void CustomMessageManager::FormatCustomMessage(std::string& message) { - size_t start_pos = 0; - std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); - std::replace(message.begin(), message.end(), '^', WAIT_FOR_INPUT()[0]); - std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); - ReplaceSpecialCharacters(message); - ReplaceColors(message); - message += MESSAGE_END(); +const std::string CustomMessage::WAIT_FOR_INPUT() const { + return "\x04"s; } -bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages) { +const std::string CustomMessage::PLAYER_NAME() const { + return "\x0F"s; +} + +bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessage messages) { auto foundMessageTable = messageTables.find(tableID); if (foundMessageTable == messageTables.end()) { return false; @@ -102,32 +197,28 @@ bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t tex } bool CustomMessageManager::CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, - CustomMessageEntry messageEntry) { - FormatCustomMessage(messageEntry.english, iid); - FormatCustomMessage(messageEntry.german, iid); - FormatCustomMessage(messageEntry.french, iid); + CustomMessage messageEntry) { + messageEntry.Format(iid); const uint16_t textID = giid; return InsertCustomMessage(tableID, textID, messageEntry); } -bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messageEntry) { - FormatCustomMessage(messageEntry.english); - FormatCustomMessage(messageEntry.german); - FormatCustomMessage(messageEntry.french); +bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessage messageEntry) { + messageEntry.Format(); return InsertCustomMessage(tableID, textID, messageEntry); } -CustomMessageEntry CustomMessageManager::RetrieveMessage(std::string tableID, uint16_t textID) { +CustomMessage CustomMessageManager::RetrieveMessage(std::string tableID, uint16_t textID) { std::unordered_map::const_iterator foundMessageTable = messageTables.find(tableID); if (foundMessageTable == messageTables.end()) { - return NULL_CUSTOM_MESSAGE; + throw(MessageNotFoundException(tableID, textID)); } CustomMessageTable messageTable = foundMessageTable->second; - std::unordered_map::const_iterator foundMessage = messageTable.find(textID); + std::unordered_map::const_iterator foundMessage = messageTable.find(textID); if (foundMessage == messageTable.end()) { - return NULL_CUSTOM_MESSAGE; + throw(MessageNotFoundException(tableID, textID)); } - CustomMessageEntry message = foundMessage->second; + CustomMessage message = foundMessage->second; return message; } @@ -145,27 +236,3 @@ bool CustomMessageManager::AddCustomMessageTable(std::string tableID) { CustomMessageTable newMessageTable; return messageTables.emplace(tableID, newMessageTable).second; } - -std::string CustomMessageManager::MESSAGE_END() { - return "\x02"s; -} - -std::string CustomMessageManager::ITEM_OBTAINED(uint8_t x) { - return "\x13"s + char(x); -} - -std::string CustomMessageManager::NEWLINE() { - return "\x01"s; -} - -std::string CustomMessageManager::COLOR(uint8_t x) { - return "\x05"s + char(x); -} - -std::string CustomMessageManager::WAIT_FOR_INPUT() { - return "\x04"s; -} - -std::string CustomMessageManager::PLAYER_NAME() { - return "\x0F"s; -} diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h index 2509b92b2..03ffc5ddb 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageManager.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -2,6 +2,8 @@ #include #include #include +#include + #include "../../../include/z64item.h" #include "../../../include/message_data_textbox_types.h" @@ -16,101 +18,192 @@ #define QM_YELLOW 0x46 #define QM_BLACK 0x47 -typedef struct { - TextBoxType textBoxType; - TextBoxPosition textBoxPos; - std::string english; - std::string german; - std::string french; -} CustomMessageEntry; +/** + * @brief Encapsulates logic surrounding languages, and formatting strings for OoT's textboxes and + * performing variable replacement across all of them at once. Also stores a message's text box type + * (i.e. black, blue, none, typically describes the background but also changes what a few codes mean), + * and position (i.e. top, bottom, middle). + */ +class CustomMessage { + public: + CustomMessage() = default; + CustomMessage(std::string english_, std::string german_, std::string french_, + TextBoxType type_ = TEXTBOX_TYPE_BLACK, TextBoxPosition position_ = TEXTBOX_POS_BOTTOM); -// Message Entry without the text type and position, useful for when -// you need an array of these to loop over for registration -// that will all have the same textbox type and position. -typedef struct { - std::string english; - std::string german; - std::string french; -} CustomMessageMinimal; + const std::string& GetEnglish() const; + const std::string& GetFrench() const; + const std::string& GetGerman() const; + const TextBoxType& GetTextBoxType() const; + const TextBoxPosition& GetTextBoxPosition() const; -#define NULL_CUSTOM_MESSAGE \ - { (TextBoxType)(-1), (TextBoxPosition)(-1), "", "", "" } + 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; + bool operator!=(const CustomMessage& right) const; -typedef std::unordered_map CustomMessageTable; + /** + * @brief Finds an instance of oldStr in each language of the CustomMessage + * and replaces it with newStr. Typically used for dynamic variable replacement + * (i.e. gameplay stats, skulltula count) + * + * @param oldStr the string to be replaced + * @param newStr the string to replace with + */ + void Replace(std::string&& oldStr, std::string&& newStr); + /** + * @brief Finds an instance of oldStr in each language of the CustomMessage, + * and replaces it with the corresponding new string provided for each language. + * 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 + */ + void Replace(std::string&& oldStr, std::string&& newEnglish, std::string&& newGerman, std::string&& newFrench); + + /** + * @brief Capitalizes the first letter of the string for each language. + */ + void Capitalize(); + + /** + * @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(); + + /** + * @brief Replaces our color variable strings with the OoT control codes. + */ + void ReplaceColors(); + + /** + * @brief Replaces various symbols with the control codes necessary to + * display them in OoT's textboxes. i.e. special characters, colors, newlines, + * wait for input, etc. Also adds the item icon to each page of the textbox. + * + * @param iid the ItemID whose icon should be displayed in this message's textbox. + */ + void Format(ItemID iid); + + /** + * @brief Replaces various symbols with the control codes necessary to + * display them in OoT's textboxes. i.e. special characters, colors, newlines, + * wait for input, etc. + */ + void Format(); + + private: + const std::string MESSAGE_END() const; + const std::string ITEM_OBTAINED(uint8_t x) const; + const std::string NEWLINE() const; + const std::string COLOR(uint8_t x) const; + const std::string WAIT_FOR_INPUT() const; + const std::string PLAYER_NAME() const; + + std::string english = ""; + std::string french = ""; + std::string german = ""; + TextBoxType type = TEXTBOX_TYPE_BLACK; + TextBoxPosition position = TEXTBOX_POS_BOTTOM; +}; + +typedef std::unordered_map CustomMessageTable; + +/** + * @brief Encapsulates data and functions for creating custom message tables and storing and retrieving + * `CustomMessage`s from them. It also converts a more user-friendly string syntax to the raw control + * characters that OoT's message system uses (i.e. & for newline, ^ for new page (wait for input), and % + * followed by various letters for colors). + */ class CustomMessageManager { - private: - std::unordered_map textBoxSpecialCharacters; - std::unordered_map colors; + private: std::unordered_map messageTables; - void ReplaceSpecialCharacters(std::string &string); - void ReplaceColors(std::string& string); - bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); - - std::string MESSAGE_END(); - std::string ITEM_OBTAINED(uint8_t x); - std::string NEWLINE(); - std::string COLOR(uint8_t x); - std::string WAIT_FOR_INPUT(); - std::string PLAYER_NAME(); + bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessage message); public: static CustomMessageManager* Instance; - /* - Replaces the specified string in a CustomMessageEntry with the provided value - */ - static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string value); - static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string englishValue, std::string germanValue, std::string frenchValue); + CustomMessageManager() = default; - CustomMessageManager(); - ~CustomMessageManager(); + /** + * @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + * with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to + * the provided iid (itemID) at the beginning of each page of the textbox. + * + * @param tableID the ID of the custom message table + * @param giid the GetItemID of the item (or the textID you want to use to retrieve it later) + * @param iid the ItemID of the item + * @param message the CustomMessage instance being added + * @return true if adding the custom message succeeds, or + * @return false if it does not. + */ + bool CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, CustomMessage message); - /* - Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, - with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to - the provided iid (itemID) at the beginning of each page of the textbox. - */ - bool CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, CustomMessageEntry messages); + /** + * @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + * with the provided textID as its key. + * + * @param tableID the ID of the custom message table + * @param textID the ID to use for later retrieval + * @param message the CustomMessage instance being added + * @return true if adding the custom message succeeds, or + * @return false if it does not. + */ + bool CreateMessage(std::string tableID, uint16_t textID, CustomMessage message); - /* - Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, - with the provided textID as its key. - */ - bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); + /** + * @brief Retrieves a message from the table with id tableID with the provided textID. + * Throws an exception if either the table or the message do not exist. Note: this + * returns a copy of the CustomMessage in the table on purpose, as it is sometimes normal + * to modify it's contents between retrieval and displaying it in game, in order to + * display some dynamic data like gameplay stats. + * + * @param tableID the ID of the custom message table + * @param textID the ID of the message you want to retrieve + * @return CustomMessage + */ + CustomMessage RetrieveMessage(std::string tableID, uint16_t textID); - /* - Retrieves a message from the table with id tableID with the provided textID. - Returns a NULL_CUSTOM_MESSAGE if the message or table does not exist. - */ - CustomMessageEntry RetrieveMessage(std::string tableID, uint16_t textID); - - /* - Empties out the message table identified by tableID. - Returns true if successful and false if not (for instance - if a table with the provided tableID does not exist). - */ + /** + * @brief Empties out the message table identified by tableID. + * + * @param tableID the ID of the table to clear + * @return true if it was cleared successfully, or + * @return false if the table did not exist + */ bool ClearMessageTable(std::string tableID); - /* - Creates an empty CustomMessageTable accessible at the provided - tableID, returns true if creation was successful and false - if not. - */ + /** + * @brief Creates an empty CustomMessageTable accessible at the provided tableID + * + * @param tableID the ID of the table to create + * @return true if the table was created successfully, or + * @return false if not (i.e. because a table with that ID + * already exists.) + */ bool AddCustomMessageTable(std::string tableID); - - /* - Replaces special characters and certain symbols with control codes - & for newline, ^ for wait-for-input, and @ for the player name, - as well as % for colors (i.e. %r for red and %w for white). - */ - void FormatCustomMessage(std::string& message, ItemID iid); - - /* - Replaces special characters and certain symbols with control codes - & for newline, ^ for wait-for-input, and @ for the player name, - as well as % for colors (i.e. %r for red and %w for white). - */ - void FormatCustomMessage(std::string& message); }; + +class MessageNotFoundException : public std::exception { + private: + std::string messageTableId; + uint16_t textId; + + public: + MessageNotFoundException(std::string messageTableId_, uint16_t textId_) + : messageTableId(messageTableId_), textId(textId_) { + } + MessageNotFoundException(std::string&& messageTableId_, uint16_t textId_) + : messageTableId(std::move(messageTableId_)), textId(textId_) { + } + virtual const char* what() const noexcept { + char* message; + sprintf(message, "Message from table %s with textId %u was not found", messageTableId.c_str(), textId); + return message; + } +}; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 6fd58e908..dc03b070e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -382,22 +382,20 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID); CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID); - CustomMessageManager::Instance->CreateMessage( - Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD, - { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.childAltarText, - gSaveContext.childAltarText, gSaveContext.childAltarText }); - CustomMessageManager::Instance->CreateMessage( - Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT, - { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.adultAltarText, - gSaveContext.adultAltarText, gSaveContext.adultAltarText }); + CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD, + CustomMessage(gSaveContext.childAltarText, + gSaveContext.childAltarText, + gSaveContext.childAltarText, TEXTBOX_TYPE_BLUE)); + CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT, + CustomMessage(gSaveContext.adultAltarText, + gSaveContext.adultAltarText, + gSaveContext.adultAltarText, TEXTBOX_TYPE_BLUE)); CustomMessageManager::Instance->CreateMessage( Randomizer::hintMessageTableID, TEXT_GANONDORF, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonHintText, - gSaveContext.ganonHintText, gSaveContext.ganonHintText }); + CustomMessage(gSaveContext.ganonHintText, gSaveContext.ganonHintText, gSaveContext.ganonHintText)); CustomMessageManager::Instance->CreateMessage( Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonText, - gSaveContext.ganonText, gSaveContext.ganonText }); + CustomMessage(gSaveContext.ganonText, gSaveContext.ganonText, gSaveContext.ganonText)); this->childAltarText = gSaveContext.childAltarText; this->adultAltarText = gSaveContext.adultAltarText; @@ -408,7 +406,7 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { if(hintLocation.check == RC_LINKS_POCKET) break; this->hintLocations[hintLocation.check] = hintLocation.hintText; CustomMessageManager::Instance->CreateMessage( - Randomizer::hintMessageTableID, hintLocation.check, { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, hintLocation.hintText, hintLocation.hintText, hintLocation.hintText }); + Randomizer::hintMessageTableID, hintLocation.check, CustomMessage(hintLocation.hintText, hintLocation.hintText, hintLocation.hintText)); } //Extra Hints @@ -417,51 +415,36 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { CustomMessageManager::Instance->CreateMessage( Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "Yeaaarrgh! I'm cursed!!^Please save me by destroying&%r{{params}} Spiders of the Curse%w&and I will give you my&%b{{check}}%w!", + CustomMessage("Yeaaarrgh! I'm cursed!!^Please save me by destroying&%r{{params}} Spiders of the Curse%w&and I will give you my&%b{{check}}%w!", "Yeaaarrgh! Ich bin verflucht!^Bitte rette mich, indem du %r{{params}} Skulltulas&%wzerstörst und ich werde dir dafür&%b{{check}} %wgeben!", - "Yeaaarrgh! Je suis maudit!^Détruit encore %r{{params}} Araignées de&la Malédiction%w et j'aurai quelque&chose à te donner!&%b({{check}})", - } + "Yeaaarrgh! Je suis maudit!^Détruit encore %r{{params}} Araignées de&la Malédiction%w et j'aurai quelque&chose à te donner!&%b({{check}})") ); CustomMessageManager::Instance->CreateMessage( Randomizer::randoMiscHintsTableID, TEXT_DAMPES_DIARY, - { - TEXTBOX_TYPE_BLUE, - TEXTBOX_POS_TOP, + CustomMessage(gSaveContext.dampeText, gSaveContext.dampeText, - gSaveContext.dampeText, - gSaveContext.dampeText - } + gSaveContext.dampeText) ); CustomMessageManager::Instance->CreateMessage( Randomizer::randoMiscHintsTableID, TEXT_CHEST_GAME_PROCEED, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_VARIABLE, + CustomMessage(gSaveContext.gregHintText, gSaveContext.gregHintText, - gSaveContext.gregHintText, - gSaveContext.gregHintText - } + gSaveContext.gregHintText) ); CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "Warp to&{{location}}?\x1B&%gOK&No%w\x02", + CustomMessage("Warp to&{{location}}?\x1B&%gOK&No%w\x02", "Zu {{location}}?\x1B&%gOK&No%w\x02", - "Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02" }); + "Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02")); CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN, - { TEXTBOX_TYPE_WOODEN, TEXTBOX_POS_BOTTOM, - "Water level control system.&Keep away!", + CustomMessage("Water level control system.&Keep away!", "Wasserstand Kontrollsystem&Finger weg!", - "Système de contrôle du niveau&d'eau.&Ne pas toucher!" - }); + "Système de contrôle du niveau&d'eau.&Ne pas toucher!")); CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI, - { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, - "%cThis switch is rustier than you think.^%cSomething must be wrong with the&pipe system in the %bWater Temple%c.", + CustomMessage("%cThis switch is rustier than you think.^%cSomething must be wrong with the&pipe system in the %bWater Temple%c.", "%cDieser Schalter scheint rostiger zu&sein als er aussieht.^%cEtwas muss mit dem Leitungssystem&im %bWassertempel%c nicht stimmen.", - "%cCet interrupteur est très rouillé.^%cIl doit y avoir un problème avec&la tuyauterie du %bTemple de l'Eau%c." - }); + "%cCet interrupteur est très rouillé.^%cIl doit y avoir un problème avec&la tuyauterie du %bTemple de l'Eau%c.")); } // Reference soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h @@ -503,29 +486,21 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) { // 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, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "\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", + 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", - }); + "\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, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "\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", + 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" - }); + "\x12\x38\x82" "J'abandonne! Tu veux bien m'acheter&un %g{{item}}%w?&Ça fera %r{{price}} Rubis%w!\x07\x10\xA3")); CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_BOTTOM, - "I tried to be a %rmagic bean%w salesman,&but it turns out my marketing skills&weren't worth " + CustomMessage("I tried to be a %rmagic bean%w salesman,&but it turns out my marketing skills&weren't worth " "beans!^Anyway, want to buy my&%gmysterious item%w for 60 Rupees?\x1B&%gYes&No%w", "Möchten Sie einen geheimnisvollen&Gegenstand für 60 Rubine?\x1B&%gJa&Nein%w", "J'ai essayé d'être un vendeur de&%rharicots magiques%w, mais j'étais&mauvais au niveau du marketing et&ça " "me courait sur le haricot...^Enfin bref, ça te dirait de m'acheter un&" - "%gobjet mystérieux%w pour 60 Rubis?\x1B&%gOui&Non%w", - }); + "%gobjet mystérieux%w pour 60 Rubis?\x1B&%gOui&Non%w")); //Setup for merchant text boxes @@ -533,25 +508,17 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) { //RANDOTODO: Implement obscure/ambiguous hints CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_MEDIGORON, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_BOTTOM, - "How about buying %r&{{item}}%w for %g200 rupees%w?\x1B&%gYes&No%w", + CustomMessage("How about buying %r&{{item}}%w for %g200 rupees%w?\x1B&%gYes&No%w", "Wie wäre es mit %r&{{item}}%w für %g200 Rubine?%w\x1B&%gJa!&Nein!%w", - "Veux-tu acheter %r&{{item}}%w pour %g200 rubis?%w\x1B&%gOui&Non&w" - }); + "Veux-tu acheter %r&{{item}}%w pour %g200 rubis?%w\x1B&%gOui&Non&w")); //Granny Shopy //RANDOTODO: Implement obscure/ambiguous hints CustomMessageManager::Instance->CreateMessage( Randomizer::merchantMessageTableID, TEXT_GRANNYS_SHOP, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_BOTTOM, - "%r{{item}}%w!&How about %g100 rupees%w?\x1B&%gYes&No%w", + CustomMessage("%r{{item}}%w!&How about %g100 rupees%w?\x1B&%gYes&No%w", "%r{{item}}%w!&Wie wäre es mit %g100 Rubine?%w\x1B&%gJa!&Nein!%w", - "%r{{item}}%w!&Que dis-tu de %g100 rubis?%w\x1B&%gOui&Non&w" - }); + "%r{{item}}%w!&Que dis-tu de %g100 rubis?%w\x1B&%gOui&Non&w")); //Carpet Salesman //RANDOTODO: Implement obscure/ambiguous hints @@ -569,45 +536,36 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) { "!%w&Je ne te dirai pas ce que c'est avant&d'être payé rubis sur l'ongle...^" }; } - CustomMessageManager::Instance->CreateMessage( - Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_BOTTOM, - "Welcome!^I am selling stuff, strange and rare, &from all over the world to everybody.&Today's special is...^%r{{item}}" + cgBoxTwoText[0] + - "How about %g200 Rupees?%w\x1B&&%gYes&No%w", - "Sei gegrüßt!^Ich verkaufe allerlei Kuriorisäten.&Stets sonderliche und seltene Ware&aus aller Welt für jedermann.&Das heutige Angebot bleibt...^%r{{item}}" + - cgBoxTwoText[1] + "Wie wäre es mit %g200 Rubinen?%w\x1B&&%gJa!&Nein!%w", - "Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout le&monde! L'objet du jour est...^%r{{item}}" + - cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w" - } - ); + CustomMessageManager::Instance->CreateMessage( + Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1, + CustomMessage("Welcome!^I am selling stuff, strange and rare, &from all over the world to " + "everybody.&Today's special is...^%r{{item}}" + + cgBoxTwoText[0] + "How about %g200 Rupees?%w\x1B&&%gYes&No%w", + "Sei gegrüßt!^Ich verkaufe allerlei Kuriorisäten.&Stets sonderliche und seltene Ware&aus " + "aller Welt für jedermann.&Das heutige Angebot bleibt...^%r{{item}}" + + cgBoxTwoText[1] + "Wie wäre es mit %g200 Rubinen?%w\x1B&&%gJa!&Nein!%w", + "Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout " + "le&monde! L'objet du jour est...^%r{{item}}" + + cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w")); - CustomMessageManager::Instance->CreateMessage( - Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2, - { - TEXTBOX_TYPE_BLACK, - TEXTBOX_POS_TOP, - "Finally! Now I can go back to being &an %rarms dealer%w!", - "Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!", - "Squalala! Je vais enfin pouvoir &%rprendre des vacances%w!" - } - ); + CustomMessageManager::Instance->CreateMessage( + Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2, + CustomMessage("Finally! Now I can go back to being &an %rarms dealer%w!", + "Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!", + "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, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, - "\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, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, - "\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", - }); + // 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")); } void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) { @@ -4614,52 +4572,52 @@ void DrawRandoEditor(bool& open) { ImGui::End(); } -CustomMessageEntry Randomizer::GetWarpSongMessage(u16 textId, bool mysterious) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage( +CustomMessage Randomizer::GetWarpSongMessage(u16 textId, bool mysterious) { + CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage( Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT); if (mysterious) { - std::vector locationName ={ + std::array locationName ={ "a mysterious place", "ein mysteriöser Ort", "un endroit mystérieux", }; - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{location}}", locationName[0], + messageEntry.Replace("{{location}}", locationName[0], locationName[1], locationName[2]); return messageEntry; } - std::string locationName; + const char* locationName; switch (textId) { case TEXT_WARP_MINUET_OF_FOREST: - locationName = std::string(gSaveContext.warpMinuetText); + locationName = gSaveContext.warpMinuetText; break; case TEXT_WARP_BOLERO_OF_FIRE: - locationName = std::string(gSaveContext.warpBoleroText); + locationName = gSaveContext.warpBoleroText; break; case TEXT_WARP_SERENADE_OF_WATER: - locationName = std::string(gSaveContext.warpSerenadeText); + locationName = gSaveContext.warpSerenadeText; break; case TEXT_WARP_REQUIEM_OF_SPIRIT: - locationName = std::string(gSaveContext.warpRequiemText); + locationName = gSaveContext.warpRequiemText; break; case TEXT_WARP_NOCTURNE_OF_SHADOW: - locationName = std::string(gSaveContext.warpNocturneText); + locationName = gSaveContext.warpNocturneText; break; case TEXT_WARP_PRELUDE_OF_LIGHT: - locationName = std::string(gSaveContext.warpPreludeText); + locationName = gSaveContext.warpPreludeText; break; } - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{location}}", locationName); + messageEntry.Replace("{{location}}", locationName); return messageEntry; } -CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId); +CustomMessage Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) { + CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId); RandomizerCheck rc = GetCheckFromRandomizerInf(randomizerInf); RandomizerGet shopItemGet = this->itemLocations[rc].rgID; - std::vector shopItemName; + std::array shopItemName; if (mysterious) { shopItemName = { "mysterious item", @@ -4688,16 +4646,16 @@ CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u } } - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{item}}", shopItemName[0], shopItemName[1], shopItemName[2]); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{price}}", std::to_string(shopItemPrice)); + messageEntry.Replace("{{item}}", std::move(shopItemName[0]), std::move(shopItemName[1]), std::move(shopItemName[2])); + messageEntry.Replace("{{price}}", std::to_string(shopItemPrice)); return messageEntry; } -CustomMessageEntry Randomizer::GetCursedSkullMessage(s16 params) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE); +CustomMessage Randomizer::GetCursedSkullMessage(s16 params) { + CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE); RandomizerCheck rc = GetCheckFromActor(ACTOR_EN_SSH, SCENE_KINSUTA, params); RandomizerGet itemGet = this->itemLocations[rc].rgID; - std::vector itemName; + std::array itemName; if (itemGet == RG_ICE_TRAP) { itemGet = this->itemLocations[rc].fakeRgID; itemName = { @@ -4709,8 +4667,8 @@ CustomMessageEntry Randomizer::GetCursedSkullMessage(s16 params) { itemName = EnumToSpoilerfileGetName[itemGet]; } - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{params}}", std::to_string(params*10)); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{check}}", itemName[0], itemName[1], itemName[2]); + messageEntry.Replace("{{params}}", std::to_string(params*10)); + messageEntry.Replace("{{check}}", std::move(itemName[0]), std::move(itemName[1]), std::move(itemName[2])); return messageEntry; } @@ -4720,8 +4678,8 @@ static const char* mapGetItemHints[3][2] = { { "&Elle vous semble %rordinaire%w.", "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus." }, }; -CustomMessageEntry Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId); +CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { + CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId); int sceneNum; switch (itemEntry.getItemId) { case RG_DEKU_TREE_MAP: @@ -4757,23 +4715,25 @@ CustomMessageEntry Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEnt } if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) { - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", ""); + messageEntry.Replace("{{typeHint}}", ""); } else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) { - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]); + messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]); } else { - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", mapGetItemHints[0][0], mapGetItemHints[1][0], mapGetItemHints[2][0]); + messageEntry.Replace("{{typeHint}}", mapGetItemHints[0][0], mapGetItemHints[1][0], mapGetItemHints[2][0]); } return messageEntry; } -void CreateGetItemMessages(std::vector messageEntries) { +template +void CreateGetItemMessages(const std::array* messageEntries) { CustomMessageManager* customMessageManager = CustomMessageManager::Instance; customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID); - for (const GetItemMessage& messageEntry : messageEntries) { - customMessageManager->CreateGetItemMessage(Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid, - { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english, - messageEntry.german, messageEntry.french }); + for (const GetItemMessage& messageEntry : *messageEntries) { + customMessageManager->CreateGetItemMessage( + Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid, + CustomMessage(messageEntry.english, messageEntry.german, messageEntry.french, TEXTBOX_TYPE_BLUE, + TEXTBOX_POS_BOTTOM)); } } @@ -4797,299 +4757,314 @@ void CreateRupeeMessages() { rupeeText = "\x05\x06 200 {{rupee}}\x05\x00"; break; } - customMessageManager->CreateMessage(Randomizer::rupeeMessageTableID, rupee, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "You found" + rupeeText + " !", - "Du hast" + rupeeText + " gefunden!", - "Vous obtenez" + rupeeText + " !" - }); + customMessageManager->CreateMessage( + Randomizer::rupeeMessageTableID, rupee, + CustomMessage("You found" + rupeeText + " !", "Du hast" + rupeeText + " gefunden!", + "Vous obtenez" + rupeeText + " !", TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM)); } } -CustomMessageEntry Randomizer::GetRupeeMessage(u16 rupeeTextId) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId); - std::string englishName = RandomElement(englishRupeeNames); - std::string germanName = RandomElement(germanRupeeNames); - std::string frenchName = RandomElement(frenchRupeeNames); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{rupee}}", englishName, germanName, frenchName); +CustomMessage Randomizer::GetRupeeMessage(u16 rupeeTextId) { + CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId); + messageEntry.Replace("{{rupee}}", RandomElement(englishRupeeNames), + RandomElement(germanRupeeNames), RandomElement(frenchRupeeNames)); return messageEntry; } -CustomMessageMinimal NaviMessages[NUM_NAVI_MESSAGES] = { - - { "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!", - "%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!", - "%cIl te manque une %wPetite Clé %cdans&un donjon? C'est peut-être le %rboss&%cqui l'a!" }, - - { "%cSometimes you can use the %rMegaton&Hammer %cinstead of bombs!", - "%cManchmal kannst du den %rStahlhammer&%cstatt Bomben verwenden!", - "%cParfois, tu peux utiliser la %rMasse&des Titans %cau lieu de tes bombes!" }, - - { "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?", - "%cEs gibt drei %gDeku-Händler %cin Hyrule&die mysteriöse Gegenstände&verkaufen. Weißt du wo Sie sind?", - "%cIl y a trois %gPestes Marchandes%c en&Hyrule qui vendent des %wobjets&mystérieux%c. Tu sais où elles sont?" }, - - { "%cStuck on this seed? You could &throw in the towel and check the&%wspoiler log%c...", - "%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...", - "%cSi tu es coincé sur cette seed,&tu peux toujours jeter l'éponge&et regader le %wSpoiler log%c..." }, - - { "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%c?", - "%cWußtest du, daß du mit den&%yGleitstiefeln %cTreibsand %wüberqueren&kannst%c?", - "%cEst-ce que tu savais que les %rBottes&des airs %cpouvaient être utilisées&pour traverser les %wsables mouvants%c?" }, - - { "%cYou can reach the back of %wImpa's&House %cby jumping from the&unfinished house with a %rcucco%c!", - "%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem %rHuhn&%cspringst!", - "%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec une&%rcocotte%c!" }, - - { "%cThe %ySpirit Trial %cin %pGanon's Castle&%chas a %whidden chest %csomewhere.&Did you already know that?", - "%cDie %yGeister-Prüfung %cin %pGanons&Schloß %chat irgendwo eine&%wversteckte Kiste%c. Weißt du schon&wo?", - "%cL'%yÉpreuve de l'Esprit%c dans le %pChâteau&de Ganon %ca un coffre caché quelque&part. Je suppose que tu le savais&déjà?" }, - - { "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is junk!", - "%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der Rest&ist Krimskrams!", - "%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont intéréssant?" }, - - { "%cHave you been using %wDeku Nuts&%cenough? I've seen them blow up&a %rBombchu%c!", - "%cBenutzt du auch genügend %wDeku&Nüsse%c? Ich habe mal gesehen daß&man damit %rKrabbelminen %cdetonieren&kann!", - "%cTu es sûr d'utiliser tes %wNoix Mojo &%ccorrectement? J'en ai déjà vu&exploser des %rChoux-Péteurs%c!" }, - - { "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your safety!", - "%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. Dies&ist zu deiner Sicherheit!", - "%cIl se peut que tu aies une %wPetite&Clé %cen trop dans le %bTemple de l'Eau&%cou le %rTemple du Feu%c. C'est pour ta&propre sécurité!" }, - - { "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with %warrows%c!", - "%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer %coder&%wPfeilen %cdetonieren!", - "%cSi tu ne peux pas ramasser&un %rChoux-Péteur %cavec tes mains, tu&peux toujours le faire exploser&avec du %rFeu %cou avec des %wflèches%c!" }, - - { "%cEven an adult can't push large&blocks without some %wextra&strength%c!", - "%cSelbst ein Erwachsener kann ohne&etwas %wzusätzliche Kraft %ckeine&großen Blöcke verschieben!", - "%cMême un adulte ne peut pas pousser&de grands blocs sans un petit %wgain&de force%c!" }, - - { "%cI've heard that %rFlare Dancer&%cis weak to the %wMaster Sword%c!&Have you tried it?", - "%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es schonmal versucht einzusetzen?", - "%cJ'ai entendu dire que les %rDanse-&Flammes %csont faîbles face à l'%wÉpée de&Légende%c! Est-ce que tu as essayé?" }, - - { "%cDon't have a weapon to kill a&%rspider%c? Try throwing a %wpot&%cat it!", - "%cFehlt dir die Waffe um gegen&eine %rSkulltula %czu kämpfen? Versuch&Sie mit einem %wKrug %cabzuwerfen!", - "%cSi tu n'as pas d'arme pour tuer&une %raraignée%c, pourquoi n'essayerais&-tu pas de lui jetter une %wjarre&%cà la figure?" }, - - { "%cI hear the patch of %wsoft soil&%cin %bZora's River %cis the only one&that isn't home to a %rspider%c!", - "%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula %cbeherbergt.", - "%cJ'ai entendu dire que la %wterre meuble&%cqui se trouve à la %bRivière Zora %cest&la seule qui ne contienne pas&d'%raraignée%c." }, - - { "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a mask%c!", - "%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken trägst%c!", - "%cIl se peut que les habitants d'Hyrule&aient des %wobjets %cpour toi. Par contre,&ils risquent de ne pas trop apprécier&le fait que tu %wportes un masque%c!" }, - - { "%cIf you get trapped somewhere, you&might have to %wsave your game %cand&%wreset%c!", - "%cSolltest du irgendwo eingeschloßen&sein, mußt du vielleicht dein %wSpiel&speichern %cund %wneustarten%c!", - "%cSi tu es coincé quelque part, tu&devrais %wsauvegarder ta partie %cet&faire un %wreset%c!" }, - - { "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater %cMedallions!", - "%cShiek wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund %bWassers&%cbesitzt.", - "%cSheik t'attendra dans un %rvillage&en feu %clorsque tu auras récupéré&les médaillons de la %gForêt%c, du %rFeu&%cet de l'%bEau%c!" }, - - { "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your foes!", - "%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind effektiv gegen&Widersacher!", - "%cSi tu n'as pas d'%wépée %cen tant&qu'enfant, pourquoi n'irais-tu pas&acheter quelques %wBâtons Mojo%c? Ils&sont efficaces contre tes ennemis!" } -}; - void CreateNaviRandoMessages() { + CustomMessage NaviMessages[NUM_NAVI_MESSAGES] = { + + { "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!", + "%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!", + "%cIl te manque une %wPetite Clé %cdans&un donjon? C'est peut-être le %rboss&%cqui l'a!" }, + + { "%cSometimes you can use the %rMegaton&Hammer %cinstead of bombs!", + "%cManchmal kannst du den %rStahlhammer&%cstatt Bomben verwenden!", + "%cParfois, tu peux utiliser la %rMasse&des Titans %cau lieu de tes bombes!" }, + + { "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?", + "%cEs gibt drei %gDeku-Händler %cin Hyrule&die mysteriöse Gegenstände&verkaufen. Weißt du wo Sie sind?", + "%cIl y a trois %gPestes Marchandes%c en&Hyrule qui vendent des %wobjets&mystérieux%c. Tu sais où elles " + "sont?" }, + + { "%cStuck on this seed? You could &throw in the towel and check the&%wspoiler log%c...", + "%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...", + "%cSi tu es coincé sur cette seed,&tu peux toujours jeter l'éponge&et regader le %wSpoiler log%c..." }, + + { "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%c?", + "%cWußtest du, daß du mit den&%yGleitstiefeln %cTreibsand %wüberqueren&kannst%c?", + "%cEst-ce que tu savais que les %rBottes&des airs %cpouvaient être utilisées&pour traverser les %wsables " + "mouvants%c?" }, + + { "%cYou can reach the back of %wImpa's&House %cby jumping from the&unfinished house with a %rcucco%c!", + "%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem " + "%rHuhn&%cspringst!", + "%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec " + "une&%rcocotte%c!" }, + + { "%cThe %ySpirit Trial %cin %pGanon's Castle&%chas a %whidden chest %csomewhere.&Did you already know that?", + "%cDie %yGeister-Prüfung %cin %pGanons&Schloß %chat irgendwo eine&%wversteckte Kiste%c. Weißt du schon&wo?", + "%cL'%yÉpreuve de l'Esprit%c dans le %pChâteau&de Ganon %ca un coffre caché quelque&part. Je suppose que tu " + "le savais&déjà?" }, + + { "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is " + "junk!", + "%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der " + "Rest&ist Krimskrams!", + "%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont " + "intéréssant?" }, + + { "%cHave you been using %wDeku Nuts&%cenough? I've seen them blow up&a %rBombchu%c!", + "%cBenutzt du auch genügend %wDeku&Nüsse%c? Ich habe mal gesehen daß&man damit %rKrabbelminen " + "%cdetonieren&kann!", + "%cTu es sûr d'utiliser tes %wNoix Mojo &%ccorrectement? J'en ai déjà vu&exploser des %rChoux-Péteurs%c!" }, + + { "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your " + "safety!", + "%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. " + "Dies&ist zu deiner Sicherheit!", + "%cIl se peut que tu aies une %wPetite&Clé %cen trop dans le %bTemple de l'Eau&%cou le %rTemple du Feu%c. " + "C'est pour ta&propre sécurité!" }, + + { "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with " + "%warrows%c!", + "%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer " + "%coder&%wPfeilen %cdetonieren!", + "%cSi tu ne peux pas ramasser&un %rChoux-Péteur %cavec tes mains, tu&peux toujours le faire exploser&avec du " + "%rFeu %cou avec des %wflèches%c!" }, + + { "%cEven an adult can't push large&blocks without some %wextra&strength%c!", + "%cSelbst ein Erwachsener kann ohne&etwas %wzusätzliche Kraft %ckeine&großen Blöcke verschieben!", + "%cMême un adulte ne peut pas pousser&de grands blocs sans un petit %wgain&de force%c!" }, + + { "%cI've heard that %rFlare Dancer&%cis weak to the %wMaster Sword%c!&Have you tried it?", + "%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es " + "schonmal versucht einzusetzen?", + "%cJ'ai entendu dire que les %rDanse-&Flammes %csont faîbles face à l'%wÉpée de&Légende%c! Est-ce que tu as " + "essayé?" }, + + { "%cDon't have a weapon to kill a&%rspider%c? Try throwing a %wpot&%cat it!", + "%cFehlt dir die Waffe um gegen&eine %rSkulltula %czu kämpfen? Versuch&Sie mit einem %wKrug %cabzuwerfen!", + "%cSi tu n'as pas d'arme pour tuer&une %raraignée%c, pourquoi n'essayerais&-tu pas de lui jetter une " + "%wjarre&%cà la figure?" }, + + { "%cI hear the patch of %wsoft soil&%cin %bZora's River %cis the only one&that isn't home to a %rspider%c!", + "%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula " + "%cbeherbergt.", + "%cJ'ai entendu dire que la %wterre meuble&%cqui se trouve à la %bRivière Zora %cest&la seule qui ne " + "contienne pas&d'%raraignée%c." }, + + { "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a " + "mask%c!", + "%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken " + "trägst%c!", + "%cIl se peut que les habitants d'Hyrule&aient des %wobjets %cpour toi. Par contre,&ils risquent de ne pas " + "trop apprécier&le fait que tu %wportes un masque%c!" }, + + { "%cIf you get trapped somewhere, you&might have to %wsave your game %cand&%wreset%c!", + "%cSolltest du irgendwo eingeschloßen&sein, mußt du vielleicht dein %wSpiel&speichern %cund %wneustarten%c!", + "%cSi tu es coincé quelque part, tu&devrais %wsauvegarder ta partie %cet&faire un %wreset%c!" }, + + { "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater " + "%cMedallions!", + "%cShiek wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund " + "%bWassers&%cbesitzt.", + "%cSheik t'attendra dans un %rvillage&en feu %clorsque tu auras récupéré&les médaillons de la %gForêt%c, du " + "%rFeu&%cet de l'%bEau%c!" }, + + { "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your " + "foes!", + "%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind " + "effektiv gegen&Widersacher!", + "%cSi tu n'as pas d'%wépée %cen tant&qu'enfant, pourquoi n'irais-tu pas&acheter quelques %wBâtons Mojo%c? " + "Ils&sont efficaces contre tes ennemis!" } + }; CustomMessageManager* customMessageManager = CustomMessageManager::Instance; customMessageManager->AddCustomMessageTable(Randomizer::NaviRandoMessageTableID); for (unsigned int i = 0; i <= (NUM_NAVI_MESSAGES - 1); i++) { - customMessageManager->CreateMessage(Randomizer::NaviRandoMessageTableID, i, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, NaviMessages[i].english, - NaviMessages[i].german, NaviMessages[i].french }); + customMessageManager->CreateMessage( + Randomizer::NaviRandoMessageTableID, i, + NaviMessages[i]); } } -CustomMessageMinimal IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = { - { "You are a %bFOOL%w!", - "Du bist ein %bDUMMKOPF%w!", - "%bPauvre fou%w..." }, - - { "You are a %bFOWL%w!", - "Du bist eine %bFrostbeule%w!", - "Tu es un %bglaçon%w, Harry!" }, - - { "%bFOOL%w!", - "%bDUMMKOPF%w!", - "%bSot%w que tu es." }, - - { "You just got %bPUNKED%w!", - "Du wurdest %beiskalt%w erwischt!", - "Ça me %bglace%w le sang!" }, - - { "Stay %bfrosty%w, @.", - "Es läuft dir %beiskalt%w den Rücken&hinunter, @.", - "%bReste au frais%w, @." }, - - { "Take a %bchill pill%w, @.", - "Bleib %bcool%w, @.", - "Et c'est la douche %bfroide%w!" }, - - { "%bWinter%w is coming.", - "Der %bWinter%w naht.", - "L'%bhiver%w vient." }, - - { "%bICE%w to see you, @.", - "Alles %bcool%w im Pool?", - "%bGlacier%w!" }, - - { "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.", - "%bAbkühlung gefällig%w?", - "%Ça en jette un %bfroid%w." }, - - { "It's a %bcold day%w in the Evil Realm.", - "Es ist ein %kalter%w Tag im Herzen&von Hyrule.", - "Est-ce que tu as déjà eu des sueurs&%bfroides%w?" }, - - { "Getting %bcold feet%w?", - "Bekommst du etwa %bkalte%w Füße?", - "La vengeance est un plat qui se mange&%bfroid%w!" }, - - { "Say hello to the %bZoras%w for me!", - "Sag den %bZoras%w viele Grüße von mir!", - "Dit bonjour aux %bZoras%w pour moi!" }, - - { "Can you keep a %bcool head%w?", - "Bewahre einen %bkühlen%w! Kopf.", - "Il faut parfois savoir garder la tête&%bfroide%w!" }, - - { "Ganondorf used %bIce Trap%w!&It's super effective!", - "Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!", - "Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" }, - - { "Allow me to break the %bice%w!", - "Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!", - "Laisse moi briser la %bglace%w!" }, - - { "%bCold pun%w.", - "%bEiskalt%w lässt du meine Seele&erfrier'n.", - "Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" }, - - { "The %bTitanic%w would be scared of you,&@.", - "Die %bTitanic%w hätte Angst vor dir,&@.", - "Le %bTitanic%w aurait peur de toi,&@." }, - - { "Oh no!", - "Oh nein!", - "Oh non!" }, - - { "What killed the dinosaurs?&The %bICE%w age!", - "Was die Dinosaurier getötet hat?&Die %bEiszeit%w!", - "Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%w!" }, - - { "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.", - "Nachts ist es %bkälter%w als draußen.", - "L'imbécile réfléchit uniquement quand il&s'observe dans la %bglace%w." }, - - { "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.", - "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.", - "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w." }, - - { "Thank you %b@%w!&But your item is in another castle!", - "Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!", - "Merci %b@%w!&Mais ton objet est dans un autre&château!" }, - - { "%bFREEZE%w! Don't move!", - " Kalt. Kalt. Kälter. %bEISKALT%w!", - "J'espère que ça ne te fait ni chaud, ni&%bfroid%w." }, - -}; - void CreateIceTrapRandoMessages() { + CustomMessage IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = { + { "You are a %bFOOL%w!", "Du bist ein %bDUMMKOPF%w!", "%bPauvre fou%w..." }, + + { "You are a %bFOWL%w!", "Du bist eine %bFrostbeule%w!", "Tu es un %bglaçon%w, Harry!" }, + + { "%bFOOL%w!", "%bDUMMKOPF%w!", "%bSot%w que tu es." }, + + { "You just got %bPUNKED%w!", "Du wurdest %beiskalt%w erwischt!", "Ça me %bglace%w le sang!" }, + + { "Stay %bfrosty%w, @.", "Es läuft dir %beiskalt%w den Rücken&hinunter, @.", "%bReste au frais%w, @." }, + + { "Take a %bchill pill%w, @.", "Bleib %bcool%w, @.", "Et c'est la douche %bfroide%w!" }, + + { "%bWinter%w is coming.", "Der %bWinter%w naht.", "L'%bhiver%w vient." }, + + { "%bICE%w to see you, @.", "Alles %bcool%w im Pool?", "%bGlacier%w!" }, + + { "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.", "%bAbkühlung gefällig%w?", + "%Ça en jette un %bfroid%w." }, + + { "It's a %bcold day%w in the Evil Realm.", "Es ist ein %kalter%w Tag im Herzen&von Hyrule.", + "Est-ce que tu as déjà eu des sueurs&%bfroides%w?" }, + + { "Getting %bcold feet%w?", "Bekommst du etwa %bkalte%w Füße?", + "La vengeance est un plat qui se mange&%bfroid%w!" }, + + { "Say hello to the %bZoras%w for me!", "Sag den %bZoras%w viele Grüße von mir!", + "Dit bonjour aux %bZoras%w pour moi!" }, + + { "Can you keep a %bcool head%w?", "Bewahre einen %bkühlen%w! Kopf.", + "Il faut parfois savoir garder la tête&%bfroide%w!" }, + + { "Ganondorf used %bIce Trap%w!&It's super effective!", + "Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!", + "Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" }, + + { "Allow me to break the %bice%w!", "Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!", + "Laisse moi briser la %bglace%w!" }, + + { "%bCold pun%w.", "%bEiskalt%w lässt du meine Seele&erfrier'n.", + "Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" }, + + { "The %bTitanic%w would be scared of you,&@.", "Die %bTitanic%w hätte Angst vor dir,&@.", + "Le %bTitanic%w aurait peur de toi,&@." }, + + { "Oh no!", "Oh nein!", "Oh non!" }, + + { "What killed the dinosaurs?&The %bICE%w age!", "Was die Dinosaurier getötet hat?&Die %bEiszeit%w!", + "Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%w!" }, + + { "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.", + "Nachts ist es %bkälter%w als draußen.", + "L'imbécile réfléchit uniquement quand il&s'observe dans la %bglace%w." }, + + { "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.", + "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.", + "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w." }, + + { "Thank you %b@%w!&But your item is in another castle!", + "Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!", + "Merci %b@%w!&Mais ton objet est dans un autre&château!" }, + + { "%bFREEZE%w! Don't move!", " Kalt. Kalt. Kälter. %bEISKALT%w!", + "J'espère que ça ne te fait ni chaud, ni&%bfroid%w." }, + + }; CustomMessageManager* customMessageManager = CustomMessageManager::Instance; customMessageManager->AddCustomMessageTable(Randomizer::IceTrapRandoMessageTableID); for (u8 i = 0; i <= (NUM_ICE_TRAP_MESSAGES - 1); i++) { customMessageManager->CreateMessage(Randomizer::IceTrapRandoMessageTableID, i, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, IceTrapMessages[i].english, - IceTrapMessages[i].german, IceTrapMessages[i].french }); + IceTrapMessages[i]); } // We only use this ice trap message for christmas, so we don't want it in the normal ice trap messages rotation - customMessageManager->CreateMessage(Randomizer::IceTrapRandoMessageTableID, NUM_ICE_TRAP_MESSAGES + 1, - { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "This year for Christmas, all&you get is %BCOAL%w!", - "This year for Christmas, all&you get is %BCOAL%w!", - "Pour Noël, cette année, tu&n'auras que du %BCHARBON!&%rJoyeux Noël%w!" }); + customMessageManager->CreateMessage( + Randomizer::IceTrapRandoMessageTableID, NUM_ICE_TRAP_MESSAGES + 1, + CustomMessage("This year for Christmas, all&you get is %BCOAL%w!", + "This year for Christmas, all&you get is %BCOAL%w!", + "Pour Noël, cette année, tu&n'auras que du %BCHARBON!&%rJoyeux Noël%w!")); } -CustomMessageMinimal FireTempleGoronMessages[NUM_GORON_MESSAGES] = { - { - "Are you the one they call %g@%w?^You look really weird for %rDarunia's kid.%w&Are you adopted?", - "Du bist also der, den sie @ nennen?^Du siehst nicht aus als wärst du&%rDarunias Kind.%w Bist du adoptiert?", - "C'est toi qu'on appelle %g@%w?^Tu es vraiment bizarre pour être&le %rfils du Chef%w. Tu as été adopté?", - }, - { - "Thank Hylia! I was so worried about&when my teacher would let me get&out of detention.^I gotta go home and see my parents.", - "Ich wollte nur dieses Ding hier wieder&in seine Truhe zurücklegen, weil...^...gehört mir ja eigentlich nicht,&weißt du?^Doch dann ging plötzlich dieses&Tor hinter mir zu.&Danke für die Rettung.", - "Par les déesses!&Mon Frère?!&C'est bien toi?&Comment ça on ne se connaît pas?^Tu trouves vraiment que 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 cake!!", - "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 JOURS!?^En plus il est %r\x1F%w...&Il faut vraiment que je rentre...", - }, - { - //0x39C7 - ganon laugh - "\x12\x39\xC7You fell into my %rtrap!%w&Foolish boy, it was me, Ganondorf!!!^...whoa, where am I?&What happened?^Weird.", - "\x12\x39\xC7""Du bist mir in die %rFalle%w gegangen!&Du Narr, ich bin es, %rGanondorf%w!!!^...Huch? Wo bin ich? Was ist passiert?^Seltsam...", - "\x12\x39\xC7Tu es tombé dans mon %rpiège%w!&Tu croyais que j'étais un Goron mais,&c'était moi! %rGanondorf%w!^...Hein? Où suis-je?&Que s'est-il passé?", - }, - { - "Thanks, but I don't know if I wanna go&just yet...^Hmm...^...^...^...^...^...maybe I can come back later.&Bye bye.", - "Danke für die Rettung, aber&eigentlich finde ich es hier ganz&nett...^Hmm...^...^...^...^...^...Naja, ich kann ja jederzeit&wiederkommen. Man sieht sich.", - "Merci, mais je me sens plus en&sécurité ici...^...^...^...^...^Hmm...^...Tout compte fait, je vais y 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.", - "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...", - "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..." - }, - { - "\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 rupees!", - "\x13\x1A""Hey, ist dir nicht zu warm?&Besorg dir doch eine Flasche&%rLon Lon-Milch%w, um dich&abzukühlen.^Kostet dich auch nur %g30%w Rubine!", - "\x13\x1A""Woah! Tu dois avoir chaud!&Tu savais que tu pouvais acheter&du %rLait de Lon Lon%w pour&seulement %g30 rubis%w?^Il n'y a rien de mieux pour s'hydrater!", - }, - { - "In that case, I'll help you out!^They say that %rthe thing you're&looking for%w can only be found%g when&you're not looking for it.%w^Hope that helps!", - "Pass auf, ich geb dir einen Tipp!^Man sagt, man findet %rdas was&man sucht%w nur, und wirklich nur&dann, %gwenn man gerade nicht danach&sucht%w.^Du kannst mich jederzeit wieder für&mehr hilfreiche Tipps aufsuchen!", - "Dans ce cas, je vais t'aider!&On dit que l'objet que tu cherches&ne peut être trouvé que lorsque&tu ne le cherches pas.", - }, - { - "I dunno why I was thrown in here,&truth be told.&I'm just a %g\"PR\"%w person.", - "Wat weiß'n ich, wieso ich hier&eingepfercht wurd. Ich mach&doch nur %g\"Pull&Requests\"%w.", - "Je ne sais pas comment on m'a jeté&ici. Il faut croire que je dors comme&une pierre.", - }, -}; - -static int goronIDs[9] = {0x3052, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070}; +static int goronIDs[9] = { 0x3052, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070 }; void CreateFireTempleGoronMessages() { - + CustomMessage FireTempleGoronMessages[NUM_GORON_MESSAGES] = { + { + "Are you the one they call %g@%w?^You look really weird for %rDarunia's kid.%w&Are you adopted?", + "Du bist also der, den sie @ nennen?^Du siehst nicht aus als wärst du&%rDarunias Kind.%w Bist du " + "adoptiert?", + "C'est toi qu'on appelle %g@%w?^Tu es vraiment bizarre pour être&le %rfils du Chef%w. Tu as été adopté?", + }, + { + "Thank Hylia! I was so worried about&when my teacher would let me get&out of detention.^I gotta go home " + "and see my parents.", + "Ich wollte nur dieses Ding hier wieder&in seine Truhe zurücklegen, weil...^...gehört mir ja eigentlich " + "nicht,&weißt du?^Doch dann ging plötzlich dieses&Tor hinter mir zu.&Danke für die Rettung.", + "Par les déesses!&Mon Frère?!&C'est bien toi?&Comment ça on ne se connaît pas?^Tu trouves vraiment que " + "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 " + "cake!!", + "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 " + "JOURS!?^En plus il est %r\x1F%w...&Il faut vraiment que je rentre...", + }, + { + // 0x39C7 - ganon laugh + "\x12\x39\xC7You fell into my %rtrap!%w&Foolish boy, it was me, Ganondorf!!!^...whoa, where am I?&What " + "happened?^Weird.", + "\x12\x39\xC7" + "Du bist mir in die %rFalle%w gegangen!&Du Narr, ich bin es, %rGanondorf%w!!!^...Huch? Wo bin ich? Was ist " + "passiert?^Seltsam...", + "\x12\x39\xC7Tu es tombé dans mon %rpiège%w!&Tu croyais que j'étais un Goron mais,&c'était moi! " + "%rGanondorf%w!^...Hein? Où suis-je?&Que s'est-il passé?", + }, + { + "Thanks, but I don't know if I wanna go&just yet...^Hmm...^...^...^...^...^...maybe I can come back " + "later.&Bye bye.", + "Danke für die Rettung, aber&eigentlich finde ich es hier ganz&nett...^Hmm...^...^...^...^...^...Naja, ich " + "kann ja jederzeit&wiederkommen. Man sieht sich.", + "Merci, mais je me sens plus en&sécurité ici...^...^...^...^...^Hmm...^...Tout compte fait, je vais y " + "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.", + "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...", + "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..." }, + { + "\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 " + "rupees!", + "\x13\x1A" + "Hey, ist dir nicht zu warm?&Besorg dir doch eine Flasche&%rLon Lon-Milch%w, um dich&abzukühlen.^Kostet " + "dich auch nur %g30%w Rubine!", + "\x13\x1A" + "Woah! Tu dois avoir chaud!&Tu savais que tu pouvais acheter&du %rLait de Lon Lon%w pour&seulement %g30 " + "rubis%w?^Il n'y a rien de mieux pour s'hydrater!", + }, + { + "In that case, I'll help you out!^They say that %rthe thing you're&looking for%w can only be found%g " + "when&you're not looking for it.%w^Hope that helps!", + "Pass auf, ich geb dir einen Tipp!^Man sagt, man findet %rdas was&man sucht%w nur, und wirklich nur&dann, " + "%gwenn man gerade nicht danach&sucht%w.^Du kannst mich jederzeit wieder für&mehr hilfreiche Tipps " + "aufsuchen!", + "Dans ce cas, je vais t'aider!&On dit que l'objet que tu cherches&ne peut être trouvé que lorsque&tu ne le " + "cherches pas.", + }, + { + "I dunno why I was thrown in here,&truth be told.&I'm just a %g\"PR\"%w person.", + "Wat weiß'n ich, wieso ich hier&eingepfercht wurd. Ich mach&doch nur %g\"Pull&Requests\"%w.", + "Je ne sais pas comment on m'a jeté&ici. Il faut croire que je dors comme&une pierre.", + }, + }; + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; customMessageManager->AddCustomMessageTable(customMessageTableID); for (u8 i = 0; i <= NUM_GORON_MESSAGES - 1; i++) { - customMessageManager->CreateMessage(customMessageTableID, goronIDs[i], { - TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - FireTempleGoronMessages[i].english, FireTempleGoronMessages[i].german, FireTempleGoronMessages[i].french - }); + customMessageManager->CreateMessage(customMessageTableID, goronIDs[i], FireTempleGoronMessages[i]); } } -CustomMessageEntry Randomizer::GetGoronMessage(u16 index) { - CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, goronIDs[index]); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{days}}", std::to_string(gSaveContext.totalDays)); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{a_btn}}", std::to_string(gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A])); +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])); return messageEntry; } void Randomizer::CreateCustomMessages() { // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED // with GIMESSAGE(getItemID, itemID, english, german, french). - const std::vector getItemMessages = { + const std::array getItemMessages = {{ GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON, "You found %gGreg%w!", "%gGreg%w! Du hast ihn wirklich gefunden!", @@ -5320,8 +5295,8 @@ void Randomizer::CreateCustomMessages() { "You got a %rTycoon's Wallet%w!&It's gigantic! Now you can carry&up to %y999 rupees%w!", "Du erhältst die %rGoldene&Geldbörse%w! Die größte aller&Geldbörsen! Jetzt kannst Du bis&zu %y999 Rubine%w mit dir führen!", "Vous obtenez la %rBourse de Magnat%w!&Elle peut contenir jusqu'à %y999 rubis%w!&C'est gigantesque!") - }; - CreateGetItemMessages(getItemMessages); + }}; + CreateGetItemMessages(&getItemMessages); CreateRupeeMessages(); CreateNaviRandoMessages(); CreateIceTrapRandoMessages(); diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index b6da344e7..4eb5563ea 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -53,7 +53,7 @@ class Randomizer { std::unordered_map trialsRequired; std::unordered_set masterQuestDungeons; std::unordered_map merchantPrices; - std::unordered_map> EnumToSpoilerfileGetName; + std::unordered_map> EnumToSpoilerfileGetName; static Sprite* GetSeedTexture(uint8_t index); s16 GetItemModelFromId(s16 itemId); @@ -87,13 +87,13 @@ class Randomizer { GetItemID GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId); ItemObtainability GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck); ItemObtainability GetItemObtainabilityFromRandomizerGet(RandomizerGet randomizerCheck); - CustomMessageEntry GetWarpSongMessage(u16 textId, bool mysterious = false); - CustomMessageEntry GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious = false); - CustomMessageEntry GetCursedSkullMessage(s16 params); - CustomMessageEntry GetGoronMessage(u16 index); - CustomMessageEntry GetMapGetItemMessageWithHint(GetItemEntry itemEntry); + CustomMessage GetWarpSongMessage(u16 textId, bool mysterious = false); + CustomMessage GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious = false); + CustomMessage GetCursedSkullMessage(s16 params); + CustomMessage GetGoronMessage(u16 index); + CustomMessage GetMapGetItemMessageWithHint(GetItemEntry itemEntry); static void CreateCustomMessages(); - static CustomMessageEntry GetRupeeMessage(u16 rupeeTextId); + static CustomMessage GetRupeeMessage(u16 rupeeTextId); bool CheckContainsVanillaItem(RandomizerCheck randoCheck); }; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index d192de6aa..1082c35ab 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1677,22 +1677,43 @@ extern "C" void* getN64WeirdFrame(s32 i) { } extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSize) { + CustomMessage customMessage("\x04\x1A\x08" + "D\x96sirez-vous l'\x96quiper maintenant?" + "\x09&&" + "\x1B%g" + "Oui" + "&" + "Non" + "%w\x02", + "\x04\x1A\x08" + "M" + "\x9A" + "chtest Du es jetzt ausr\x9Esten?" + "\x09&&" + "\x1B%g" + "Ja!" + "&" + "Nein!" + "%w\x02", + "\x04\x1A\x08" + "Would you like to equip it now?" + "\x09&&" + "\x1B%g" + "Yes" + "&" + "No" + "%w\x02"); + customMessage.Format(); + std::string postfix; if (gSaveContext.language == LANGUAGE_FRA) { - postfix = "\x04\x1A\x08" "D\x96sirez-vous l'\x96quiper maintenant?" "\x09&&" - "\x1B%g" "Oui" "&" - "Non" "%w\x02"; + postfix = customMessage.GetFrench(); } else if (gSaveContext.language == LANGUAGE_GER) { - postfix = "\x04\x1A\x08" "M""\x9A""chtest Du es jetzt ausr\x9Esten?" "\x09&&" - "\x1B%g" "Ja!" "&" - "Nein!" "%w\x02"; + postfix = customMessage.GetGerman(); } else { - postfix = "\x04\x1A\x08" "Would you like to equip it now?" "\x09&&" - "\x1B%g" "Yes" "&" - "No" "%w\x02"; + postfix = customMessage.GetEnglish(); } - CustomMessageManager::Instance->FormatCustomMessage(postfix); std::string str; std::string FixedBaseStr(src); int RemoveControlChar = FixedBaseStr.find_first_of("\x02"); @@ -1796,14 +1817,14 @@ extern "C" ItemObtainability Randomizer_GetItemObtainabilityFromRandomizerCheck( return OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerCheck(randomizerCheck); } -extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(Player* player) { +CustomMessage Randomizer_GetCustomGetItemMessage(Player* player) { s16 giid; if (player->getItemEntry.objectId != OBJECT_INVALID) { giid = player->getItemEntry.getItemId; } else { giid = player->getItemId; } - const CustomMessageEntry getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid); + const CustomMessage getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid); return getItemText; } @@ -1813,7 +1834,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { Font* font = &msgCtx->font; char* buffer = font->msgBuf; const int maxBufferSize = sizeof(font->msgBuf); - CustomMessageEntry messageEntry; + CustomMessage messageEntry; s16 actorParams = 0; if (gSaveContext.n64ddFlag) { if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) { @@ -1952,16 +1973,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 + (gSaveContext.n64ddFlag ? 1 : 0); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{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); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{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); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{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_ENTRA_N) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT); @@ -1969,21 +1990,19 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { if (textId == TEXT_RANDO_SAVE_VERSION_WARNING) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING); } - if (messageEntry.textBoxType != -1) { - font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos; + font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition(); switch (gSaveContext.language) { case LANGUAGE_FRA: return msgCtx->msgLength = font->msgLength = - CopyStringToCharBuffer(messageEntry.french, buffer, maxBufferSize); + CopyStringToCharBuffer(messageEntry.GetFrench(), buffer, maxBufferSize); case LANGUAGE_GER: return msgCtx->msgLength = font->msgLength = - CopyStringToCharBuffer(messageEntry.german, buffer, maxBufferSize); + CopyStringToCharBuffer(messageEntry.GetGerman(), buffer, maxBufferSize); case LANGUAGE_ENG: default: return msgCtx->msgLength = font->msgLength = - CopyStringToCharBuffer(messageEntry.english, buffer, maxBufferSize); + CopyStringToCharBuffer(messageEntry.GetEnglish(), buffer, maxBufferSize); } - } return false; } diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp index a11befaa0..317508be5 100644 --- a/soh/soh/z_message_OTR.cpp +++ b/soh/soh/z_message_OTR.cpp @@ -109,74 +109,57 @@ extern "C" void OTRMessage_Init() CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID); CustomMessageManager::Instance->CreateGetItemMessage( customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN, - { - TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, - "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 tout!\x0E\x3C" - } - ); + 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 " + "tout!\x0E\x3C", + TEXTBOX_TYPE_BLUE)); CustomMessageManager::Instance->CreateGetItemMessage( customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN, - { - TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, - "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!" - } - ); + 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 " + "Skulltula-Symbole&gesammelt!", + "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, - { - TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "\x08%rBombchu (10 pieces) 99 Rupees&%wThis looks like a toy mouse, but&it's actually a self-propelled time&bomb!\x09\x0A", - "\x08%rKrabbelmine 10 Stück 99 Rubine&%wDas ist eine praktische Zeitbombe,&die Du als Distanzwaffe&einsetzen kannst!\x09\x0A", - "\x08%rMissile 10 unités 99 Rubis&%wProfilée comme une souris&mécanique, cette arme est &destructrice!!!\x09\x0A", - } - ); + CustomMessage("\x08%rBombchu (10 pieces) 99 Rupees&%wThis looks like a toy mouse, but&it's actually a " + "self-propelled time&bomb!\x09\x0A", + "\x08%rKrabbelmine 10 Stück 99 Rubine&%wDas ist eine praktische Zeitbombe,&die Du als " + "Distanzwaffe&einsetzen kannst!\x09\x0A", + "\x08%rMissile 10 unités 99 Rubis&%wProfilée comme une souris&mécanique, cette arme est " + "&destructrice!!!\x09\x0A")); CustomMessageManager::Instance->CreateMessage( customMessageTableID, TEXT_BUY_BOMBCHU_10_PROMPT, - { - TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "\x08" "Bombchu 10 pieces 99 Rupees\x09&&\x1B%gBuy&Don't buy%w", - "\x08Krabbelmine 10 Stück 99 Rubine\x09&&\x1B%gKaufen!&Nicht kaufen!%w", - "\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w", - } - ); + CustomMessage("\x08" + "Bombchu 10 pieces 99 Rupees\x09&&\x1B%gBuy&Don't buy%w", + "\x08Krabbelmine 10 Stück 99 Rubine\x09&&\x1B%gKaufen!&Nicht kaufen!%w", + "\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w")); CustomMessageManager::Instance->CreateGetItemMessage( customMessageTableID, (GetItemID)TEXT_HEART_CONTAINER, ITEM_HEART_CONTAINER, - { - TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + 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!" - } - ); + "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, - { - TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, - "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, - { - TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, - "You look bored. Wanna go out for a&walk?\x1B&%gYes&No%w", - "Du siehst gelangweilt aus.&Willst du einen Spaziergang machen?\x1B&%gJa&Nein%w", - "Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w", - } - ); + CustomMessage("You look bored. Wanna go out for a&walk?\x1B&%gYes&No%w", + "Du siehst gelangweilt aus.&Willst du einen Spaziergang machen?\x1B&%gJa&Nein%w", + "Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w")); CustomMessageManager::Instance->CreateMessage( customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING, - { - TEXTBOX_TYPE_NONE_BOTTOM, TEXTBOX_POS_BOTTOM, + CustomMessage( "This save was created on&a different version of SoH.&&Things may be broken.", "Dieser Spielstand wurde auf einer&anderen Version von SoH erstellt.&&Es könnten Fehler auftreten.", - "Cette sauvegarde a été créée sur&une version différente de SoH.&Certaines fonctionnalités&peuvent être corrompues.", - } - ); + "Cette sauvegarde a été créée sur&une version différente de SoH.&Certaines fonctionnalités&peuvent être " + "corrompues.", + TEXTBOX_TYPE_NONE_BOTTOM)); }