Merge branch 'HarbourMasters:develop' into sectional-saves

This commit is contained in:
Malkierian 2023-05-19 14:56:19 -07:00 committed by GitHub
commit 8d0b83b691
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 792 additions and 655 deletions

View File

@ -4,94 +4,189 @@
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
CustomMessageManager::CustomMessageManager() { static const std::unordered_map<std::string, char> textBoxSpecialCharacters = {
this->textBoxSpecialCharacters = { { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, { "È", 0x85 }, { "É", 0x86 },
{ "È", 0x85 }, { "É", 0x86 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, { "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D },
{ "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, { "Ü", 0x8E }, { "Ü", 0x8E }, { "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, { "ç", 0x94 },
{ "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B },
{ "ç", 0x94 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, { "ù", 0x9C }, { "û", 0x9D }, { "ü", 0x9E }
{ "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, { "ù", 0x9C }, { "û", 0x9D }, };
{ "ü", 0x9E } }; static const std::unordered_map<std::string, char> colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN },
this->colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN }, { "b", QM_BLUE }, { "b", QM_BLUE }, { "c", QM_LBLUE }, { "p", QM_PINK },
{ "c", QM_LBLUE }, { "p", QM_PINK }, { "y", QM_YELLOW }, { "B", QM_BLACK } }; { "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() { const std::string& CustomMessage::GetEnglish() const {
this->textBoxSpecialCharacters.clear(); return english;
this->colors.clear();
this->messageTables.clear();
} }
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 // add special characters
for (auto specialCharacterPair : this->textBoxSpecialCharacters) { for (std::string* str : { &english, &french, &german }) {
for (auto specialCharacterPair : textBoxSpecialCharacters) {
size_t start_pos = 0; size_t start_pos = 0;
std::string textBoxSpecialCharacterString = ""s; std::string textBoxSpecialCharacterString = ""s;
textBoxSpecialCharacterString += specialCharacterPair.second; textBoxSpecialCharacterString += specialCharacterPair.second;
while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) { while ((start_pos = str->find(specialCharacterPair.first, start_pos)) != std::string::npos) {
string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); str->replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length(); start_pos += textBoxSpecialCharacterString.length();
} }
} }
}
} }
void CustomMessageManager::ReplaceColors(std::string& string) { void CustomMessage::ReplaceColors() {
for (std::string* str : { &english, &french, &german }) {
for (auto colorPair : colors) { for (auto colorPair : colors) {
std::string textToReplace = "%"; std::string textToReplace = "%";
textToReplace += colorPair.first; textToReplace += colorPair.first;
size_t start_pos = 0; size_t start_pos = 0;
while ((start_pos = string.find(textToReplace)) != std::string::npos) { while ((start_pos = str->find(textToReplace, start_pos)) != std::string::npos) {
string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second)); str->replace(start_pos, textToReplace.length(), COLOR(colorPair.second));
start_pos += textToReplace.length(); 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);
} }
void CustomMessageManager::ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string value) { const std::string CustomMessage::MESSAGE_END() const {
ReplaceString(messageEntry.english, textToReplace, value); return "\x02"s;
ReplaceString(messageEntry.german, textToReplace, value);
ReplaceString(messageEntry.french, textToReplace, value);
} }
void CustomMessageManager::ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string englishValue, std::string germanValue, std::string frenchValue) { const std::string CustomMessage::ITEM_OBTAINED(uint8_t x) const {
ReplaceString(messageEntry.english, textToReplace, englishValue); return "\x13"s + char(x);
ReplaceString(messageEntry.german, textToReplace, germanValue);
ReplaceString(messageEntry.french, textToReplace, frenchValue);
} }
void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) { const std::string CustomMessage::NEWLINE() const {
message.insert(0, ITEM_OBTAINED(iid)); return "\x01"s;
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();
} }
void CustomMessageManager::FormatCustomMessage(std::string& message) { const std::string CustomMessage::COLOR(uint8_t x) const {
size_t start_pos = 0; return "\x05"s + char(x);
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();
} }
bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages) { const std::string CustomMessage::WAIT_FOR_INPUT() const {
return "\x04"s;
}
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); auto foundMessageTable = messageTables.find(tableID);
if (foundMessageTable == messageTables.end()) { if (foundMessageTable == messageTables.end()) {
return false; 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, bool CustomMessageManager::CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid,
CustomMessageEntry messageEntry) { CustomMessage messageEntry) {
FormatCustomMessage(messageEntry.english, iid); messageEntry.Format(iid);
FormatCustomMessage(messageEntry.german, iid);
FormatCustomMessage(messageEntry.french, iid);
const uint16_t textID = giid; const uint16_t textID = giid;
return InsertCustomMessage(tableID, textID, messageEntry); return InsertCustomMessage(tableID, textID, messageEntry);
} }
bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messageEntry) { bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessage messageEntry) {
FormatCustomMessage(messageEntry.english); messageEntry.Format();
FormatCustomMessage(messageEntry.german);
FormatCustomMessage(messageEntry.french);
return InsertCustomMessage(tableID, textID, messageEntry); 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<std::string, CustomMessageTable>::const_iterator foundMessageTable = messageTables.find(tableID); std::unordered_map<std::string, CustomMessageTable>::const_iterator foundMessageTable = messageTables.find(tableID);
if (foundMessageTable == messageTables.end()) { if (foundMessageTable == messageTables.end()) {
return NULL_CUSTOM_MESSAGE; throw(MessageNotFoundException(tableID, textID));
} }
CustomMessageTable messageTable = foundMessageTable->second; CustomMessageTable messageTable = foundMessageTable->second;
std::unordered_map<uint16_t, CustomMessageEntry>::const_iterator foundMessage = messageTable.find(textID); std::unordered_map<uint16_t, CustomMessage>::const_iterator foundMessage = messageTable.find(textID);
if (foundMessage == messageTable.end()) { if (foundMessage == messageTable.end()) {
return NULL_CUSTOM_MESSAGE; throw(MessageNotFoundException(tableID, textID));
} }
CustomMessageEntry message = foundMessage->second; CustomMessage message = foundMessage->second;
return message; return message;
} }
@ -145,27 +236,3 @@ bool CustomMessageManager::AddCustomMessageTable(std::string tableID) {
CustomMessageTable newMessageTable; CustomMessageTable newMessageTable;
return messageTables.emplace(tableID, newMessageTable).second; 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;
}

