From 1ed45e1433ab25b7e0487df837cd1ed2fae053cc Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Sun, 17 Jul 2022 21:03:32 -0400 Subject: [PATCH] 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. --- .../custom_message/CustomMessage.cpp | 78 +++++++++++++------ .../custom_message/CustomMessage.h | 16 +++- .../Enhancements/randomizer/randomizer.cpp | 2 + soh/soh/Enhancements/randomizer/randomizer.h | 1 + .../randomizer/randomizer_custom_messages.cpp | 35 ++++++--- soh/soh/OTRGlobals.cpp | 2 +- .../actors/ovl_player_actor/z_player.c | 8 +- 7 files changed, 99 insertions(+), 43 deletions(-) diff --git a/soh/soh/Enhancements/custom_message/CustomMessage.cpp b/soh/soh/Enhancements/custom_message/CustomMessage.cpp index 80340801d..d513c8286 100644 --- a/soh/soh/Enhancements/custom_message/CustomMessage.cpp +++ b/soh/soh/Enhancements/custom_message/CustomMessage.cpp @@ -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::const_iterator result = messageTables.find(tableID); + if (result == messageTables.end()) { + return ""; + } + CustomMessageTable messageTable = result->second; + std::unordered_map::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::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() { diff --git a/soh/soh/Enhancements/custom_message/CustomMessage.h b/soh/soh/Enhancements/custom_message/CustomMessage.h index d8cfd8e0d..d3d1d8849 100644 --- a/soh/soh/Enhancements/custom_message/CustomMessage.h +++ b/soh/soh/Enhancements/custom_message/CustomMessage.h @@ -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 CustomMessageTable; + class CustomMessage { private: std::unordered_map textBoxSpecialCharacters; std::unordered_map colors; - std::unordered_map getItemMessageTable[LANGUAGE_MAX]; + std::unordered_map 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); }; \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 4c589ea48..2e47c7c80 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -21,6 +21,8 @@ std::unordered_map 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; diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index 6f928bd66..22047a9d8 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -16,6 +16,7 @@ class Randomizer { std::string ganonHintText; std::string ganonText; std::unordered_map 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); diff --git a/soh/soh/Enhancements/randomizer/randomizer_custom_messages.cpp b/soh/soh/Enhancements/randomizer/randomizer_custom_messages.cpp index 61a2559ac..62e213cda 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_custom_messages.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_custom_messages.cpp @@ -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); } \ No newline at end of file diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index d4a78fb34..2bd04a5e8 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -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) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 15ff2e7d8..4d2c6e23b 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -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),