diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 67d5db850..5342374f0 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -215,6 +215,13 @@ set(Header_Files__soh__Enhancements__randomizer__3drando ) source_group("Header Files\\soh\\Enhancements\\randomizer\\3drando" FILES ${Header_Files__soh__Enhancements__randomizer__3drando}) +set(Header_Files__soh__Enhancements__custom_message + "soh/Enhancements/custom-message/CustomMessageTypes.h" + "soh/Enhancements/custom-message/CustomMessageManager.h" +) + +source_group("Header Files\\soh\\Enhancements\\custom-message" FILES ${Header_Files__soh__Enhancements__custom_message}) + set(Source_Files__soh "soh/GbiWrap.cpp" "soh/OTRAudio.h" @@ -326,6 +333,12 @@ set(Source_Files__soh__Enhancements__randomizer__3drando__location_access ) source_group("Source Files\\soh\\Enhancements\\randomizer\\3drando\\location_access" FILES ${Source_Files__soh__Enhancements__randomizer__3drando__location_access}) +set(Source_Files__soh__Enhancements__custom_message + "soh/Enhancements/custom-message/CustomMessageManager.cpp" +) + +source_group("Source Files\\soh\\Enhancements\\custom-message" FILES ${Source_Files__soh__Enhancements__custom_message}) + set(Source_Files__src__boot "src/boot/assert.c" "src/boot/boot_main.c" @@ -1534,6 +1547,7 @@ set(ALL_FILES ${Header_Files__soh__Enhancements__debugger} ${Header_Files__soh__Enhancements__randomizer} ${Header_Files__soh__Enhancements__randomizer__3drando} + ${Header_Files__soh__Enhancements__custom_message} ${Source_Files__soh} ${Source_Files__soh__Enhancements} ${Source_Files__soh__Enhancements__cosmetics} @@ -1542,6 +1556,7 @@ set(ALL_FILES ${Source_Files__soh__Enhancements__randomizer__3drando} ${Source_Files__soh__Enhancements__randomizer__3drando__hint_list} ${Source_Files__soh__Enhancements__randomizer__3drando__location_access} + ${Source_Files__soh__Enhancements__custom_message} ${Source_Files__src__boot} ${Source_Files__src__buffers} ${Source_Files__src__code} diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp new file mode 100644 index 000000000..d7dd08991 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.cpp @@ -0,0 +1,150 @@ +#include "CustomMessageManager.h" +#include + +using namespace std::literals::string_literals; + +CustomMessageManager::CustomMessageManager() { + this->textBoxSpecialCharacters = { { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 }, + { "È", 0x85 }, { "É", 0x86 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 }, + { "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, { "Ü", 0x8E }, + { "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 }, + { "ç", 0x94 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 }, + { "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, { "ù", 0x9C }, { "û", 0x9D }, + { "ü", 0x9E } }; + this->colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN }, { "b", QM_BLUE }, + { "c", QM_LBLUE }, { "p", QM_PINK }, { "y", QM_YELLOW }, { "B", QM_BLACK } }; +} + +CustomMessageManager::~CustomMessageManager() { + this->textBoxSpecialCharacters.clear(); + this->colors.clear(); + this->messageTables.clear(); +} + +void CustomMessageManager::ReplaceSpecialCharacters(std::string& string) { + // add special characters + for (auto specialCharacterPair : this->textBoxSpecialCharacters) { + size_t start_pos = 0; + std::string textBoxSpecialCharacterString = ""s; + textBoxSpecialCharacterString += specialCharacterPair.second; + while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) { + string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); + start_pos += textBoxSpecialCharacterString.length(); + } + } +} + +void CustomMessageManager::ReplaceColors(std::string& string) { + for (auto colorPair : colors) { + std::string textToReplace = "%"; + textToReplace += colorPair.first; + size_t start_pos = 0; + while ((start_pos = string.find(textToReplace)) != std::string::npos) { + string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second)); + start_pos += textToReplace.length(); + } + } +} + +void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) { + message.insert(0, ITEM_OBTAINED(iid)); + size_t start_pos = 0; + std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); + while ((start_pos = message.find('^', start_pos)) != std::string::npos) { + message.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid)); + start_pos += 3; + } + std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); + ReplaceSpecialCharacters(message); + ReplaceColors(message); + message += MESSAGE_END(); +} + +void CustomMessageManager::FormatCustomMessage(std::string& message) { + size_t start_pos = 0; + std::replace(message.begin(), message.end(), '&', NEWLINE()[0]); + std::replace(message.begin(), message.end(), '^', WAIT_FOR_INPUT()[0]); + std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]); + ReplaceSpecialCharacters(message); + ReplaceColors(message); + message += MESSAGE_END(); +} + +bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages) { + auto foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return false; + } + auto& messageTable = foundMessageTable->second; + auto messageInsertResult = messageTable.emplace(textID, messages); + return messageInsertResult.second; +} + + + +bool CustomMessageManager::CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messageEntry) { + FormatCustomMessage(messageEntry.english, iid); + FormatCustomMessage(messageEntry.german, iid); + FormatCustomMessage(messageEntry.french, iid); + const uint16_t textID = giid; + return InsertCustomMessage(tableID, textID, messageEntry); +} + +bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messageEntry) { + FormatCustomMessage(messageEntry.english); + FormatCustomMessage(messageEntry.german); + FormatCustomMessage(messageEntry.french); + return InsertCustomMessage(tableID, textID, messageEntry); +} + +CustomMessageEntry CustomMessageManager::RetrieveMessage(std::string tableID, uint16_t textID) { + std::unordered_map::const_iterator foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return NULL_CUSTOM_MESSAGE; + } + CustomMessageTable messageTable = foundMessageTable->second; + std::unordered_map::const_iterator foundMessage = messageTable.find(textID); + if (foundMessage == messageTable.end()) { + return NULL_CUSTOM_MESSAGE; + } + CustomMessageEntry message = foundMessage->second; + return message; +} + +bool CustomMessageManager::ClearMessageTable(std::string tableID) { + auto foundMessageTable = messageTables.find(tableID); + if (foundMessageTable == messageTables.end()) { + return false; + } + auto& messageTable = foundMessageTable->second; + messageTable.clear(); +} + +bool CustomMessageManager::AddCustomMessageTable(std::string tableID) { + CustomMessageTable newMessageTable; + return messageTables.emplace(tableID, newMessageTable).second; +} + +std::string CustomMessageManager::MESSAGE_END() { + return "\x02"s; +} + +std::string CustomMessageManager::ITEM_OBTAINED(uint8_t x) { + return "\x13"s + char(x); +} + +std::string CustomMessageManager::NEWLINE() { + return "\x01"s; +} + +std::string CustomMessageManager::COLOR(uint8_t x) { + return "\x05"s + char(x); +} + +std::string CustomMessageManager::WAIT_FOR_INPUT() { + return "\x04"s; +} + +std::string CustomMessageManager::PLAYER_NAME() { + return "\x0F"s; +} diff --git a/soh/soh/Enhancements/custom-message/CustomMessageManager.h b/soh/soh/Enhancements/custom-message/CustomMessageManager.h new file mode 100644 index 000000000..0681073b8 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageManager.h @@ -0,0 +1,133 @@ +#pragma once +#include +#include +#include "../../../include/z64item.h" + +#undef MESSAGE_END + +#define QM_WHITE 0x00 +#define QM_RED 0x41 +#define QM_GREEN 0x42 +#define QM_BLUE 0x43 +#define QM_LBLUE 0x44 +#define QM_PINK 0x45 +#define QM_YELLOW 0x46 +#define QM_BLACK 0x47 + +#ifndef MESSAGE_DATA_STATIC_H + +typedef enum { + /* 0 */ TEXTBOX_TYPE_BLACK, + /* 1 */ TEXTBOX_TYPE_WOODEN, + /* 2 */ TEXTBOX_TYPE_BLUE, + /* 3 */ TEXTBOX_TYPE_OCARINA, + /* 4 */ TEXTBOX_TYPE_NONE_BOTTOM, + /* 5 */ TEXTBOX_TYPE_NONE_NO_SHADOW, + /* 11 */ TEXTBOX_TYPE_CREDITS = 11 +} TextBoxType; + +typedef enum { + /* 0 */ TEXTBOX_BG_CROSS +} TextBoxBackground; + +typedef enum { + /* 0 */ TEXTBOX_POS_VARIABLE, + /* 1 */ TEXTBOX_POS_TOP, + /* 2 */ TEXTBOX_POS_MIDDLE, + /* 3 */ TEXTBOX_POS_BOTTOM +} TextBoxPosition; + +#endif + +typedef struct { + TextBoxType textBoxType; + TextBoxPosition textBoxPos; + std::string english; + std::string german; + std::string french; +} CustomMessageEntry; + +// Message Entry without the text type and position, useful for when +// you need an array of these to loop over for registration +// that will all have the same textbox type and position. +typedef struct { + std::string english; + std::string german; + std::string french; +} CustomMessageMinimal; + +#define NULL_CUSTOM_MESSAGE \ + { (TextBoxType)(-1), (TextBoxPosition)(-1), "", "", "" } + +typedef std::unordered_map CustomMessageTable; + +class CustomMessageManager { + private: + std::unordered_map textBoxSpecialCharacters; + std::unordered_map colors; + std::unordered_map messageTables; + + void ReplaceSpecialCharacters(std::string &string); + void ReplaceColors(std::string& string); + bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); + + std::string MESSAGE_END(); + std::string ITEM_OBTAINED(uint8_t x); + std::string NEWLINE(); + std::string COLOR(uint8_t x); + std::string WAIT_FOR_INPUT(); + std::string PLAYER_NAME(); + + public: + static CustomMessageManager* Instance; + + CustomMessageManager(); + ~CustomMessageManager(); + + /* + Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to + the provided iid (itemID) at the beginning of each page of the textbox. + */ + bool CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messages); + + /* + Formats the provided Custom Message Entry and inserts it into the table with the provided tableID, + with the provided textID as its key. + */ + bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages); + + /* + Retrieves a message from the table with id tableID with the provided textID. + Returns a NULL_CUSTOM_MESSAGE if the message or table does not exist. + */ + CustomMessageEntry RetrieveMessage(std::string tableID, uint16_t textID); + + /* + Empties out the message table identified by tableID. + Returns true if successful and false if not (for instance + if a table with the provided tableID does not exist). + */ + bool ClearMessageTable(std::string tableID); + + /* + Creates an empty CustomMessageTable accessible at the provided + tableID, returns true if creation was successful and false + if not. + */ + bool AddCustomMessageTable(std::string tableID); + + /* + Replaces special characters and certain symbols with control codes + & for newline, ^ for wait-for-input, and @ for the player name, + as well as % for colors (i.e. %r for red and %w for white). + */ + void FormatCustomMessage(std::string& message, ItemID iid); + + /* + Replaces special characters and certain symbols with control codes + & for newline, ^ for wait-for-input, and @ for the player name, + as well as % for colors (i.e. %r for red and %w for white). + */ + void FormatCustomMessage(std::string& message); +}; diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h new file mode 100644 index 000000000..d2f30f123 --- /dev/null +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -0,0 +1,33 @@ +#pragma once + +typedef enum { + TEXT_GS_NO_FREEZE = 0xB4, + TEXT_GS_FREEZE = 0xB5, + TEXT_RANDOMIZER_CUSTOM_ITEM = 0xF8, + TEXT_SCRUB_POH = 0x10A2, + TEXT_SCRUB_STICK_UPGRADE = 0x10DC, + TEXT_SCRUB_NUT_UPGRADE = 0x10DD, + TEXT_RANDOMIZER_GOSSIP_STONE_HINTS = 0x2053, + TEXT_ALTAR_CHILD = 0x7040, + TEXT_ALTAR_ADULT = 0x7088, + TEXT_GANONDORF = 0x70CC, + TEXT_GANONDORF_NOHINT = 0x70CD +} TextIDs; + +#ifdef __cplusplus + +typedef struct { + GetItemID giid; + ItemID iid; + std::string english; + std::string german; + std::string french; +} GetItemMessage; + +#define GIMESSAGE(giid, iid, english, german, french) \ + { giid, iid, english, german, french } + +#define GIMESSAGE_UNTRANSLATED(giid, iid, message) \ + { giid, iid, message, message, message } + +#endif diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 38eb0ea89..eaf9f3098 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -13,13 +13,20 @@ #include "3drando/rando_main.hpp" #include #include "Lib/ImGui/imgui_internal.h" +#include +#include using json = nlohmann::json; +using namespace std::literals::string_literals; std::unordered_map gSeedTextures; u8 generated; +const std::string Randomizer::getItemMessageTableID = "Randomizer"; +const std::string Randomizer::hintMessageTableID = "RandomizerHints"; +const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs"; + Randomizer::Randomizer() { Sprite bowSprite = { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 }; gSeedTextures[0] = bowSprite; @@ -1476,6 +1483,26 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { ParseHintLocationsFile(spoilerFileName); } + CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID); + CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID); + + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.childAltarText, + gSaveContext.childAltarText, gSaveContext.childAltarText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.adultAltarText, + gSaveContext.adultAltarText, gSaveContext.adultAltarText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_GANONDORF, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonHintText, + gSaveContext.ganonHintText, gSaveContext.ganonHintText }); + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonText, + gSaveContext.ganonText, gSaveContext.ganonText }); + this->childAltarText = gSaveContext.childAltarText; this->adultAltarText = gSaveContext.adultAltarText; this->ganonHintText = gSaveContext.ganonHintText; @@ -1484,6 +1511,8 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) { for (auto hintLocation : gSaveContext.hintLocations) { if(hintLocation.check == RC_LINKS_POCKET) break; this->hintLocations[hintLocation.check] = hintLocation.hintText; + CustomMessageManager::Instance->CreateMessage( + Randomizer::hintMessageTableID, hintLocation.check, { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, hintLocation.hintText, hintLocation.hintText, hintLocation.hintText }); } } @@ -1795,57 +1824,8 @@ std::string AltarIconString(char iconChar) { std::string FormatJsonHintText(std::string jsonHint) { std::string formattedHintMessage = jsonHint; - char newLine = 0x01; - char playerName = 0x0F; - char nextBox = 0x04; - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '&', newLine); - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '^', nextBox); - std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '@', playerName); - std::unordered_map textBoxSpecialCharacters = { - {"À", 0x80 }, - {"î", 0x81 }, - {"Â", 0x82 }, - {"Ä", 0x83 }, - {"Ç", 0x84 }, - {"È", 0x85 }, - {"É", 0x86 }, - {"Ê", 0x87 }, - {"Ë", 0x88 }, - {"Ï", 0x89 }, - {"Ô", 0x8A }, - {"Ö", 0x8B }, - {"Ù", 0x8C }, - {"Û", 0x8D }, - {"Ü", 0x8E }, - {"ß", 0x8F }, - {"à", 0x90 }, - {"á", 0x91 }, - {"â", 0x92 }, - {"ä", 0x93 }, - {"ç", 0x94 }, - {"è", 0x95 }, - {"é", 0x96 }, - {"ê", 0x97 }, - {"ë", 0x98 }, - {"ï", 0x99 }, - {"ô", 0x9A }, - {"ö", 0x9B }, - {"ù", 0x9C }, - {"û", 0x9D }, - {"ü", 0x9E } - }; - - // add special characters - for (auto specialCharacterPair : textBoxSpecialCharacters) { - size_t start_pos = 0; - std::string textBoxSpecialCharacterString = ""; - textBoxSpecialCharacterString += specialCharacterPair.second; - while((start_pos = formattedHintMessage.find(specialCharacterPair.first, start_pos)) != std::string::npos) { - formattedHintMessage.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString); - start_pos += textBoxSpecialCharacterString.length(); - } - } + CustomMessageManager::Instance->FormatCustomMessage(formattedHintMessage); // add icons to altar text for (char iconChar : {'0', '1', '2', '3', '4', '5', '6', '7', '8', 'o', 'c', 'i', 'l', 'b', 'L', 'k'}) { @@ -2406,10 +2386,6 @@ std::string Randomizer::GetGanonHintText() const { return ganonHintText; } -std::string Randomizer::GetHintFromCheck(RandomizerCheck check) { - return this->hintLocations[check]; -} - u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) { return this->randoSettings[randoSettingKey]; } @@ -4726,9 +4702,64 @@ void DrawRandoEditor(bool& open) { ImGui::End(); }*/ +void CreateGetItemMessages(std::vector messageEntries) { + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; + customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID); + for (GetItemMessage messageEntry : messageEntries) { + customMessageManager->CreateGetItemMessage(Randomizer::getItemMessageTableID, messageEntry.giid, messageEntry.iid, + { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + messageEntry.english, messageEntry.german, + messageEntry.french }); + } +} + +void CreateScrubMessages() { + CustomMessageManager* customMessageManager = CustomMessageManager::Instance; + customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID); + const std::vector prices = { 10, 40 }; + for (u8 price : prices) { + customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price, + { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, + "\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" + + std::to_string(price) + " Rupees%w it is!\x07\x10\xA3", + // RANDTODO: Translate the below string to German. + "\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" + + std::to_string(price) + " Rupees%w it is!\x07\x10\xA3", + "\x12\x38\x82J'abandonne! Tu veux bien m'acheter&un %gobjet mystérieux%w?&Ça fera %r" + + std::to_string(price) + " Rubis%w!\x07\x10\xA3" + }); + } +} + +void Randomizer::CreateCustomMessages() { + // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED + // with GIMESSAGE(getItemID, itemID, english, german, french). + const std::vector getItemMessages = { + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, + "You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE, + "You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE, + "You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_FISH, ITEM_FISH, + "You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BUGS, ITEM_BUG, + "You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, + "You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN, + "You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!"), + GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_POE, ITEM_POE, + "You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this..."), + }; + CreateGetItemMessages(getItemMessages); + CreateScrubMessages(); +} void InitRando() { SohImGui::AddWindow("Randomizer", "Randomizer Settings", DrawRandoEditor); + Randomizer::CreateCustomMessages(); } extern "C" { diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index f4101e8fd..1994cfdc4 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -4,7 +4,8 @@ #include #include "../../../include/ultra64.h" #include "../../../include/z64item.h" -#include "soh/Enhancements/randomizer/randomizerTypes.h" +#include +#include class Randomizer { private: @@ -25,6 +26,10 @@ class Randomizer { Randomizer(); ~Randomizer(); + static const std::string getItemMessageTableID; + static const std::string hintMessageTableID; + static const std::string scrubMessageTableID; + static Sprite* GetSeedTexture(uint8_t index); s16 GetItemModelFromId(s16 itemId); s32 GetItemIDFromGetItemID(s32 getItemId); @@ -38,9 +43,9 @@ class Randomizer { std::string GetAdultAltarText() const; std::string GetGanonText() const; std::string GetGanonHintText() const; - std::string GetHintFromCheck(RandomizerCheck check); GetItemID GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); GetItemID GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum); + static void CreateCustomMessages(); }; #ifdef __cplusplus diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 48ecceb07..ba61805f0 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -32,7 +32,6 @@ #include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/debugconsole.h" #include "Enhancements/debugger/debugger.h" -#include "Enhancements/randomizer/randomizer.h" #include #include "Enhancements/n64_weird_frame_data.inc" #include "soh/frame_interpolation.h" @@ -40,6 +39,7 @@ #include "macros.h" #include #include "Hooks.h" +#include #ifdef __APPLE__ #include @@ -52,9 +52,12 @@ #endif #include +#include +#include OTRGlobals* OTRGlobals::Instance; SaveManager* SaveManager::Instance; +CustomMessageManager* CustomMessageManager::Instance; OTRGlobals::OTRGlobals() { context = Ship::GlobalCtx2::CreateInstance("Ship of Harkinian"); @@ -171,6 +174,7 @@ extern "C" void InitOTR() { #endif OTRGlobals::Instance = new OTRGlobals(); SaveManager::Instance = new SaveManager(); + CustomMessageManager::Instance = new CustomMessageManager(); auto t = OTRGlobals::Instance->context->GetResourceManager()->LoadFile("version"); if (!t->bHasLoadError) @@ -1431,99 +1435,39 @@ extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 sceneNum, s16 actorI return OTRGlobals::Instance->gRandomizer->GetCheckFromActor(sceneNum, actorId, actorParams); } -extern "C" int CopyScrubMessage(u16 scrubTextId, char* buffer, const int maxBufferSize) { - std::string scrubText(""); - int language = CVar_GetS32("gLanguages", 0); +extern "C" CustomMessageEntry Randomizer_GetScrubMessage(u16 scrubTextId) { int price = 0; switch (scrubTextId) { - case 0x10A2: + case TEXT_SCRUB_POH: price = 10; break; - case 0x10DC: - case 0x10DD: + case TEXT_SCRUB_STICK_UPGRADE: + case TEXT_SCRUB_NUT_UPGRADE: price = 40; break; } - switch (language) { - case 0: default: - scrubText += 0x12; // add the sound - scrubText += 0x38; // sound id - scrubText += 0x82; // sound id - scrubText += "All right! You win! In return for"; - scrubText += 0x01; // newline - scrubText += "sparing me, I will sell you a"; - scrubText += 0x01; // newline - scrubText += 0x05; // change the color - scrubText += 0x42; // green - scrubText += "mysterious item"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "!"; - scrubText += 0x01; // newline - scrubText += 0x05; // change the color - scrubText += 0x41; // red - scrubText += std::to_string(price); - scrubText += price > 1 ? " Rupees" : " Rupee"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += " it is!"; - scrubText += 0x07; // go to a new message - scrubText += 0x10; // message id - scrubText += 0xA3; // message id - break; - case 2: - scrubText += 0x12; // add the sound - scrubText += 0x38; // sound id - scrubText += 0x82; // sound id - scrubText += "J'abandonne! Tu veux bien m'acheter"; - scrubText += 0x01; // newline - scrubText += "un "; - scrubText += 0x05; // change the color - scrubText += 0x42; // green - scrubText += "objet myst\x96rieux"; - //scrubText += "; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "?"; - scrubText += 0x01; // newline - scrubText += "\x84"; - scrubText += "a fera "; - scrubText += 0x05; // change the color - scrubText += 0x41; // red - scrubText += std::to_string(price) + " Rubis"; - scrubText += 0x05; // change the color - scrubText += 0x40; // white - scrubText += "!"; - scrubText += 0x07; // go to a new message - scrubText += 0x10; // message id - scrubText += 0xA3; // message id - break; - } - - return CopyStringToCharBuffer(scrubText, buffer, maxBufferSize); + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, price); } -extern "C" int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize) { - const std::string& altarText = (LINK_IS_ADULT) ? OTRGlobals::Instance->gRandomizer->GetAdultAltarText() - : OTRGlobals::Instance->gRandomizer->GetChildAltarText(); - return CopyStringToCharBuffer(altarText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetAltarMessage() { + return (LINK_IS_ADULT) + ? CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT) + : CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD); } -extern "C" int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize) { - const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonText(); - return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetGanonText() { + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT); } -extern "C" int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize) { - const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonHintText(); - return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize); +extern "C" CustomMessageEntry Randomizer_GetGanonHintText() { + return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF); } -extern "C" int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize) { - // we don't want to make a copy of the std::string returned from GetHintFromCheck +extern "C" CustomMessageEntry Randomizer_GetHintFromCheck(RandomizerCheck check) { + // we don't want to make a copy of the std::string returned from GetHintFromCheck // so we're just going to let RVO take care of it - const std::string& hintText = OTRGlobals::Instance->gRandomizer->GetHintFromCheck(check); - return CopyStringToCharBuffer(hintText, buffer, maxBufferSize); + const CustomMessageEntry hintText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, check); + return hintText; } extern "C" s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) { @@ -1542,3 +1486,89 @@ extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomize extern "C" bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId) { return gSaveContext.n64ddFlag && Randomizer_GetItemIdFromKnownCheck(randomizerCheck, ogId) == GI_ICE_TRAP; } + +extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(GetItemID giid, char* buffer, const int maxBufferSize) { + const CustomMessageEntry getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid); + return getItemText; +} + +extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + uint16_t textId = msgCtx->textId; + Font* font = &msgCtx->font; + char* buffer = font->msgBuf; + const int maxBufferSize = sizeof(font->msgBuf); + CustomMessageEntry messageEntry; + if (gSaveContext.n64ddFlag) { + if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) { + messageEntry = + Randomizer_GetCustomGetItemMessage((GetItemID)GET_PLAYER(globalCtx)->getItemId, buffer, maxBufferSize); + } else if (textId == TEXT_RANDOMIZER_GOSSIP_STONE_HINTS && Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) != 0 && + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 1 || + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 2 && + Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) || + (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 3 && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) { + + s16 actorParams = msgCtx->talkActor->params; + + // if we're in a generic grotto + if (globalCtx->sceneNum == 62 && actorParams == 14360) { + // look for the chest in the actorlist to determine + // which grotto we're in + int numOfActorLists = + sizeof(globalCtx->actorCtx.actorLists) / sizeof(globalCtx->actorCtx.actorLists[0]); + for (int i = 0; i < numOfActorLists; i++) { + if (globalCtx->actorCtx.actorLists[i].length) { + if (globalCtx->actorCtx.actorLists[i].head->id == 10) { + // set the params for the hint check to be negative chest params + actorParams = 0 - globalCtx->actorCtx.actorLists[i].head->params; + } + } + } + } + + RandomizerCheck hintCheck = + Randomizer_GetCheckFromActor(globalCtx->sceneNum, msgCtx->talkActor->id, actorParams); + + messageEntry = Randomizer_GetHintFromCheck(hintCheck); + } else if (textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT) { + // rando hints at altar + messageEntry = Randomizer_GetAltarMessage(); + } else if (textId == TEXT_GANONDORF) { + if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) { + messageEntry = Randomizer_GetGanonText(); + } else { + messageEntry = Randomizer_GetGanonHintText(); + } + } else if (textId == TEXT_SCRUB_POH || textId == TEXT_SCRUB_STICK_UPGRADE || textId == TEXT_SCRUB_NUT_UPGRADE) { + messageEntry = Randomizer_GetScrubMessage(textId); + } + } + if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { + if (CVar_GetS32("gInjectSkulltulaCount", 0) != 0) { + if (CVar_GetS32("gSkulltulaFreeze", 0) != 0) { + textId = TEXT_GS_NO_FREEZE; + } else { + textId = TEXT_GS_FREEZE; + } + messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); + } + } + if (messageEntry.textBoxType != -1) { + font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos; + switch (gSaveContext.language) { + case LANGUAGE_FRA: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.french, buffer, maxBufferSize); + case LANGUAGE_GER: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.german, buffer, maxBufferSize); + + case LANGUAGE_ENG: + default: + return msgCtx->msgLength = font->msgLength = + CopyStringToCharBuffer(messageEntry.english, buffer, maxBufferSize); + } + } + return false; +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index fca7baea6..ae8f799b9 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -10,6 +10,8 @@ #include "Enhancements/savestates.h" #include "Enhancements/randomizer/randomizer.h" +const std::string customMessageTableID = "BaseGameOverrides"; + class OTRGlobals { public: @@ -91,10 +93,6 @@ Sprite* GetSeedTexture(uint8_t index); void Randomizer_LoadSettings(const char* spoilerFileName); u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum); -int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize); -int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize); -int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize); -int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize); void Randomizer_LoadHintLocations(const char* spoilerFileName); void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent); s16 Randomizer_GetItemModelFromId(s16 itemId); @@ -103,6 +101,7 @@ s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s32 Randomizer_GetItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor); bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId); +int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx); #endif #endif diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp index 8c1d26a75..c6be43964 100644 --- a/soh/soh/z_message_OTR.cpp +++ b/soh/soh/z_message_OTR.cpp @@ -1,11 +1,13 @@ #include "OTRGlobals.h" #include "ResourceMgr.h" #include "Scene.h" -#include "message_data_static.h" #include "Utils/StringHelper.h" #include "global.h" #include "vt.h" #include +#include +#include +#include extern "C" MessageTableEntry* sNesMessageEntryTablePtr; extern "C" MessageTableEntry* sGerMessageEntryTablePtr; @@ -92,4 +94,22 @@ extern "C" void OTRMessage_Init() sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str(); sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size(); } -} \ No newline at end of file + + CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID); + CustomMessageManager::Instance->CreateGetItemMessage( + customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN, + { + TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + "You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!\x0E\x3C", + "Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!\x0E\x3C", + "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!\x0E\x3C" + } + ); + CustomMessageManager::Instance->CreateGetItemMessage( + customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN, + { + TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, + "You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!", + "Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!", + "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" }); +} diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 29d3ee4ff..42b3a86a2 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -1663,7 +1663,9 @@ void Message_OpenText(GlobalContext* globalCtx, u16 textId) { gSaveContext.eventInf[0] = gSaveContext.eventInf[1] = gSaveContext.eventInf[2] = gSaveContext.eventInf[3] = 0; } - if (sTextIsCredits) { + if (CustomMessage_RetrieveIfExists(globalCtx)) { + osSyncPrintf("Found custom message"); + } else if (sTextIsCredits) { Message_FindCreditsMessage(globalCtx, textId); msgCtx->msgLength = font->msgLength; char* src = (uintptr_t)font->msgOffset; @@ -1674,73 +1676,9 @@ void Message_OpenText(GlobalContext* globalCtx, u16 textId) { //font->msgLength, __FILE__, __LINE__); } else { Message_FindMessage(globalCtx, textId); - // if we're rando'd and talking to a gossip stone - if (gSaveContext.n64ddFlag && - textId == 0x2053 && - Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) != 0 && - (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 1 || - (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 2 && - Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) || - (Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 3 && - CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) { - - s16 actorParams = msgCtx->talkActor->params; - - // if we're in a generic grotto - if (globalCtx->sceneNum == 62 && actorParams == 14360) { - // look for the chest in the actorlist to determine - // which grotto we're in - int numOfActorLists = sizeof(globalCtx->actorCtx.actorLists)/sizeof(globalCtx->actorCtx.actorLists[0]); - for(int i = 0; i < numOfActorLists; i++) { - if(globalCtx->actorCtx.actorLists[i].length) { - if(globalCtx->actorCtx.actorLists[i].head->id == 10) { - // set the params for the hint check to be negative chest params - actorParams = 0 - globalCtx->actorCtx.actorLists[i].head->params; - } - } - } - } - - RandomizerCheck hintCheck = Randomizer_GetCheckFromActor(globalCtx->sceneNum, msgCtx->talkActor->id, actorParams); - - // Pass the sizeof the message buffer so we don't hardcode any sizes and can rely on globals. - // If no hint can be found, this just returns 0 size and doesn't modify the buffer, so no worries. - msgCtx->msgLength = font->msgLength = Randomizer_CopyHintFromCheck(hintCheck, font->msgBuf, sizeof(font->msgBuf)); - } else if (gSaveContext.n64ddFlag && (textId == 0x7040 || textId == 0x7088)) { - // rando hints at altar - msgCtx->msgLength = font->msgLength = Randomizer_CopyAltarMessage(font->msgBuf, sizeof(font->msgBuf)); - } else if (textId == 0x00b4 && CVar_GetS32("gInjectSkulltulaCount", 0) != 0) { - switch (gSaveContext.language) { - case LANGUAGE_FRA: - strcpy(font->msgBuf, "\x08\x13\x71Vous obtenez un \x05\x41Symbole de\x01Skulltula d'or\x05\x40! " - "Vous avez\x01\collect\x96 " - "\x05\x41\x19\x05\x40 symboles en tout!\x02"); - break; - case LANGUAGE_GER: - strcpy(font->msgBuf, "\x08\x13\x71\Du erh\x93lst ein \x05\x41Goldene\x01Skulltula-Symbol\x05\x40\! " - "Du hast\x01insgesamt " - "\x05\x41\x19\x05\x40 symbol gesammelt!\x02"); - break; - case LANGUAGE_ENG: default: - strcpy(font->msgBuf, - "\x08\x13\x71You got a \x05\x41Gold Skulltula Token\x05\x40!\x01You've collected " - "\x05\x41\x19\x05\x40 tokens\x01in total!\x02"); - break; - } - msgCtx->msgLength = font->msgLength = strlen(font->msgBuf); - } else if (gSaveContext.n64ddFlag && (textId == 0x10A2 || textId == 0x10DC || textId == 0x10DD)) { - msgCtx->msgLength = font->msgLength = CopyScrubMessage(textId, font->msgBuf, sizeof(font->msgBuf)); - } else if (gSaveContext.n64ddFlag && textId == 0x70CC) { - if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) { - msgCtx->msgLength = font->msgLength = Randomizer_CopyGanonText(font->msgBuf, sizeof(font->msgBuf)); - } else { - msgCtx->msgLength = font->msgLength = Randomizer_CopyGanonHintText(font->msgBuf, sizeof(font->msgBuf)); - } - } else { - msgCtx->msgLength = font->msgLength; - char* src = (uintptr_t)font->msgOffset; - memcpy(font->msgBuf, src, font->msgLength); - } + msgCtx->msgLength = font->msgLength; + char* src = (uintptr_t)font->msgOffset; + memcpy(font->msgBuf, src, font->msgLength); } msgCtx->textBoxProperties = font->charTexBuf[0]; 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 c2327d0c5..1485d19fc 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -20,6 +20,7 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/object_link_child/object_link_child.h" #include "textures/icon_item_24_static/icon_item_24_static.h" +#include typedef struct { /* 0x00 */ u8 itemId; @@ -653,15 +654,15 @@ 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_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, 0x45, 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_FISH, OBJECT_GI_FISH, GID_FISH, 0x47, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 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_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG), - GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF9, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG), GET_ITEM_NONE, GET_ITEM_NONE,