View File

@ -2,6 +2,8 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <cstdint> #include <cstdint>
#include <exception>
#include "../../../include/z64item.h" #include "../../../include/z64item.h"
#include "../../../include/message_data_textbox_types.h" #include "../../../include/message_data_textbox_types.h"
@ -16,101 +18,192 @@
#define QM_YELLOW 0x46 #define QM_YELLOW 0x46
#define QM_BLACK 0x47 #define QM_BLACK 0x47
typedef struct { /**
TextBoxType textBoxType; * @brief Encapsulates logic surrounding languages, and formatting strings for OoT's textboxes and
TextBoxPosition textBoxPos; * performing variable replacement across all of them at once. Also stores a message's text box type
std::string english; * (i.e. black, blue, none, typically describes the background but also changes what a few codes mean),
std::string german; * and position (i.e. top, bottom, middle).
std::string french; */
} CustomMessageEntry; 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 const std::string& GetEnglish() const;
// you need an array of these to loop over for registration const std::string& GetFrench() const;
// that will all have the same textbox type and position. const std::string& GetGerman() const;
typedef struct { const TextBoxType& GetTextBoxType() const;
std::string english; const TextBoxPosition& GetTextBoxPosition() const;
std::string german;
std::string french;
} CustomMessageMinimal;
#define NULL_CUSTOM_MESSAGE \ CustomMessage operator+(const CustomMessage& right) const;
{ (TextBoxType)(-1), (TextBoxPosition)(-1), "", "", "" } 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<uint16_t, CustomMessageEntry> 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<uint16_t, CustomMessage> 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 { class CustomMessageManager {
private: private:
std::unordered_map<std::string, char> textBoxSpecialCharacters;
std::unordered_map<std::string, char> colors;
std::unordered_map<std::string, CustomMessageTable> messageTables; std::unordered_map<std::string, CustomMessageTable> messageTables;
void ReplaceSpecialCharacters(std::string &string); bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessage message);
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();
public: public:
static CustomMessageManager* Instance; static CustomMessageManager* Instance;
/* CustomMessageManager() = default;
Replaces the specified string in a CustomMessageEntry with the provided value
/**
* @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.
*/ */
static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string value); bool CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, CustomMessage message);
static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string englishValue, std::string germanValue, std::string frenchValue);
CustomMessageManager(); /**
~CustomMessageManager(); * @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
* with the provided textID as its key.
/* *
Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, * @param tableID the ID of the custom message table
with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to * @param textID the ID to use for later retrieval
the provided iid (itemID) at the beginning of each page of the textbox. * @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, CustomMessageEntry messages); 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, * @brief Retrieves a message from the table with id tableID with the provided textID.
with the provided textID as its key. * 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
*/ */
bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); CustomMessage RetrieveMessage(std::string tableID, uint16_t textID);
/* /**
Retrieves a message from the table with id tableID with the provided textID. * @brief Empties out the message table identified by tableID.
Returns a NULL_CUSTOM_MESSAGE if the message or table does not exist. *
*/ * @param tableID the ID of the table to clear
CustomMessageEntry RetrieveMessage(std::string tableID, uint16_t textID); * @return true if it was cleared successfully, or
* @return false if the table did not exist
/*
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).
*/ */
bool ClearMessageTable(std::string tableID); bool ClearMessageTable(std::string tableID);
/* /**
Creates an empty CustomMessageTable accessible at the provided * @brief Creates an empty CustomMessageTable accessible at the provided tableID
tableID, returns true if creation was successful and false *
if not. * @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); bool AddCustomMessageTable(std::string tableID);
};
/*
Replaces special characters and certain symbols with control codes class MessageNotFoundException : public std::exception {
& for newline, ^ for wait-for-input, and @ for the player name, private:
as well as %<letter> for colors (i.e. %r for red and %w for white). std::string messageTableId;
*/ uint16_t textId;
void FormatCustomMessage(std::string& message, ItemID iid);
public:
/* MessageNotFoundException(std::string messageTableId_, uint16_t textId_)
Replaces special characters and certain symbols with control codes : messageTableId(messageTableId_), textId(textId_) {
& for newline, ^ for wait-for-input, and @ for the player name, }
as well as %<letter> for colors (i.e. %r for red and %w for white). MessageNotFoundException(std::string&& messageTableId_, uint16_t textId_)
*/ : messageTableId(std::move(messageTableId_)), textId(textId_) {
void FormatCustomMessage(std::string& message); }
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;
}
}; };

View File

