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 }) {
size_t start_pos = 0; for (auto specialCharacterPair : textBoxSpecialCharacters) {
std::string textBoxSpecialCharacterString = ""s; size_t start_pos = 0;
textBoxSpecialCharacterString += specialCharacterPair.second; std::string textBoxSpecialCharacterString = ""s;
while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) { textBoxSpecialCharacterString += specialCharacterPair.second;
string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); while ((start_pos = str->find(specialCharacterPair.first, start_pos)) != std::string::npos) {
start_pos += textBoxSpecialCharacterString.length(); str->replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length();
}
} }
} }
} }
void CustomMessageManager::ReplaceColors(std::string& string) { void CustomMessage::ReplaceColors() {
for (auto colorPair : colors) { for (std::string* str : { &english, &french, &german }) {
std::string textToReplace = "%"; for (auto colorPair : colors) {
textToReplace += colorPair.first; std::string textToReplace = "%";
size_t start_pos = 0; textToReplace += colorPair.first;
while ((start_pos = string.find(textToReplace)) != std::string::npos) { size_t start_pos = 0;
string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second)); while ((start_pos = str->find(textToReplace, start_pos)) != std::string::npos) {
start_pos += textToReplace.length(); str->replace(start_pos, textToReplace.length(), COLOR(colorPair.second));
start_pos += textToReplace.length();
}
} }
} }
} }
void ReplaceString(std::string& source, std::string textToReplace, std::string value) { const std::string CustomMessage::MESSAGE_END() const {
size_t pos = source.find(textToReplace); return "\x02"s;
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::ITEM_OBTAINED(uint8_t x) const {
ReplaceString(messageEntry.english, textToReplace, value); return "\x13"s + char(x);
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::NEWLINE() const {
ReplaceString(messageEntry.english, textToReplace, englishValue); return "\x01"s;
ReplaceString(messageEntry.german, textToReplace, germanValue);
ReplaceString(messageEntry.french, textToReplace, frenchValue);
} }
void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) { const std::string CustomMessage::COLOR(uint8_t x) const {
message.insert(0, ITEM_OBTAINED(iid)); return "\x05"s + char(x);
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::WAIT_FOR_INPUT() const {
size_t start_pos = 0; return "\x04"s;
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::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
*/
static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string value);
static void ReplaceStringInMessage(CustomMessageEntry& messageEntry, std::string textToReplace, std::string englishValue, std::string germanValue, std::string frenchValue);
CustomMessageManager(); /**
~CustomMessageManager(); * @brief Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
* with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to
* the provided iid (itemID) at the beginning of each page of the textbox.
*
* @param tableID the ID of the custom message table
* @param giid the GetItemID of the item (or the textID you want to use to retrieve it later)
* @param iid the ItemID of the item
* @param message the CustomMessage instance being added
* @return true if adding the custom message succeeds, or
* @return false if it does not.
*/
bool CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, CustomMessage message);
/* /**
Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, * @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 * with the provided textID as its key.
the provided iid (itemID) at the beginning of each page of the textbox. *
*/ * @param tableID the ID of the custom message table
bool CreateGetItemMessage(std::string tableID, uint16_t giid, ItemID iid, CustomMessageEntry messages); * @param textID the ID to use for later retrieval
* @param message the CustomMessage instance being added
* @return true if adding the custom message succeeds, or
* @return false if it does not.
*/
bool CreateMessage(std::string tableID, uint16_t textID, CustomMessage message);
/* /**
Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, * @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
bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); * to modify it's contents between retrieval and displaying it in game, in order to
* display some dynamic data like gameplay stats.
*
* @param tableID the ID of the custom message table
* @param textID the ID of the message you want to retrieve
* @return CustomMessage
*/
CustomMessage RetrieveMessage(std::string tableID, uint16_t textID);
/* /**
Retrieves a message from the table with id tableID with the provided textID. * @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
& 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).
*/
void FormatCustomMessage(std::string& message, ItemID iid);
/*
Replaces special characters and certain symbols with control codes
& for newline, ^ for wait-for-input, and @ for the player name,
as well as %<letter> for colors (i.e. %r for red and %w for white).
*/
void FormatCustomMessage(std::string& message);
}; };
class MessageNotFoundException : public std::exception {
private:
std::string messageTableId;
uint16_t textId;
public:
MessageNotFoundException(std::string messageTableId_, uint16_t textId_)
: messageTableId(messageTableId_), textId(textId_) {
}
MessageNotFoundException(std::string&& messageTableId_, uint16_t textId_)
: messageTableId(std::move(messageTableId_)), textId(textId_) {
}
virtual const char* what() const noexcept {
char* message;
sprintf(message, "Message from table %s with textId %u was not found", messageTableId.c_str(), textId);
return message;
}
};

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
@ -569,45 +536,36 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
"!%w&Je ne te dirai pas ce que c'est avant&d'être payé rubis sur l'ongle...^" "!%w&Je ne te dirai pas ce que c'est avant&d'être payé rubis sur l'ongle...^"
}; };
} }
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 "
"Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout le&monde! L'objet du jour est...^%r{{item}}" + "le&monde! L'objet du jour est...^%r{{item}}" +
cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w" 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, "Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!",
TEXTBOX_POS_TOP, "Squalala! Je vais enfin pouvoir &%rprendre des vacances%w!"));
"Finally! Now I can go back to being &an %rarms dealer%w!",
"Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!",
"Squalala! Je vais enfin pouvoir &%rprendre des vacances%w!"
}
);
// Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are // 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,
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE, CustomMessage("\x08{{item}} {{price}} Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02",
"\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,299 +4757,314 @@ 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] = {
{ "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!",
"%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!",
"%cIl te manque une %wPetite Clé %cdans&un donjon? C'est peut-être le %rboss&%cqui l'a!" },
{ "%cSometimes you can use the %rMegaton&Hammer %cinstead of bombs!",
"%cManchmal kannst du den %rStahlhammer&%cstatt Bomben verwenden!",
"%cParfois, tu peux utiliser la %rMasse&des Titans %cau lieu de tes bombes!" },
{ "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?",
"%cEs gibt drei %gDeku-Händler %cin Hyrule&die mysteriöse Gegenstände&verkaufen. Weißt du wo Sie sind?",
"%cIl y a trois %gPestes Marchandes%c en&Hyrule qui vendent des %wobjets&mystérieux%c. Tu sais où elles sont?" },
{ "%cStuck on this seed? You could &throw in the towel and check the&%wspoiler log%c...",
"%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...",
"%cSi tu es coincé sur cette seed,&tu peux toujours jeter l'éponge&et regader le %wSpoiler log%c..." },
{ "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%c?",
"%cWußtest du, daß du mit den&%yGleitstiefeln %cTreibsand %wüberqueren&kannst%c?",
"%cEst-ce que tu savais que les %rBottes&des airs %cpouvaient être utilisées&pour traverser les %wsables mouvants%c?" },
{ "%cYou can reach the back of %wImpa's&House %cby jumping from the&unfinished house with a %rcucco%c!",
"%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem %rHuhn&%cspringst!",
"%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec une&%rcocotte%c!" },
{ "%cThe %ySpirit Trial %cin %pGanon's Castle&%chas a %whidden chest %csomewhere.&Did you already know that?",
"%cDie %yGeister-Prüfung %cin %pGanons&Schloß %chat irgendwo eine&%wversteckte Kiste%c. Weißt du schon&wo?",
"%cL'%yÉpreuve de l'Esprit%c dans le %pChâteau&de Ganon %ca un coffre caché quelque&part. Je suppose que tu le savais&déjà?" },
{ "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is junk!",
"%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der Rest&ist Krimskrams!",
"%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont intéréssant?" },
{ "%cHave you been using %wDeku Nuts&%cenough? I've seen them blow up&a %rBombchu%c!",
"%cBenutzt du auch genügend %wDeku&Nüsse%c? Ich habe mal gesehen daß&man damit %rKrabbelminen %cdetonieren&kann!",
"%cTu es sûr d'utiliser tes %wNoix Mojo &%ccorrectement? J'en ai déjà vu&exploser des %rChoux-Péteurs%c!" },
{ "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your safety!",
"%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. Dies&ist zu deiner Sicherheit!",
"%cIl se peut que tu aies une %wPetite&Clé %cen trop dans le %bTemple de l'Eau&%cou le %rTemple du Feu%c. C'est pour ta&propre sécurité!" },
{ "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with %warrows%c!",
"%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer %coder&%wPfeilen %cdetonieren!",
"%cSi tu ne peux pas ramasser&un %rChoux-Péteur %cavec tes mains, tu&peux toujours le faire exploser&avec du %rFeu %cou avec des %wflèches%c!" },
{ "%cEven an adult can't push large&blocks without some %wextra&strength%c!",
"%cSelbst ein Erwachsener kann ohne&etwas %wzusätzliche Kraft %ckeine&großen Blöcke verschieben!",
"%cMême un adulte ne peut pas pousser&de grands blocs sans un petit %wgain&de force%c!" },
{ "%cI've heard that %rFlare Dancer&%cis weak to the %wMaster Sword%c!&Have you tried it?",
"%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es schonmal versucht einzusetzen?",
"%cJ'ai entendu dire que les %rDanse-&Flammes %csont faîbles face à l'%wÉpée de&Légende%c! Est-ce que tu as essayé?" },
{ "%cDon't have a weapon to kill a&%rspider%c? Try throwing a %wpot&%cat it!",
"%cFehlt dir die Waffe um gegen&eine %rSkulltula %czu kämpfen? Versuch&Sie mit einem %wKrug %cabzuwerfen!",
"%cSi tu n'as pas d'arme pour tuer&une %raraignée%c, pourquoi n'essayerais&-tu pas de lui jetter une %wjarre&%cà la figure?" },
{ "%cI hear the patch of %wsoft soil&%cin %bZora's River %cis the only one&that isn't home to a %rspider%c!",
"%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula %cbeherbergt.",
"%cJ'ai entendu dire que la %wterre meuble&%cqui se trouve à la %bRivière Zora %cest&la seule qui ne contienne pas&d'%raraignée%c." },
{ "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a mask%c!",
"%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken trägst%c!",
"%cIl se peut que les habitants d'Hyrule&aient des %wobjets %cpour toi. Par contre,&ils risquent de ne pas trop apprécier&le fait que tu %wportes un masque%c!" },
{ "%cIf you get trapped somewhere, you&might have to %wsave your game %cand&%wreset%c!",
"%cSolltest du irgendwo eingeschloßen&sein, mußt du vielleicht dein %wSpiel&speichern %cund %wneustarten%c!",
"%cSi tu es coincé quelque part, tu&devrais %wsauvegarder ta partie %cet&faire un %wreset%c!" },
{ "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater %cMedallions!",
"%cShiek wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund %bWassers&%cbesitzt.",
"%cSheik t'attendra dans un %rvillage&en feu %clorsque tu auras récupéré&les médaillons de la %gForêt%c, du %rFeu&%cet de l'%bEau%c!" },
{ "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your foes!",
"%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind effektiv gegen&Widersacher!",
"%cSi tu n'as pas d'%wépée %cen tant&qu'enfant, pourquoi n'irais-tu pas&acheter quelques %wBâtons Mojo%c? Ils&sont efficaces contre tes ennemis!" }
};
void CreateNaviRandoMessages() { void CreateNaviRandoMessages() {
CustomMessage NaviMessages[NUM_NAVI_MESSAGES] = {
{ "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!",
"%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!",
"%cIl te manque une %wPetite Clé %cdans&un donjon? C'est peut-être le %rboss&%cqui l'a!" },
{ "%cSometimes you can use the %rMegaton&Hammer %cinstead of bombs!",
"%cManchmal kannst du den %rStahlhammer&%cstatt Bomben verwenden!",
"%cParfois, tu peux utiliser la %rMasse&des Titans %cau lieu de tes bombes!" },
{ "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?",
"%cEs gibt drei %gDeku-Händler %cin Hyrule&die mysteriöse Gegenstände&verkaufen. Weißt du wo Sie sind?",
"%cIl y a trois %gPestes Marchandes%c en&Hyrule qui vendent des %wobjets&mystérieux%c. Tu sais où elles "
"sont?" },
{ "%cStuck on this seed? You could &throw in the towel and check the&%wspoiler log%c...",
"%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...",
"%cSi tu es coincé sur cette seed,&tu peux toujours jeter l'éponge&et regader le %wSpoiler log%c..." },
{ "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%c?",
"%cWußtest du, daß du mit den&%yGleitstiefeln %cTreibsand %wüberqueren&kannst%c?",
"%cEst-ce que tu savais que les %rBottes&des airs %cpouvaient être utilisées&pour traverser les %wsables "
"mouvants%c?" },
{ "%cYou can reach the back of %wImpa's&House %cby jumping from the&unfinished house with a %rcucco%c!",
"%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem "
"%rHuhn&%cspringst!",
"%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec "
"une&%rcocotte%c!" },
{ "%cThe %ySpirit Trial %cin %pGanon's Castle&%chas a %whidden chest %csomewhere.&Did you already know that?",
"%cDie %yGeister-Prüfung %cin %pGanons&Schloß %chat irgendwo eine&%wversteckte Kiste%c. Weißt du schon&wo?",
"%cL'%yÉpreuve de l'Esprit%c dans le %pChâteau&de Ganon %ca un coffre caché quelque&part. Je suppose que tu "
"le savais&déjà?" },
{ "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is "
"junk!",
"%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der "
"Rest&ist Krimskrams!",
"%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont "
"intéréssant?" },
{ "%cHave you been using %wDeku Nuts&%cenough? I've seen them blow up&a %rBombchu%c!",
"%cBenutzt du auch genügend %wDeku&Nüsse%c? Ich habe mal gesehen daß&man damit %rKrabbelminen "
"%cdetonieren&kann!",
"%cTu es sûr d'utiliser tes %wNoix Mojo &%ccorrectement? J'en ai déjà vu&exploser des %rChoux-Péteurs%c!" },
{ "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your "
"safety!",
"%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. "
"Dies&ist zu deiner Sicherheit!",
"%cIl se peut que tu aies une %wPetite&Clé %cen trop dans le %bTemple de l'Eau&%cou le %rTemple du Feu%c. "
"C'est pour ta&propre sécurité!" },
{ "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with "
"%warrows%c!",
"%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer "
"%coder&%wPfeilen %cdetonieren!",
"%cSi tu ne peux pas ramasser&un %rChoux-Péteur %cavec tes mains, tu&peux toujours le faire exploser&avec du "
"%rFeu %cou avec des %wflèches%c!" },
{ "%cEven an adult can't push large&blocks without some %wextra&strength%c!",
"%cSelbst ein Erwachsener kann ohne&etwas %wzusätzliche Kraft %ckeine&großen Blöcke verschieben!",
"%cMême un adulte ne peut pas pousser&de grands blocs sans un petit %wgain&de force%c!" },
{ "%cI've heard that %rFlare Dancer&%cis weak to the %wMaster Sword%c!&Have you tried it?",
"%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es "
"schonmal versucht einzusetzen?",
"%cJ'ai entendu dire que les %rDanse-&Flammes %csont faîbles face à l'%wÉpée de&Légende%c! Est-ce que tu as "
"essayé?" },
{ "%cDon't have a weapon to kill a&%rspider%c? Try throwing a %wpot&%cat it!",
"%cFehlt dir die Waffe um gegen&eine %rSkulltula %czu kämpfen? Versuch&Sie mit einem %wKrug %cabzuwerfen!",
"%cSi tu n'as pas d'arme pour tuer&une %raraignée%c, pourquoi n'essayerais&-tu pas de lui jetter une "
"%wjarre&%cà la figure?" },
{ "%cI hear the patch of %wsoft soil&%cin %bZora's River %cis the only one&that isn't home to a %rspider%c!",
"%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula "
"%cbeherbergt.",
"%cJ'ai entendu dire que la %wterre meuble&%cqui se trouve à la %bRivière Zora %cest&la seule qui ne "
"contienne pas&d'%raraignée%c." },
{ "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a "
"mask%c!",
"%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken "
"trägst%c!",
"%cIl se peut que les habitants d'Hyrule&aient des %wobjets %cpour toi. Par contre,&ils risquent de ne pas "
"trop apprécier&le fait que tu %wportes un masque%c!" },
{ "%cIf you get trapped somewhere, you&might have to %wsave your game %cand&%wreset%c!",
"%cSolltest du irgendwo eingeschloßen&sein, mußt du vielleicht dein %wSpiel&speichern %cund %wneustarten%c!",
"%cSi tu es coincé quelque part, tu&devrais %wsauvegarder ta partie %cet&faire un %wreset%c!" },
{ "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater "
"%cMedallions!",
"%cShiek wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund "
"%bWassers&%cbesitzt.",
"%cSheik t'attendra dans un %rvillage&en feu %clorsque tu auras récupéré&les médaillons de la %gForêt%c, du "
"%rFeu&%cet de l'%bEau%c!" },
{ "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your "
"foes!",
"%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind "
"effektiv gegen&Widersacher!",
"%cSi tu n'as pas d'%wépée %cen tant&qu'enfant, pourquoi n'irais-tu pas&acheter quelques %wBâtons Mojo%c? "
"Ils&sont efficaces contre tes ennemis!" }
};
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* 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] = {
{ "You are a %bFOOL%w!",
"Du bist ein %bDUMMKOPF%w!",
"%bPauvre fou%w..." },
{ "You are a %bFOWL%w!",
"Du bist eine %bFrostbeule%w!",
"Tu es un %bglaçon%w, Harry!" },
{ "%bFOOL%w!",
"%bDUMMKOPF%w!",
"%bSot%w que tu es." },
{ "You just got %bPUNKED%w!",
"Du wurdest %beiskalt%w erwischt!",
"Ça me %bglace%w le sang!" },
{ "Stay %bfrosty%w, @.",
"Es läuft dir %beiskalt%w den Rücken&hinunter, @.",
"%bReste au frais%w, @." },
{ "Take a %bchill pill%w, @.",
"Bleib %bcool%w, @.",
"Et c'est la douche %bfroide%w!" },
{ "%bWinter%w is coming.",
"Der %bWinter%w naht.",
"L'%bhiver%w vient." },
{ "%bICE%w to see you, @.",
"Alles %bcool%w im Pool?",
"%bGlacier%w!" },
{ "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.",
"%bAbkühlung gefällig%w?",
"%Ça en jette un %bfroid%w." },
{ "It's a %bcold day%w in the Evil Realm.",
"Es ist ein %kalter%w Tag im Herzen&von Hyrule.",
"Est-ce que tu as déjà eu des sueurs&%bfroides%w?" },
{ "Getting %bcold feet%w?",
"Bekommst du etwa %bkalte%w Füße?",
"La vengeance est un plat qui se mange&%bfroid%w!" },
{ "Say hello to the %bZoras%w for me!",
"Sag den %bZoras%w viele Grüße von mir!",
"Dit bonjour aux %bZoras%w pour moi!" },
{ "Can you keep a %bcool head%w?",
"Bewahre einen %bkühlen%w! Kopf.",
"Il faut parfois savoir garder la tête&%bfroide%w!" },
{ "Ganondorf used %bIce Trap%w!&It's super effective!",
"Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!",
"Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" },
{ "Allow me to break the %bice%w!",
"Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!",
"Laisse moi briser la %bglace%w!" },
{ "%bCold pun%w.",
"%bEiskalt%w lässt du meine Seele&erfrier'n.",
"Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" },
{ "The %bTitanic%w would be scared of you,&@.",
"Die %bTitanic%w hätte Angst vor dir,&@.",
"Le %bTitanic%w aurait peur de toi,&@." },
{ "Oh no!",
"Oh nein!",
"Oh non!" },
{ "What killed the dinosaurs?&The %bICE%w age!",
"Was die Dinosaurier getötet hat?&Die %bEiszeit%w!",
"Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%w!" },
{ "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.",
"Nachts ist es %bkälter%w als draußen.",
"L'imbécile réfléchit uniquement quand il&s'observe dans la %bglace%w." },
{ "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w." },
{ "Thank you %b@%w!&But your item is in another castle!",
"Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!",
"Merci %b@%w!&Mais ton objet est dans un autre&château!" },
{ "%bFREEZE%w! Don't move!",
" Kalt. Kalt. Kälter. %bEISKALT%w!",
"J'espère que ça ne te fait ni chaud, ni&%bfroid%w." },
};
void CreateIceTrapRandoMessages() { void CreateIceTrapRandoMessages() {
CustomMessage IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = {
{ "You are a %bFOOL%w!", "Du bist ein %bDUMMKOPF%w!", "%bPauvre fou%w..." },
{ "You are a %bFOWL%w!", "Du bist eine %bFrostbeule%w!", "Tu es un %bglaçon%w, Harry!" },
{ "%bFOOL%w!", "%bDUMMKOPF%w!", "%bSot%w que tu es." },
{ "You just got %bPUNKED%w!", "Du wurdest %beiskalt%w erwischt!", "Ça me %bglace%w le sang!" },
{ "Stay %bfrosty%w, @.", "Es läuft dir %beiskalt%w den Rücken&hinunter, @.", "%bReste au frais%w, @." },
{ "Take a %bchill pill%w, @.", "Bleib %bcool%w, @.", "Et c'est la douche %bfroide%w!" },
{ "%bWinter%w is coming.", "Der %bWinter%w naht.", "L'%bhiver%w vient." },
{ "%bICE%w to see you, @.", "Alles %bcool%w im Pool?", "%bGlacier%w!" },
{ "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.", "%bAbkühlung gefällig%w?",
"%Ça en jette un %bfroid%w." },
{ "It's a %bcold day%w in the Evil Realm.", "Es ist ein %kalter%w Tag im Herzen&von Hyrule.",
"Est-ce que tu as déjà eu des sueurs&%bfroides%w?" },
{ "Getting %bcold feet%w?", "Bekommst du etwa %bkalte%w Füße?",
"La vengeance est un plat qui se mange&%bfroid%w!" },
{ "Say hello to the %bZoras%w for me!", "Sag den %bZoras%w viele Grüße von mir!",
"Dit bonjour aux %bZoras%w pour moi!" },
{ "Can you keep a %bcool head%w?", "Bewahre einen %bkühlen%w! Kopf.",
"Il faut parfois savoir garder la tête&%bfroide%w!" },
{ "Ganondorf used %bIce Trap%w!&It's super effective!",
"Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!",
"Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" },
{ "Allow me to break the %bice%w!", "Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!",
"Laisse moi briser la %bglace%w!" },
{ "%bCold pun%w.", "%bEiskalt%w lässt du meine Seele&erfrier'n.",
"Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" },
{ "The %bTitanic%w would be scared of you,&@.", "Die %bTitanic%w hätte Angst vor dir,&@.",
"Le %bTitanic%w aurait peur de toi,&@." },
{ "Oh no!", "Oh nein!", "Oh non!" },
{ "What killed the dinosaurs?&The %bICE%w age!", "Was die Dinosaurier getötet hat?&Die %bEiszeit%w!",
"Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%w!" },
{ "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.",
"Nachts ist es %bkälter%w als draußen.",
"L'imbécile réfléchit uniquement quand il&s'observe dans la %bglace%w." },
{ "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w." },
{ "Thank you %b@%w!&But your item is in another castle!",
"Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!",
"Merci %b@%w!&Mais ton objet est dans un autre&château!" },
{ "%bFREEZE%w! Don't move!", " Kalt. Kalt. Kälter. %bEISKALT%w!",
"J'espère que ça ne te fait ni chaud, ni&%bfroid%w." },
};
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* 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,
"This year for Christmas, all&you get is %BCOAL%w!", 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!",
"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 };
{
"Are you the one they call %g@%w?^You look really weird for %rDarunia's kid.%w&Are you adopted?",
"Du bist also der, den sie @ nennen?^Du siehst nicht aus als wärst du&%rDarunias Kind.%w Bist du adoptiert?",
"C'est toi qu'on appelle %g@%w?^Tu es vraiment bizarre pour être&le %rfils du Chef%w. Tu as été adopté?",
},
{
"Thank Hylia! I was so worried about&when my teacher would let me get&out of detention.^I gotta go home and see my parents.",
"Ich wollte nur dieses Ding hier wieder&in seine Truhe zurücklegen, weil...^...gehört mir ja eigentlich nicht,&weißt du?^Doch dann ging plötzlich dieses&Tor hinter mir zu.&Danke für die Rettung.",
"Par les déesses!&Mon Frère?!&C'est bien toi?&Comment ça on ne se connaît pas?^Tu trouves vraiment que je&ressemble à n'importe quel Goron?",
},
{
"How long has it been, do you know?^%r{{days}}%w days!?^Oh no, and it's %r\x1F%w?&I have to check on my cake!!",
"Weißt du zufällig, wie viele Tage&vergangen sind?^%r{{days}}%w Tage!?^Oh je, und es ist %r\x1F%w Uhr? Ich&muss dringend nach meinem Kuchen&sehen!!!",
"Cela fait combien de temps que&je suis enfermé ici?&Non mais je ne vais pas crier.^COMBIEN?! %r{{days}}%w JOURS!?^En plus il est %r\x1F%w...&Il faut vraiment que je rentre...",
},
{
//0x39C7 - ganon laugh
"\x12\x39\xC7You fell into my %rtrap!%w&Foolish boy, it was me, Ganondorf!!!^...whoa, where am I?&What happened?^Weird.",
"\x12\x39\xC7""Du bist mir in die %rFalle%w gegangen!&Du Narr, ich bin es, %rGanondorf%w!!!^...Huch? Wo bin ich? Was ist passiert?^Seltsam...",
"\x12\x39\xC7Tu es tombé dans mon %rpiège%w!&Tu croyais que j'étais un Goron mais,&c'était moi! %rGanondorf%w!^...Hein? Où suis-je?&Que s'est-il passé?",
},
{
"Thanks, but I don't know if I wanna go&just yet...^Hmm...^...^...^...^...^...maybe I can come back later.&Bye bye.",
"Danke für die Rettung, aber&eigentlich finde ich es hier ganz&nett...^Hmm...^...^...^...^...^...Naja, ich kann ja jederzeit&wiederkommen. Man sieht sich.",
"Merci, mais je me sens plus en&sécurité ici...^...^...^...^...^Hmm...^...Tout compte fait, je vais y aller.&A plus tard.",
},
{
"Do you know about %b\x9f%w?&It's this weird symbol that's been&in my dreams lately...^Apparently, you pressed it %b{{a_btn}}%w times.^Wow.",
"Weißt du über %b\x9f%w bescheid?&Es sind Symbole, die mir&in letzter Zeit öfter in&meinen Träumen erschienen sind...^Es scheint, dass du sie schon&%b{{a_btn}}%w mal betätigt hast.^Faszinierend...",
"Tu as déjà entendu parler du&symbole %b\x9f%w?&C'est un symbole bizarre qui est&apparu dans mes rêves dernièrement...^Apparemment, tu as appuyé dessus&%b{{a_btn}}%w fois.^Wow..."
},
{
"\x13\x1A""Boy, you must be hot!&Get yourself a bottle of&%rLon Lon Milk%w right away and cool&down, for only %g30%w rupees!",
"\x13\x1A""Hey, ist dir nicht zu warm?&Besorg dir doch eine Flasche&%rLon Lon-Milch%w, um dich&abzukühlen.^Kostet dich auch nur %g30%w Rubine!",
"\x13\x1A""Woah! Tu dois avoir chaud!&Tu savais que tu pouvais acheter&du %rLait de Lon Lon%w pour&seulement %g30 rubis%w?^Il n'y a rien de mieux pour s'hydrater!",
},
{
"In that case, I'll help you out!^They say that %rthe thing you're&looking for%w can only be found%g when&you're not looking for it.%w^Hope that helps!",
"Pass auf, ich geb dir einen Tipp!^Man sagt, man findet %rdas was&man sucht%w nur, und wirklich nur&dann, %gwenn man gerade nicht danach&sucht%w.^Du kannst mich jederzeit wieder für&mehr hilfreiche Tipps aufsuchen!",
"Dans ce cas, je vais t'aider!&On dit que l'objet que tu cherches&ne peut être trouvé que lorsque&tu ne le cherches pas.",
},
{
"I dunno why I was thrown in here,&truth be told.&I'm just a %g\"PR\"%w person.",
"Wat weiß'n ich, wieso ich hier&eingepfercht wurd. Ich mach&doch nur %g\"Pull&Requests\"%w.",
"Je ne sais pas comment on m'a jeté&ici. Il faut croire que je dors comme&une pierre.",
},
};
static int goronIDs[9] = {0x3052, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070};
void CreateFireTempleGoronMessages() { void CreateFireTempleGoronMessages() {
CustomMessage FireTempleGoronMessages[NUM_GORON_MESSAGES] = {
{
"Are you the one they call %g@%w?^You look really weird for %rDarunia's kid.%w&Are you adopted?",
"Du bist also der, den sie @ nennen?^Du siehst nicht aus als wärst du&%rDarunias Kind.%w Bist du "
"adoptiert?",
"C'est toi qu'on appelle %g@%w?^Tu es vraiment bizarre pour être&le %rfils du Chef%w. Tu as été adopté?",
},
{
"Thank Hylia! I was so worried about&when my teacher would let me get&out of detention.^I gotta go home "
"and see my parents.",
"Ich wollte nur dieses Ding hier wieder&in seine Truhe zurücklegen, weil...^...gehört mir ja eigentlich "
"nicht,&weißt du?^Doch dann ging plötzlich dieses&Tor hinter mir zu.&Danke für die Rettung.",
"Par les déesses!&Mon Frère?!&C'est bien toi?&Comment ça on ne se connaît pas?^Tu trouves vraiment que "
"je&ressemble à n'importe quel Goron?",
},
{
"How long has it been, do you know?^%r{{days}}%w days!?^Oh no, and it's %r\x1F%w?&I have to check on my "
"cake!!",
"Weißt du zufällig, wie viele Tage&vergangen sind?^%r{{days}}%w Tage!?^Oh je, und es ist %r\x1F%w Uhr? "
"Ich&muss dringend nach meinem Kuchen&sehen!!!",
"Cela fait combien de temps que&je suis enfermé ici?&Non mais je ne vais pas crier.^COMBIEN?! %r{{days}}%w "
"JOURS!?^En plus il est %r\x1F%w...&Il faut vraiment que je rentre...",
},
{
// 0x39C7 - ganon laugh
"\x12\x39\xC7You fell into my %rtrap!%w&Foolish boy, it was me, Ganondorf!!!^...whoa, where am I?&What "
"happened?^Weird.",
"\x12\x39\xC7"
"Du bist mir in die %rFalle%w gegangen!&Du Narr, ich bin es, %rGanondorf%w!!!^...Huch? Wo bin ich? Was ist "
"passiert?^Seltsam...",
"\x12\x39\xC7Tu es tombé dans mon %rpiège%w!&Tu croyais que j'étais un Goron mais,&c'était moi! "
"%rGanondorf%w!^...Hein? Où suis-je?&Que s'est-il passé?",
},
{
"Thanks, but I don't know if I wanna go&just yet...^Hmm...^...^...^...^...^...maybe I can come back "
"later.&Bye bye.",
"Danke für die Rettung, aber&eigentlich finde ich es hier ganz&nett...^Hmm...^...^...^...^...^...Naja, ich "
"kann ja jederzeit&wiederkommen. Man sieht sich.",
"Merci, mais je me sens plus en&sécurité ici...^...^...^...^...^Hmm...^...Tout compte fait, je vais y "
"aller.&A plus tard.",
},
{ "Do you know about %b\x9f%w?&It's this weird symbol that's been&in my dreams lately...^Apparently, you "
"pressed it %b{{a_btn}}%w times.^Wow.",
"Weißt du über %b\x9f%w bescheid?&Es sind Symbole, die mir&in letzter Zeit öfter in&meinen Träumen "
"erschienen sind...^Es scheint, dass du sie schon&%b{{a_btn}}%w mal betätigt hast.^Faszinierend...",
"Tu as déjà entendu parler du&symbole %b\x9f%w?&C'est un symbole bizarre qui est&apparu dans mes rêves "
"dernièrement...^Apparemment, tu as appuyé dessus&%b{{a_btn}}%w fois.^Wow..." },
{
"\x13\x1A"
"Boy, you must be hot!&Get yourself a bottle of&%rLon Lon Milk%w right away and cool&down, for only %g30%w "
"rupees!",
"\x13\x1A"
"Hey, ist dir nicht zu warm?&Besorg dir doch eine Flasche&%rLon Lon-Milch%w, um dich&abzukühlen.^Kostet "
"dich auch nur %g30%w Rubine!",
"\x13\x1A"
"Woah! Tu dois avoir chaud!&Tu savais que tu pouvais acheter&du %rLait de Lon Lon%w pour&seulement %g30 "
"rubis%w?^Il n'y a rien de mieux pour s'hydrater!",
},
{
"In that case, I'll help you out!^They say that %rthe thing you're&looking for%w can only be found%g "
"when&you're not looking for it.%w^Hope that helps!",
"Pass auf, ich geb dir einen Tipp!^Man sagt, man findet %rdas was&man sucht%w nur, und wirklich nur&dann, "
"%gwenn man gerade nicht danach&sucht%w.^Du kannst mich jederzeit wieder für&mehr hilfreiche Tipps "
"aufsuchen!",
"Dans ce cas, je vais t'aider!&On dit que l'objet que tu cherches&ne peut être trouvé que lorsque&tu ne le "
"cherches pas.",
},
{
"I dunno why I was thrown in here,&truth be told.&I'm just a %g\"PR\"%w person.",
"Wat weiß'n ich, wieso ich hier&eingepfercht wurd. Ich mach&doch nur %g\"Pull&Requests\"%w.",
"Je ne sais pas comment on m'a jeté&ici. Il faut croire que je dors comme&une pierre.",
},
};
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* 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,21 +1990,19 @@ 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 "
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r{{gsCount}}&%wGoldene Skulltula-Symbole&gesammelt!", "Skulltula-Symbole&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r{{gsCount}}%w symboles en tout!" "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r{{gsCount}}%w symboles en tout!",
} TEXTBOX_TYPE_BLUE));
);
CustomMessageManager::Instance->CreateMessage( 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, "Ein %rHerzteil%w!&Du hast nun insgesamt %r{{heartPieceCount}}%w&Herteile gesammelt!",
"You got a %rHeart Piece%w!&You've collected %r{{heartPieceCount}}%w pieces&in total!", "Vous obtenez un %rQuart de&Coeur%w! Vous en avez collecté&%r{{heartPieceCount}}%w en tout!",
"Ein %rHerzteil%w!&Du hast nun insgesamt %r{{heartPieceCount}}%w&Herteile gesammelt!", TEXTBOX_TYPE_BLUE));
"Vous obtenez un %rQuart de&Coeur%w! Vous en avez collecté&%r{{heartPieceCount}}%w en tout!"
}
);
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, "Du siehst gelangweilt aus.&Willst du einen Spaziergang machen?\x1B&%gJa&Nein%w",
"You look bored. Wanna go out for a&walk?\x1B&%gYes&No%w", "Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w"));
"Du siehst gelangweilt aus.&Willst du einen Spaziergang machen?\x1B&%gJa&Nein%w",
"Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w",
}
);
CustomMessageManager::Instance->CreateMessage( 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));
} }