Implements adding message tables and retrieving by an ID.

Basically, some external code can choos a unique id and create a message table for itself. The idea is that modders can hook into this as well so they can get their own message table and don't have to worry about conflicting with the base game's textIDs. I have also implemented this setup for Randomizer so that the pre-filled bottles (which didn't exist in the original outside of Ruto's letter) have unique text as compared to the text for the item they are filled with.
This commit is contained in:
Christopher Leggett 2022-07-17 21:03:32 -04:00
parent ee1270f346
commit 1ed45e1433
No known key found for this signature in database
GPG Key ID: 7093AE5FF7037D79
7 changed files with 99 additions and 43 deletions

View File

@ -17,6 +17,8 @@ CustomMessage::CustomMessage() {
CustomMessage::~CustomMessage() {
this->textBoxSpecialCharacters.clear();
this->colors.clear();
this->messageTables.clear();
}
void CustomMessage::ReplaceSpecialCharacters(std::string& string) {
@ -44,33 +46,61 @@ void CustomMessage::ReplaceColors(std::string& string) {
}
}
void CustomMessage::CreateGetItemMessage(GetItemID giid, ItemID iid, std::string messages[LANGUAGE_MAX]) {
for (int i = 0; i < LANGUAGE_MAX; i++) {
if (!(messages[i].empty())) {
std::string message = messages[i];
std::string formattedMessage = ITEM_OBTAINED(iid) + message;
size_t start_pos = 0;
std::replace(formattedMessage.begin(), formattedMessage.end(), '&', NEWLINE()[0]);
while ((start_pos = formattedMessage.find('^', start_pos)) != std::string::npos) {
formattedMessage.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid));
start_pos += 3;
}
std::replace(formattedMessage.begin(), formattedMessage.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(formattedMessage);
ReplaceColors(formattedMessage);
formattedMessage += MESSAGE_END();
this->getItemMessageTable[i].emplace(giid, formattedMessage);
}
void CustomMessage::FormatMessage(std::string& message, ItemID iid) {
message.insert(0, ITEM_OBTAINED(iid));
size_t start_pos = 0;
std::replace(message.begin(), message.end(), '&', NEWLINE()[0]);
while ((start_pos = message.find('^', start_pos)) != std::string::npos) {
message.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid));
start_pos += 3;
}
std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(message);
ReplaceColors(message);
message += MESSAGE_END();
}
bool CustomMessage::CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messages) {
FormatMessage(messages.english, iid);
FormatMessage(messages.german, iid);
FormatMessage(messages.french, iid);
const uint16_t textID = giid;
auto result = messageTables.find(tableID);
if (result == messageTables.end()) {
return false;
}
auto& messageTable = result->second;
auto success = messageTable.emplace(textID, messages);
return success.second;
}
std::string CustomMessage::RetrieveMessage(std::string tableID, uint16_t textID) {
std::unordered_map<std::string, CustomMessageTable>::const_iterator result = messageTables.find(tableID);
if (result == messageTables.end()) {
return "";
}
CustomMessageTable messageTable = result->second;
std::unordered_map<uint16_t, CustomMessageEntry>::const_iterator message_pair = messageTable.find(textID);
if (message_pair == messageTable.end()) {
return "";
}
CustomMessageEntry messages = message_pair->second;
switch (gSaveContext.language) {
case LANGUAGE_FRA:
return messages.french;
case LANGUAGE_GER:
return messages.german;
case LANGUAGE_ENG:
default:
return messages.english;
}
}
std::string CustomMessage::RetrieveGetItemMessage(GetItemID giid) {
std::unordered_map<GetItemID, std::string>::const_iterator result =
getItemMessageTable[gSaveContext.language].find(giid);
if (result == getItemMessageTable[gSaveContext.language].end()) {
return "";
}
return result->second;
bool CustomMessage::AddCustomMessageTable(std::string tableID) {
CustomMessageTable newMessageTable;
return messageTables.emplace(tableID, newMessageTable).second;
}
std::string CustomMessage::MESSAGE_END() {

View File

@ -12,14 +12,23 @@
#define QM_YELLOW 0x46
#define QM_BLACK 0x47
typedef struct {
std::string english;
std::string german;
std::string french;
} CustomMessageEntry;
typedef std::unordered_map<uint16_t, CustomMessageEntry> CustomMessageTable;
class CustomMessage {
private:
std::unordered_map<std::string, char> textBoxSpecialCharacters;
std::unordered_map<std::string, char> colors;
std::unordered_map<GetItemID, std::string> getItemMessageTable[LANGUAGE_MAX];
std::unordered_map<std::string, CustomMessageTable> messageTables;
void ReplaceSpecialCharacters(std::string &string);
void ReplaceColors(std::string& string);
void FormatMessage(std::string& message, ItemID iid);
std::string MESSAGE_END();
std::string ITEM_OBTAINED(uint8_t x);
@ -34,6 +43,7 @@ class CustomMessage {
CustomMessage();
~CustomMessage();
void CreateGetItemMessage(GetItemID giid, ItemID iid, std::string messages[LANGUAGE_MAX]);
std::string RetrieveGetItemMessage(GetItemID giid);
bool CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messages);
std::string RetrieveMessage(std::string tableID, uint16_t textID);
bool AddCustomMessageTable(std::string tableID);
};

View File

@ -21,6 +21,8 @@ std::unordered_map<uint8_t, Sprite> gSeedTextures;
u8 generated;
const std::string Randomizer::customMessageTableID = "Randomizer";
Randomizer::Randomizer() {
Sprite bowSprite = { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 };
gSeedTextures[0] = bowSprite;

View File

@ -16,6 +16,7 @@ class Randomizer {
std::string ganonHintText;
std::string ganonText;
std::unordered_map<RandomizerSettingKey, u8> randoSettings;
static const std::string customMessageTableID;
GetItemID GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId);
GetItemID GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, GetItemID ogItemId);
void ParseRandomizerSettingsFile(const char* spoilerFileName);

View File

@ -1,25 +1,38 @@
#include "randomizer.h"
#include "soh/Enhancements/custom_message/CustomMessage.h"
#define MESSAGES(eng, ger, fra) (new std::string[]{eng, ger, fra})
#define MESSAGES(eng, ger, fra) {eng, ger, fra}
void Randomizer::CreateCustomMessages() {
CustomMessage* customMessage = CustomMessage::Instance;
customMessage->AddCustomMessageTable(Randomizer::customMessageTableID);
customMessage->CreateGetItemMessage(
GI_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE,
Randomizer::customMessageTableID, GI_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE,
MESSAGES("You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!", "", ""));
customMessage->CreateGetItemMessage(
GI_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE,
MESSAGES("You got a %rBig Poe in a bottle%w!&Sell it to the Ghost Shop!", "", ""));
Randomizer::customMessageTableID, GI_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE,
MESSAGES("You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!", "", ""));
customMessage->CreateGetItemMessage(
GI_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE,
Randomizer::customMessageTableID, GI_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE,
MESSAGES("You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!", "", ""));
customMessage->CreateGetItemMessage(
GI_BOTTLE_WITH_FISH, ITEM_FISH,
MESSAGES("You got a %rFish in a bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!", "",
""));
Randomizer::customMessageTableID, GI_BOTTLE_WITH_FISH, ITEM_FISH,
MESSAGES("You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!", "", ""));
customMessage->CreateGetItemMessage(
Randomizer::customMessageTableID, GI_BOTTLE_WITH_BUGS, ITEM_BUG,
{ "You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!", "", "" });
customMessage->CreateGetItemMessage(
Randomizer::customMessageTableID, GI_BOTTLE_WITH_FAIRY, ITEM_FAIRY,
{ "You got a %rFairy in a Bottle%w!&Use it wisely!", "", "" });
customMessage->CreateGetItemMessage(
Randomizer::customMessageTableID, GI_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
{ "You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!", "", "" });
customMessage->CreateGetItemMessage(
Randomizer::customMessageTableID, GI_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN,
{ "You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!", "", "" });
customMessage->CreateGetItemMessage(
Randomizer::customMessageTableID, GI_BOTTLE_WITH_POE, ITEM_POE,
{ "You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this...", "", "" });
}
std::string Randomizer::GetCustomGetItemMessage(GetItemID giid) {
@ -27,5 +40,5 @@ std::string Randomizer::GetCustomGetItemMessage(GetItemID giid) {
return "Not Randomized.";
}
return CustomMessage::Instance->RetrieveGetItemMessage(giid);
return CustomMessage::Instance->RetrieveMessage(Randomizer::customMessageTableID, giid);
}

View File

@ -1521,9 +1521,9 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
const int maxBufferSize = sizeof(font->msgBuf);
if (gSaveContext.n64ddFlag) {
if (textId == 0xF8) {
font->charTexBuf[0] = 0x23;
if (msgCtx->msgLength = font->msgLength = Randomizer_GetCustomGetItemMessage(
(GetItemID)GET_PLAYER(globalCtx)->getItemId, buffer, maxBufferSize)) {
font->charTexBuf[0] = 0x23;
return true;
} else {
switch (gSaveContext.language) {

View File

@ -652,13 +652,13 @@ static GetItemEntry sGetItemTable[] = {
GET_ITEM(ITEM_DOUBLE_MAGIC, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_DOUBLE_DEFENSE, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xE9, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, 0x44, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x46, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, 0xF8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF8, 0x80, CHEST_ANIM_LONG),