@ -382,22 +382,20 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID); CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID); CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD,
Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD, CustomMessage(gSaveContext.childAltarText,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.childAltarText, gSaveContext.childAltarText,
gSaveContext.childAltarText, gSaveContext.childAltarText }); gSaveContext.childAltarText, TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT,
Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT, CustomMessage(gSaveContext.adultAltarText,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.adultAltarText, gSaveContext.adultAltarText,
gSaveContext.adultAltarText, gSaveContext.adultAltarText }); gSaveContext.adultAltarText, TEXTBOX_TYPE_BLUE));
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF, Randomizer::hintMessageTableID, TEXT_GANONDORF,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonHintText, CustomMessage(gSaveContext.ganonHintText, gSaveContext.ganonHintText, gSaveContext.ganonHintText));
gSaveContext.ganonHintText, gSaveContext.ganonHintText });
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT, Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonText, CustomMessage(gSaveContext.ganonText, gSaveContext.ganonText, gSaveContext.ganonText));
gSaveContext.ganonText, gSaveContext.ganonText });
this->childAltarText = gSaveContext.childAltarText; this->childAltarText = gSaveContext.childAltarText;
this->adultAltarText = gSaveContext.adultAltarText; this->adultAltarText = gSaveContext.adultAltarText;
@ -408,7 +406,7 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
if(hintLocation.check == RC_LINKS_POCKET) break; if(hintLocation.check == RC_LINKS_POCKET) break;
this->hintLocations[hintLocation.check] = hintLocation.hintText; this->hintLocations[hintLocation.check] = hintLocation.hintText;
CustomMessageManager::Instance->CreateMessage( 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 //Extra Hints
@ -417,51 +415,36 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE, Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, 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! 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! 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( CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_DAMPES_DIARY, Randomizer::randoMiscHintsTableID, TEXT_DAMPES_DIARY,
{ CustomMessage(gSaveContext.dampeText,
TEXTBOX_TYPE_BLUE,
TEXTBOX_POS_TOP,
gSaveContext.dampeText, gSaveContext.dampeText,
gSaveContext.dampeText, gSaveContext.dampeText)
gSaveContext.dampeText
}
); );
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::randoMiscHintsTableID, TEXT_CHEST_GAME_PROCEED, Randomizer::randoMiscHintsTableID, TEXT_CHEST_GAME_PROCEED,
{ CustomMessage(gSaveContext.gregHintText,
TEXTBOX_TYPE_BLACK,
TEXTBOX_POS_VARIABLE,
gSaveContext.gregHintText, gSaveContext.gregHintText,
gSaveContext.gregHintText, gSaveContext.gregHintText)
gSaveContext.gregHintText
}
); );
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT, CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, CustomMessage("Warp to&{{location}}?\x1B&%gOK&No%w\x02",
"Warp to&{{location}}?\x1B&%gOK&No%w\x02",
"Zu {{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, CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
{ TEXTBOX_TYPE_WOODEN, TEXTBOX_POS_BOTTOM, CustomMessage("Water level control system.&Keep away!",
"Water level control system.&Keep away!",
"Wasserstand Kontrollsystem&Finger weg!", "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, CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, CustomMessage("%cThis switch is rustier than you think.^%cSomething must be wrong with the&pipe system in the %bWater Temple%c.",
"%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.", "%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 // 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 // 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, CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, 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" "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" "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, CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, 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" "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" "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( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN, Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN,
{ CustomMessage("I tried to be a %rmagic bean%w salesman,&but it turns out my marketing skills&weren't worth "
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 "
"beans!^Anyway, want to buy my&%gmysterious item%w for 60 Rupees?\x1B&%gYes&No%w", "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", "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 " "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&" "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 //Setup for merchant text boxes
@ -533,25 +508,17 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
//RANDOTODO: Implement obscure/ambiguous hints //RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_MEDIGORON, Randomizer::merchantMessageTableID, TEXT_MEDIGORON,
{ CustomMessage("How about buying %r&{{item}}%w for %g200 rupees%w?\x1B&%gYes&No%w",
TEXTBOX_TYPE_BLACK,
TEXTBOX_POS_BOTTOM,
"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", "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 //Granny Shopy
//RANDOTODO: Implement obscure/ambiguous hints //RANDOTODO: Implement obscure/ambiguous hints
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_GRANNYS_SHOP, Randomizer::merchantMessageTableID, TEXT_GRANNYS_SHOP,
{ CustomMessage("%r{{item}}%w!&How about %g100 rupees%w?\x1B&%gYes&No%w",
TEXTBOX_TYPE_BLACK,
TEXTBOX_POS_BOTTOM,
"%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!&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 //Carpet Salesman
//RANDOTODO: Implement obscure/ambiguous hints //RANDOTODO: Implement obscure/ambiguous hints
@ -571,43 +538,34 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
} }
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1, Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1,
{ CustomMessage("Welcome!^I am selling stuff, strange and rare, &from all over the world to "
TEXTBOX_TYPE_BLACK, "everybody.&Today's special is...^%r{{item}}" +
TEXTBOX_POS_BOTTOM, cgBoxTwoText[0] + "How about %g200 Rupees?%w\x1B&&%gYes&No%w",
"Welcome!^I am selling stuff, strange and rare, &from all over the world to everybody.&Today's special is...^%r{{item}}" + cgBoxTwoText[0] + "Sei gegrüßt!^Ich verkaufe allerlei Kuriorisäten.&Stets sonderliche und seltene Ware&aus "
"How about %g200 Rupees?%w\x1B&&%gYes&No%w", "aller Welt für jedermann.&Das heutige Angebot bleibt...^%r{{item}}" +
"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", 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}}" + "Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout "
cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w" "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( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2, Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2,
{ CustomMessage("Finally! Now I can go back to being &an %rarms dealer%w!",
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!", "Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!",
"Squalala! Je vais enfin pouvoir &%rprendre des vacances%w!" "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 // 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 // prompted buy/don't buy
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM,
"\x08%r{{item}} {{price}} Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02", 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}} 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", "\x08%r{{item}} {{price}} Rubis&%wOffre spéciale! DERNIER EN STOCK!&Faites vite!\x0A\x02"));
});
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM,
"\x08{{item}} {{price}} Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02", 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}} Rubine\x09&&\x1B%gKaufen&Nicht kaufen%w\x09\x02",
"\x08{{item}} {{price}} Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02", "\x08{{item}} {{price}} Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02"));
});
} }
void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) { void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
@ -4614,52 +4572,52 @@ void DrawRandoEditor(bool& open) {
ImGui::End(); ImGui::End();
} }
CustomMessageEntry Randomizer::GetWarpSongMessage(u16 textId, bool mysterious) { CustomMessage Randomizer::GetWarpSongMessage(u16 textId, bool mysterious) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage( CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(
Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT); Randomizer::hintMessageTableID, TEXT_WARP_RANDOM_REPLACED_TEXT);
if (mysterious) { if (mysterious) {
std::vector<std::string> locationName ={ std::array<const char*, LANGUAGE_MAX> locationName ={
"a mysterious place", "a mysterious place",
"ein mysteriöser Ort", "ein mysteriöser Ort",
"un endroit mystérieux", "un endroit mystérieux",
}; };
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{location}}", locationName[0], messageEntry.Replace("{{location}}", locationName[0],
locationName[1], locationName[2]); locationName[1], locationName[2]);
return messageEntry; return messageEntry;
} }
std::string locationName; const char* locationName;
switch (textId) { switch (textId) {
case TEXT_WARP_MINUET_OF_FOREST: case TEXT_WARP_MINUET_OF_FOREST:
locationName = std::string(gSaveContext.warpMinuetText); locationName = gSaveContext.warpMinuetText;
break; break;
case TEXT_WARP_BOLERO_OF_FIRE: case TEXT_WARP_BOLERO_OF_FIRE:
locationName = std::string(gSaveContext.warpBoleroText); locationName = gSaveContext.warpBoleroText;
break; break;
case TEXT_WARP_SERENADE_OF_WATER: case TEXT_WARP_SERENADE_OF_WATER:
locationName = std::string(gSaveContext.warpSerenadeText); locationName = gSaveContext.warpSerenadeText;
break; break;
case TEXT_WARP_REQUIEM_OF_SPIRIT: case TEXT_WARP_REQUIEM_OF_SPIRIT:
locationName = std::string(gSaveContext.warpRequiemText); locationName = gSaveContext.warpRequiemText;
break; break;
case TEXT_WARP_NOCTURNE_OF_SHADOW: case TEXT_WARP_NOCTURNE_OF_SHADOW:
locationName = std::string(gSaveContext.warpNocturneText); locationName = gSaveContext.warpNocturneText;
break; break;
case TEXT_WARP_PRELUDE_OF_LIGHT: case TEXT_WARP_PRELUDE_OF_LIGHT:
locationName = std::string(gSaveContext.warpPreludeText); locationName = gSaveContext.warpPreludeText;
break; break;
} }
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{location}}", locationName); messageEntry.Replace("{{location}}", locationName);
return messageEntry; return messageEntry;
} }
CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) { CustomMessage Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId); CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId);
RandomizerCheck rc = GetCheckFromRandomizerInf(randomizerInf); RandomizerCheck rc = GetCheckFromRandomizerInf(randomizerInf);
RandomizerGet shopItemGet = this->itemLocations[rc].rgID; RandomizerGet shopItemGet = this->itemLocations[rc].rgID;
std::vector<std::string> shopItemName; std::array<std::string, LANGUAGE_MAX> shopItemName;
if (mysterious) { if (mysterious) {
shopItemName = { shopItemName = {
"mysterious item", "mysterious item",
@ -4688,16 +4646,16 @@ CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u
} }
} }
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{item}}", shopItemName[0], shopItemName[1], shopItemName[2]); messageEntry.Replace("{{item}}", std::move(shopItemName[0]), std::move(shopItemName[1]), std::move(shopItemName[2]));
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{price}}", std::to_string(shopItemPrice)); messageEntry.Replace("{{price}}", std::to_string(shopItemPrice));
return messageEntry; return messageEntry;
} }
CustomMessageEntry Randomizer::GetCursedSkullMessage(s16 params) { CustomMessage Randomizer::GetCursedSkullMessage(s16 params) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE); CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::randoMiscHintsTableID, TEXT_CURSED_SKULLTULA_PEOPLE);
RandomizerCheck rc = GetCheckFromActor(ACTOR_EN_SSH, SCENE_KINSUTA, params); RandomizerCheck rc = GetCheckFromActor(ACTOR_EN_SSH, SCENE_KINSUTA, params);
RandomizerGet itemGet = this->itemLocations[rc].rgID; RandomizerGet itemGet = this->itemLocations[rc].rgID;
std::vector<std::string> itemName; std::array<std::string, LANGUAGE_MAX> itemName;
if (itemGet == RG_ICE_TRAP) { if (itemGet == RG_ICE_TRAP) {
itemGet = this->itemLocations[rc].fakeRgID; itemGet = this->itemLocations[rc].fakeRgID;
itemName = { itemName = {
@ -4709,8 +4667,8 @@ CustomMessageEntry Randomizer::GetCursedSkullMessage(s16 params) {
itemName = EnumToSpoilerfileGetName[itemGet]; itemName = EnumToSpoilerfileGetName[itemGet];
} }
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{params}}", std::to_string(params*10)); messageEntry.Replace("{{params}}", std::to_string(params*10));
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{check}}", itemName[0], itemName[1], itemName[2]); messageEntry.Replace("{{check}}", std::move(itemName[0]), std::move(itemName[1]), std::move(itemName[2]));
return messageEntry; 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." }, { "&Elle vous semble %rordinaire%w.", "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus." },
}; };
CustomMessageEntry Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) { CustomMessage Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId); CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId);
int sceneNum; int sceneNum;
switch (itemEntry.getItemId) { switch (itemEntry.getItemId) {
case RG_DEKU_TREE_MAP: case RG_DEKU_TREE_MAP:
@ -4757,23 +4715,25 @@ CustomMessageEntry Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEnt
} }
if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) { if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) {
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", ""); messageEntry.Replace("{{typeHint}}", "");
} else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) { } 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 { } 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; return messageEntry;
} }
void CreateGetItemMessages(std::vector<GetItemMessage> messageEntries) { template<size_t N>
void CreateGetItemMessages(const std::array<GetItemMessage, N>* messageEntries) {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID); customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID);
for (const GetItemMessage& messageEntry : messageEntries) { for (const GetItemMessage& messageEntry : *messageEntries) {
customMessageManager->CreateGetItemMessage(Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid, customMessageManager->CreateGetItemMessage(
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english, Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid,
messageEntry.german, messageEntry.french }); CustomMessage(messageEntry.english, messageEntry.german, messageEntry.french, TEXTBOX_TYPE_BLUE,
TEXTBOX_POS_BOTTOM));
} }
} }
@ -4797,25 +4757,22 @@ void CreateRupeeMessages() {
rupeeText = "\x05\x06 200 {{rupee}}\x05\x00"; rupeeText = "\x05\x06 200 {{rupee}}\x05\x00";
break; break;
} }
customMessageManager->CreateMessage(Randomizer::rupeeMessageTableID, rupee, customMessageManager->CreateMessage(
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, Randomizer::rupeeMessageTableID, rupee,
"You found" + rupeeText + " !", CustomMessage("You found" + rupeeText + " !", "Du hast" + rupeeText + " gefunden!",
"Du hast" + rupeeText + " gefunden!", "Vous obtenez" + rupeeText + " !", TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM));
"Vous obtenez" + rupeeText + " !"
});
} }
} }
CustomMessageEntry Randomizer::GetRupeeMessage(u16 rupeeTextId) { CustomMessage Randomizer::GetRupeeMessage(u16 rupeeTextId) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId); CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId);
std::string englishName = RandomElement(englishRupeeNames); messageEntry.Replace("{{rupee}}", RandomElement(englishRupeeNames),
std::string germanName = RandomElement(germanRupeeNames); RandomElement(germanRupeeNames), RandomElement(frenchRupeeNames));
std::string frenchName = RandomElement(frenchRupeeNames);
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{rupee}}", englishName, germanName, frenchName);
return messageEntry; return messageEntry;
} }
CustomMessageMinimal NaviMessages[NUM_NAVI_MESSAGES] = { void CreateNaviRandoMessages() {
CustomMessage NaviMessages[NUM_NAVI_MESSAGES] = {
{ "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!", { "%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!", "%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!",
@ -4827,7 +4784,8 @@ CustomMessageMinimal NaviMessages[NUM_NAVI_MESSAGES] = {
{ "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?", { "%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?", "%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?" }, "%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...", { "%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...", "%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...",
@ -4835,150 +4793,150 @@ CustomMessageMinimal NaviMessages[NUM_NAVI_MESSAGES] = {
{ "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%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?", "%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?" }, "%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!", { "%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!", "%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem "
"%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec une&%rcocotte%c!" }, "%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?", { "%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?", "%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à?" }, "%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!", { "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is "
"%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der Rest&ist Krimskrams!", "junk!",
"%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont intéréssant?" }, "%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!", { "%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!", "%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!" }, "%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!", { "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your "
"%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. Dies&ist zu deiner Sicherheit!", "safety!",
"%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é!" }, "%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!", { "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with "
"%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer %coder&%wPfeilen %cdetonieren!", "%warrows%c!",
"%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!" }, "%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!", { "%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!", "%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!" }, "%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?", { "%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?", "%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es "
"%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é?" }, "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!", { "%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!", "%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?" }, "%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!", { "%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.", "%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula "
"%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." }, "%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!", { "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a "
"%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken trägst%c!", "mask%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!" }, "%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!", { "%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!", "%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!" }, "%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!", { "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater "
"%cShiek wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund %bWassers&%cbesitzt.", "%cMedallions!",
"%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!" }, "%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!", { "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your "
"%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind effektiv gegen&Widersacher!", "foes!",
"%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!" } "%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? "
void CreateNaviRandoMessages() { "Ils&sont efficaces contre tes ennemis!" }
};
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::NaviRandoMessageTableID); customMessageManager->AddCustomMessageTable(Randomizer::NaviRandoMessageTableID);
for (unsigned int i = 0; i <= (NUM_NAVI_MESSAGES - 1); i++) { for (unsigned int i = 0; i <= (NUM_NAVI_MESSAGES - 1); i++) {
customMessageManager->CreateMessage(Randomizer::NaviRandoMessageTableID, i, customMessageManager->CreateMessage(
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, NaviMessages[i].english, Randomizer::NaviRandoMessageTableID, i,
NaviMessages[i].german, NaviMessages[i].french }); NaviMessages[i]);
} }
} }
CustomMessageMinimal IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = { void CreateIceTrapRandoMessages() {
{ "You are a %bFOOL%w!", CustomMessage IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = {
"Du bist ein %bDUMMKOPF%w!", { "You are a %bFOOL%w!", "Du bist ein %bDUMMKOPF%w!", "%bPauvre fou%w..." },
"%bPauvre fou%w..." },
{ "You are a %bFOWL%w!", { "You are a %bFOWL%w!", "Du bist eine %bFrostbeule%w!", "Tu es un %bglaçon%w, Harry!" },
"Du bist eine %bFrostbeule%w!",
"Tu es un %bglaçon%w, Harry!" },
{ "%bFOOL%w!", { "%bFOOL%w!", "%bDUMMKOPF%w!", "%bSot%w que tu es." },
"%bDUMMKOPF%w!",
"%bSot%w que tu es." },
{ "You just got %bPUNKED%w!", { "You just got %bPUNKED%w!", "Du wurdest %beiskalt%w erwischt!", "Ça me %bglace%w le sang!" },
"Du wurdest %beiskalt%w erwischt!",
"Ça me %bglace%w le sang!" },
{ "Stay %bfrosty%w, @.", { "Stay %bfrosty%w, @.", "Es läuft dir %beiskalt%w den Rücken&hinunter, @.", "%bReste au frais%w, @." },
"Es läuft dir %beiskalt%w den Rücken&hinunter, @.",
"%bReste au frais%w, @." },
{ "Take a %bchill pill%w, @.", { "Take a %bchill pill%w, @.", "Bleib %bcool%w, @.", "Et c'est la douche %bfroide%w!" },
"Bleib %bcool%w, @.",
"Et c'est la douche %bfroide%w!" },
{ "%bWinter%w is coming.", { "%bWinter%w is coming.", "Der %bWinter%w naht.", "L'%bhiver%w vient." },
"Der %bWinter%w naht.",
"L'%bhiver%w vient." },
{ "%bICE%w to see you, @.", { "%bICE%w to see you, @.", "Alles %bcool%w im Pool?", "%bGlacier%w!" },
"Alles %bcool%w im Pool?",
"%bGlacier%w!" },
{ "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.", { "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.", "%bAbkühlung gefällig%w?",
"%bAbkühlung gefällig%w?",
"%Ça en jette un %bfroid%w." }, "%Ça en jette un %bfroid%w." },
{ "It's a %bcold day%w in the Evil Realm.", { "It's a %bcold day%w in the Evil Realm.", "Es ist ein %kalter%w Tag im Herzen&von Hyrule.",
"Es ist ein %kalter%w Tag im Herzen&von Hyrule.",
"Est-ce que tu as déjà eu des sueurs&%bfroides%w?" }, "Est-ce que tu as déjà eu des sueurs&%bfroides%w?" },
{ "Getting %bcold feet%w?", { "Getting %bcold feet%w?", "Bekommst du etwa %bkalte%w Füße?",
"Bekommst du etwa %bkalte%w Füße?",
"La vengeance est un plat qui se mange&%bfroid%w!" }, "La vengeance est un plat qui se mange&%bfroid%w!" },
{ "Say hello to the %bZoras%w for me!", { "Say hello to the %bZoras%w for me!", "Sag den %bZoras%w viele Grüße von mir!",
"Sag den %bZoras%w viele Grüße von mir!",
"Dit bonjour aux %bZoras%w pour moi!" }, "Dit bonjour aux %bZoras%w pour moi!" },
{ "Can you keep a %bcool head%w?", { "Can you keep a %bcool head%w?", "Bewahre einen %bkühlen%w! Kopf.",
"Bewahre einen %bkühlen%w! Kopf.",
"Il faut parfois savoir garder la tête&%bfroide%w!" }, "Il faut parfois savoir garder la tête&%bfroide%w!" },
{ "Ganondorf used %bIce Trap%w!&It's super effective!", { "Ganondorf used %bIce Trap%w!&It's super effective!",
"Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!", "Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!",
"Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" }, "Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" },
{ "Allow me to break the %bice%w!", { "Allow me to break the %bice%w!", "Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!",
"Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!",
"Laisse moi briser la %bglace%w!" }, "Laisse moi briser la %bglace%w!" },
{ "%bCold pun%w.", { "%bCold pun%w.", "%bEiskalt%w lässt du meine Seele&erfrier'n.",
"%bEiskalt%w lässt du meine Seele&erfrier'n.",
"Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" }, "Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" },
{ "The %bTitanic%w would be scared of you,&@.", { "The %bTitanic%w would be scared of you,&@.", "Die %bTitanic%w hätte Angst vor dir,&@.",
"Die %bTitanic%w hätte Angst vor dir,&@.",
"Le %bTitanic%w aurait peur de toi,&@." }, "Le %bTitanic%w aurait peur de toi,&@." },
{ "Oh no!", { "Oh no!", "Oh nein!", "Oh non!" },
"Oh nein!",
"Oh non!" },
{ "What killed the dinosaurs?&The %bICE%w age!", { "What killed the dinosaurs?&The %bICE%w age!", "Was die Dinosaurier getötet hat?&Die %bEiszeit%w!",
"Was die Dinosaurier getötet hat?&Die %bEiszeit%w!",
"Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%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.", { "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.",
@ -4993,103 +4951,120 @@ CustomMessageMinimal IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = {
"Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!", "Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!",
"Merci %b@%w!&Mais ton objet est dans un autre&château!" }, "Merci %b@%w!&Mais ton objet est dans un autre&château!" },
{ "%bFREEZE%w! Don't move!", { "%bFREEZE%w! Don't move!", " Kalt. Kalt. Kälter. %bEISKALT%w!",
" Kalt. Kalt. Kälter. %bEISKALT%w!",
"J'espère que ça ne te fait ni chaud, ni&%bfroid%w." }, "J'espère que ça ne te fait ni chaud, ni&%bfroid%w." },
}; };
void CreateIceTrapRandoMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::IceTrapRandoMessageTableID); customMessageManager->AddCustomMessageTable(Randomizer::IceTrapRandoMessageTableID);
for (u8 i = 0; i <= (NUM_ICE_TRAP_MESSAGES - 1); i++) { for (u8 i = 0; i <= (NUM_ICE_TRAP_MESSAGES - 1); i++) {
customMessageManager->CreateMessage(Randomizer::IceTrapRandoMessageTableID, i, customMessageManager->CreateMessage(Randomizer::IceTrapRandoMessageTableID, i,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, IceTrapMessages[i].english, IceTrapMessages[i]);
IceTrapMessages[i].german, IceTrapMessages[i].french });
} }
// We only use this ice trap message for christmas, so we don't want it in the normal ice trap messages rotation // 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, customMessageManager->CreateMessage(
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, 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!", "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!"));
"Pour Noël, cette année, tu&n'auras que du %BCHARBON!&%rJoyeux Noël%w!" });
} }
CustomMessageMinimal FireTempleGoronMessages[NUM_GORON_MESSAGES] = { 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?", "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?", "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é?", "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.", "Thank Hylia! I was so worried about&when my teacher would let me get&out of detention.^I gotta go home "
"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.", "and see my parents.",
"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?", "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!!", "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 "
"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!!!", "cake!!",
"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...", "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 // 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\xC7You fell into my %rtrap!%w&Foolish boy, it was me, Ganondorf!!!^...whoa, where am I?&What "
"\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...", "happened?^Weird.",
"\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é?", "\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.", "Thanks, but I don't know if I wanna go&just yet...^Hmm...^...^...^...^...^...maybe I can come back "
"Danke für die Rettung, aber&eigentlich finde ich es hier ganz&nett...^Hmm...^...^...^...^...^...Naja, ich kann ja jederzeit&wiederkommen. Man sieht sich.", "later.&Bye bye.",
"Merci, mais je me sens plus en&sécurité ici...^...^...^...^...^Hmm...^...Tout compte fait, je vais y aller.&A plus tard.", "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!",
}, },
{ {
"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.", "In that case, I'll help you out!^They say that %rthe thing you're&looking for%w can only be found%g "
"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...", "when&you're not looking for it.%w^Hope that helps!",
"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..." "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!",
"\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!", "Dans ce cas, je vais t'aider!&On dit que l'objet que tu cherches&ne peut être trouvé que lorsque&tu ne le "
"\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!", "cherches pas.",
"\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.", "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.", "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.", "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};
void CreateFireTempleGoronMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(customMessageTableID); customMessageManager->AddCustomMessageTable(customMessageTableID);
for (u8 i = 0; i <= NUM_GORON_MESSAGES - 1; i++) { for (u8 i = 0; i <= NUM_GORON_MESSAGES - 1; i++) {
customMessageManager->CreateMessage(customMessageTableID, goronIDs[i], { customMessageManager->CreateMessage(customMessageTableID, goronIDs[i], FireTempleGoronMessages[i]);
TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
FireTempleGoronMessages[i].english, FireTempleGoronMessages[i].german, FireTempleGoronMessages[i].french
});
} }
} }
CustomMessageEntry Randomizer::GetGoronMessage(u16 index) { CustomMessage Randomizer::GetGoronMessage(u16 index) {
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, goronIDs[index]); CustomMessage messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, goronIDs[index]);
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{days}}", std::to_string(gSaveContext.totalDays)); messageEntry.Replace("{{days}}", std::to_string(gSaveContext.totalDays));
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{a_btn}}", std::to_string(gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A])); messageEntry.Replace("{{a_btn}}", std::to_string(gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]));
return messageEntry; return messageEntry;
} }
void Randomizer::CreateCustomMessages() { void Randomizer::CreateCustomMessages() {
// RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED
// with GIMESSAGE(getItemID, itemID, english, german, french). // with GIMESSAGE(getItemID, itemID, english, german, french).
const std::vector<GetItemMessage> getItemMessages = { const std::array<GetItemMessage, 56> getItemMessages = {{
GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON, GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON,
"You found %gGreg%w!", "You found %gGreg%w!",
"%gGreg%w! Du hast ihn wirklich gefunden!", "%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!", "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!", "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!") "Vous obtenez la %rBourse de Magnat%w!&Elle peut contenir jusqu'à %y999 rubis%w!&C'est gigantesque!")
}; }};
CreateGetItemMessages(getItemMessages); CreateGetItemMessages(&getItemMessages);
CreateRupeeMessages(); CreateRupeeMessages();
CreateNaviRandoMessages(); CreateNaviRandoMessages();
CreateIceTrapRandoMessages(); CreateIceTrapRandoMessages();

View File

@ -53,7 +53,7 @@ class Randomizer {
std::unordered_map<RandomizerInf, bool> trialsRequired; std::unordered_map<RandomizerInf, bool> trialsRequired;
std::unordered_set<uint16_t> masterQuestDungeons; std::unordered_set<uint16_t> masterQuestDungeons;
std::unordered_map<RandomizerCheck, u16> merchantPrices; std::unordered_map<RandomizerCheck, u16> merchantPrices;
std::unordered_map<RandomizerGet, std::vector<std::string>> EnumToSpoilerfileGetName; std::unordered_map<RandomizerGet, std::array<std::string, 3>> EnumToSpoilerfileGetName;
static Sprite* GetSeedTexture(uint8_t index); static Sprite* GetSeedTexture(uint8_t index);
s16 GetItemModelFromId(s16 itemId); s16 GetItemModelFromId(s16 itemId);
@ -87,13 +87,13 @@ class Randomizer {
GetItemID GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId); GetItemID GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId);
ItemObtainability GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck); ItemObtainability GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck);
ItemObtainability GetItemObtainabilityFromRandomizerGet(RandomizerGet randomizerCheck); ItemObtainability GetItemObtainabilityFromRandomizerGet(RandomizerGet randomizerCheck);
CustomMessageEntry GetWarpSongMessage(u16 textId, bool mysterious = false); CustomMessage GetWarpSongMessage(u16 textId, bool mysterious = false);
CustomMessageEntry GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious = false); CustomMessage GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious = false);
CustomMessageEntry GetCursedSkullMessage(s16 params); CustomMessage GetCursedSkullMessage(s16 params);
CustomMessageEntry GetGoronMessage(u16 index); CustomMessage GetGoronMessage(u16 index);
CustomMessageEntry GetMapGetItemMessageWithHint(GetItemEntry itemEntry); CustomMessage GetMapGetItemMessageWithHint(GetItemEntry itemEntry);
static void CreateCustomMessages(); static void CreateCustomMessages();
static CustomMessageEntry GetRupeeMessage(u16 rupeeTextId); static CustomMessage GetRupeeMessage(u16 rupeeTextId);
bool CheckContainsVanillaItem(RandomizerCheck randoCheck); bool CheckContainsVanillaItem(RandomizerCheck randoCheck);
}; };

View File

@ -1677,22 +1677,43 @@ extern "C" void* getN64WeirdFrame(s32 i) {
} }
extern "C" int GetEquipNowMessage(char* buffer, char* src, const int maxBufferSize) { 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; std::string postfix;
if (gSaveContext.language == LANGUAGE_FRA) { if (gSaveContext.language == LANGUAGE_FRA) {
postfix = "\x04\x1A\x08" "D\x96sirez-vous l'\x96quiper maintenant?" "\x09&&" postfix = customMessage.GetFrench();
"\x1B%g" "Oui" "&"
"Non" "%w\x02";
} else if (gSaveContext.language == LANGUAGE_GER) { } else if (gSaveContext.language == LANGUAGE_GER) {
postfix = "\x04\x1A\x08" "M""\x9A""chtest Du es jetzt ausr\x9Esten?" "\x09&&" postfix = customMessage.GetGerman();
"\x1B%g" "Ja!" "&"
"Nein!" "%w\x02";
} else { } else {
postfix = "\x04\x1A\x08" "Would you like to equip it now?" "\x09&&" postfix = customMessage.GetEnglish();
"\x1B%g" "Yes" "&"
"No" "%w\x02";
} }
CustomMessageManager::Instance->FormatCustomMessage(postfix);
std::string str; std::string str;
std::string FixedBaseStr(src); std::string FixedBaseStr(src);
int RemoveControlChar = FixedBaseStr.find_first_of("\x02"); int RemoveControlChar = FixedBaseStr.find_first_of("\x02");
@ -1796,14 +1817,14 @@ extern "C" ItemObtainability Randomizer_GetItemObtainabilityFromRandomizerCheck(
return OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerCheck(randomizerCheck); return OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerCheck(randomizerCheck);
} }
extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(Player* player) { CustomMessage Randomizer_GetCustomGetItemMessage(Player* player) {
s16 giid; s16 giid;
if (player->getItemEntry.objectId != OBJECT_INVALID) { if (player->getItemEntry.objectId != OBJECT_INVALID) {
giid = player->getItemEntry.getItemId; giid = player->getItemEntry.getItemId;
} else { } else {
giid = player->getItemId; giid = player->getItemId;
} }
const CustomMessageEntry getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid); const CustomMessage getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid);
return getItemText; return getItemText;
} }
@ -1813,7 +1834,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
Font* font = &msgCtx->font; Font* font = &msgCtx->font;
char* buffer = font->msgBuf; char* buffer = font->msgBuf;
const int maxBufferSize = sizeof(font->msgBuf); const int maxBufferSize = sizeof(font->msgBuf);
CustomMessageEntry messageEntry; CustomMessage messageEntry;
s16 actorParams = 0; s16 actorParams = 0;
if (gSaveContext.n64ddFlag) { if (gSaveContext.n64ddFlag) {
if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) { 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 // 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); s16 gsCount = gSaveContext.inventory.gsTokens + (gSaveContext.n64ddFlag ? 1 : 0);
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); 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)) { if (textId == TEXT_HEART_CONTAINER && CVarGetInteger("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_CONTAINER); 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)) { if (textId == TEXT_HEART_PIECE && CVarGetInteger("gInjectItemCounts", 0)) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_HEART_PIECE); 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) { if (textId == TEXT_MARKET_GUARD_NIGHT && CVarGetInteger("gMarketSneak", 0) && play->sceneNum == SCENE_ENTRA_N) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT);
@ -1969,20 +1990,18 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
if (textId == TEXT_RANDO_SAVE_VERSION_WARNING) { if (textId == TEXT_RANDO_SAVE_VERSION_WARNING) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING);
} }
if (messageEntry.textBoxType != -1) { font->charTexBuf[0] = (messageEntry.GetTextBoxType() << 4) | messageEntry.GetTextBoxPosition();
font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos;
switch (gSaveContext.language) { switch (gSaveContext.language) {
case LANGUAGE_FRA: case LANGUAGE_FRA:
return msgCtx->msgLength = font->msgLength = return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.french, buffer, maxBufferSize); CopyStringToCharBuffer(messageEntry.GetFrench(), buffer, maxBufferSize);
case LANGUAGE_GER: case LANGUAGE_GER:
return msgCtx->msgLength = font->msgLength = return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.german, buffer, maxBufferSize); CopyStringToCharBuffer(messageEntry.GetGerman(), buffer, maxBufferSize);
case LANGUAGE_ENG: case LANGUAGE_ENG:
default: default:
return msgCtx->msgLength = font->msgLength = return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.english, buffer, maxBufferSize); CopyStringToCharBuffer(messageEntry.GetEnglish(), buffer, maxBufferSize);
}
} }
return false; return false;
} }

View File

@ -109,74 +109,57 @@ extern "C" void OTRMessage_Init()
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID); CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);
CustomMessageManager::Instance->CreateGetItemMessage( CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN, 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",
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, "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!\x0E\x3C", "Skulltula-Symbole&gesammelt!\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" "tout!\x0E\x3C",
} TEXTBOX_TYPE_BLUE));
);
CustomMessageManager::Instance->CreateGetItemMessage( CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN, customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN,
{ CustomMessage(
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rGold Skulltula Token%w!&You've collected %r{{gsCount}}%w tokens&in total!", "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!", "Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r{{gsCount}}&%wGoldene "
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r{{gsCount}}%w symboles en tout!" "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( CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_BUY_BOMBCHU_10_DESC, customMessageTableID, TEXT_BUY_BOMBCHU_10_DESC,
{ CustomMessage("\x08%rBombchu (10 pieces) 99 Rupees&%wThis looks like a toy mouse, but&it's actually a "
TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, "self-propelled time&bomb!\x09\x0A",
"\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 "
"\x08%rKrabbelmine 10 Stück 99 Rubine&%wDas ist eine praktische Zeitbombe,&die Du als Distanzwaffe&einsetzen kannst!\x09\x0A", "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", "\x08%rMissile 10 unités 99 Rubis&%wProfilée comme une souris&mécanique, cette arme est "
} "&destructrice!!!\x09\x0A"));
);
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_BUY_BOMBCHU_10_PROMPT, customMessageTableID, TEXT_BUY_BOMBCHU_10_PROMPT,
{ CustomMessage("\x08"
TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, "Bombchu 10 pieces 99 Rupees\x09&&\x1B%gBuy&Don't buy%w",
"\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", "\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", "\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w"));
}
);
CustomMessageManager::Instance->CreateGetItemMessage( CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_CONTAINER, ITEM_HEART_CONTAINER, customMessageTableID, (GetItemID)TEXT_HEART_CONTAINER, ITEM_HEART_CONTAINER,
{ CustomMessage(
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rHeart Container%w!&You've collected %r{{heartContainerCount}}%w containers&in total!", "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!", "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( CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_HEART_PIECE, ITEM_HEART_PIECE, customMessageTableID, (GetItemID)TEXT_HEART_PIECE, ITEM_HEART_PIECE,
{ CustomMessage("You got a %rHeart Piece%w!&You've collected %r{{heartPieceCount}}%w pieces&in total!",
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!", "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!" "Vous obtenez un %rQuart de&Coeur%w! Vous en avez collecté&%r{{heartPieceCount}}%w en tout!",
} TEXTBOX_TYPE_BLUE));
);
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_MARKET_GUARD_NIGHT, customMessageTableID, TEXT_MARKET_GUARD_NIGHT,
{ CustomMessage("You look bored. Wanna go out for a&walk?\x1B&%gYes&No%w",
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", "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", "Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w"));
}
);
CustomMessageManager::Instance->CreateMessage( CustomMessageManager::Instance->CreateMessage(
customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING, customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING,
{ CustomMessage(
TEXTBOX_TYPE_NONE_BOTTOM, TEXTBOX_POS_BOTTOM,
"This save was created on&a different version of SoH.&&Things may be broken.", "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.", "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));
} }