Merge branch 'rando-next' into rando-settings-menu-even-moar-changes

This commit is contained in:
aMannus 2022-08-28 20:19:19 +02:00
commit 1c59995feb
18 changed files with 441 additions and 175 deletions

View File

@ -260,5 +260,6 @@ extern GraphicsContext* __gfxCtx;
#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1) #define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1)
#define NUM_TRIALS 6
#endif #endif

View File

@ -15,7 +15,8 @@ typedef enum {
TEXT_BLUE_RUPEE = 0xCC, TEXT_BLUE_RUPEE = 0xCC,
TEXT_RED_RUPEE = 0xF0, TEXT_RED_RUPEE = 0xF0,
TEXT_PURPLE_RUPEE = 0xF1, TEXT_PURPLE_RUPEE = 0xF1,
TEXT_HUGE_RUPEE = 0xF2 TEXT_HUGE_RUPEE = 0xF2,
TEXT_BEAN_SALESMAN = 0x405E
} TextIDs; } TextIDs;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -2517,18 +2517,16 @@ namespace Settings {
BridgeRewardCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_REWARD_COUNT]); BridgeRewardCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_REWARD_COUNT]);
BridgeDungeonCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT]); BridgeDungeonCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT]);
BridgeTokenCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_TOKEN_COUNT]); BridgeTokenCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_TOKEN_COUNT]);
RandomGanonsTrials.SetSelectedIndex(cvarSettings[RSK_RANDOM_TRIALS]); if (cvarSettings[RSK_RANDOM_TRIALS] == 2) {
// RANDTODO: Switch this back once Ganon's Trials Count is properly implemented. RandomGanonsTrials.SetSelectedIndex(1);
//GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]); } else {
switch (cvarSettings[RSK_TRIAL_COUNT]) { RandomGanonsTrials.SetSelectedIndex(0);
case 0: }
GanonsTrialsCount.SetSelectedIndex(6); if (cvarSettings[RSK_RANDOM_TRIALS] == 0) {
break; GanonsTrialsCount.SetSelectedIndex(0);
case 1: } else {
GanonsTrialsCount.SetSelectedIndex(0); GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
break;
} }
ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]); ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]);
ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]); ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]);
Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]); Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]);
@ -2552,6 +2550,7 @@ namespace Settings {
ShuffleFrogSongRupees.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES]); ShuffleFrogSongRupees.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES]);
ShuffleAdultTradeQuest.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_ADULT_TRADE]); ShuffleAdultTradeQuest.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_ADULT_TRADE]);
ShuffleMagicBeans.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_MAGIC_BEANS]);
// the checkbox works because 0 is "Off" and 1 is "Fairy Ocarina" // the checkbox works because 0 is "Off" and 1 is "Fairy Ocarina"
StartingOcarina.SetSelectedIndex(cvarSettings[RSK_STARTING_OCARINA]); StartingOcarina.SetSelectedIndex(cvarSettings[RSK_STARTING_OCARINA]);

View File

@ -501,25 +501,23 @@ static void WriteMasterQuestDungeons(tinyxml2::XMLDocument& spoilerLog) {
} }
} }
// Writes the required trails to the spoiler log, if there are any. // Writes the required trials to the spoiler log, if there are any.
static void WriteRequiredTrials(tinyxml2::XMLDocument& spoilerLog) { static void WriteRequiredTrials() {
auto parentNode = spoilerLog.NewElement("required-trials"); for (const auto& trial : Trial::trialList) {
if (trial->IsRequired()) {
for (const auto* trial : Trial::trialList) { std::string trialName;
if (trial->IsSkipped()) { switch (gSaveContext.language) {
continue; case LANGUAGE_FRA:
trialName = trial->GetName().GetFrench();
break;
case LANGUAGE_ENG:
default:
trialName = trial->GetName().GetEnglish();
break;
}
jsonData["requiredTrials"].push_back(RemoveLineBreaks(trialName));
}
} }
auto node = parentNode->InsertNewChildElement("trial");
// PURPLE TODO: LOCALIZATION
std::string name = trial->GetName().GetEnglish();
name[0] = toupper(name[0]); // Capitalize T in "The"
node->SetAttribute("name", name.c_str());
}
if (!parentNode->NoChildren()) {
spoilerLog.RootElement()->InsertEndChild(parentNode);
}
} }
// Writes the intended playthrough to the spoiler log, separated into spheres. // Writes the intended playthrough to the spoiler log, separated into spheres.
@ -723,7 +721,7 @@ const char* SpoilerLog_Write(int language) {
// WriteEnabledGlitches(spoilerLog); // WriteEnabledGlitches(spoilerLog);
//} //}
//WriteMasterQuestDungeons(spoilerLog); //WriteMasterQuestDungeons(spoilerLog);
//WriteRequiredTrials(spoilerLog); WriteRequiredTrials();
WritePlaythrough(); WritePlaythrough();
//WriteWayOfTheHeroLocation(spoilerLog); //WriteWayOfTheHeroLocation(spoilerLog);
@ -773,7 +771,7 @@ bool PlacementLog_Write() {
WriteEnabledTricks(placementLog); WriteEnabledTricks(placementLog);
WriteEnabledGlitches(placementLog); WriteEnabledGlitches(placementLog);
WriteMasterQuestDungeons(placementLog); WriteMasterQuestDungeons(placementLog);
WriteRequiredTrials(placementLog); //WriteRequiredTrials(placementLog);
placementtxt = "\n" + placementtxt; placementtxt = "\n" + placementtxt;

View File

@ -7,12 +7,12 @@ TrialInfo::TrialInfo(Text name_)
TrialInfo::~TrialInfo() = default; TrialInfo::~TrialInfo() = default;
TrialInfo ForestTrial = TrialInfo(Text{"the Forest Trial", /*french*/"l'épreuve de la forêt", /*spanish*/"la prueba del bosque"}); TrialInfo ForestTrial = TrialInfo(Text{"the Forest Trial", /*french*/"l'épreuve de la Forêt", /*spanish*/"la prueba del bosque"});
TrialInfo FireTrial = TrialInfo(Text{"the Fire Trial", /*french*/"l'épreuve du feu", /*spanish*/"la prueba del fuego"}); TrialInfo FireTrial = TrialInfo(Text{"the Fire Trial", /*french*/"l'épreuve du Feu", /*spanish*/"la prueba del fuego"});
TrialInfo WaterTrial = TrialInfo(Text{"the Water Trial", /*french*/"l'épreuve de l'eau", /*spanish*/"la prueba del agua"}); TrialInfo WaterTrial = TrialInfo(Text{"the Water Trial", /*french*/"l'épreuve de l'Eau", /*spanish*/"la prueba del agua"});
TrialInfo SpiritTrial = TrialInfo(Text{"the Spirit Trial", /*french*/"l'épreuve de l'esprit", /*spanish*/"la prueba del espíritu"}); TrialInfo SpiritTrial = TrialInfo(Text{"the Spirit Trial", /*french*/"l'épreuve de l'Esprit", /*spanish*/"la prueba del espíritu"});
TrialInfo ShadowTrial = TrialInfo(Text{"the Shadow Trial", /*french*/"l'épreuve de l'ombre", /*spanish*/"la prueba de las sombras"}); TrialInfo ShadowTrial = TrialInfo(Text{"the Shadow Trial", /*french*/"l'épreuve de l'Ombre", /*spanish*/"la prueba de las sombras"});
TrialInfo LightTrial = TrialInfo(Text{"the Light Trial", /*french*/"l'épreuve de la lumière", /*spanish*/"la prueba de la luz"}); TrialInfo LightTrial = TrialInfo(Text{"the Light Trial", /*french*/"l'épreuve de la Lumière", /*spanish*/"la prueba de la luz"});
const TrialArray trialList = { const TrialArray trialList = {
&ForestTrial, &ForestTrial,

View File

@ -32,7 +32,7 @@ u8 generated;
const std::string Randomizer::getItemMessageTableID = "Randomizer"; const std::string Randomizer::getItemMessageTableID = "Randomizer";
const std::string Randomizer::hintMessageTableID = "RandomizerHints"; const std::string Randomizer::hintMessageTableID = "RandomizerHints";
const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs"; const std::string Randomizer::merchantMessageTableID = "RandomizerMerchants";
const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees"; const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees";
const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi"; const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
@ -107,6 +107,21 @@ Randomizer::~Randomizer() {
this->randomizerMerchantPrices.clear(); this->randomizerMerchantPrices.clear();
} }
std::unordered_map<std::string, RandomizerInf> spoilerFileTrialToEnum = {
{ "the Forest Trial", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "l'épreuve de la Forêt", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "the Fire Trial", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "l'épreuve du Feu", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "the Water Trial", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "l'épreuve de l'Eau", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "the Spirit Trial", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "l'épreuve de l'Esprit", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "the Shadow Trial", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "l'épreuve de l'Ombre", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "the Light Trial", RAND_INF_TRIALS_DONE_LIGHT_TRIAL },
{ "l'épreuve de la Lumière", RAND_INF_TRIALS_DONE_LIGHT_TRIAL }
};
std::unordered_map<s16, s16> getItemIdToItemId = { std::unordered_map<s16, s16> getItemIdToItemId = {
{ GI_BOW, ITEM_BOW }, { GI_BOW, ITEM_BOW },
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE }, { GI_ARROW_FIRE, ITEM_ARROW_FIRE },
@ -551,6 +566,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS }, { "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS },
{ "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS }, { "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS },
{ "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE }, { "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE },
{ "Shuffle Settings:Shuffle Magic Beans", RSK_SHUFFLE_MAGIC_BEANS},
{ "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD }, { "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD },
{ "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD }, { "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD },
{ "Start with Fairy Ocarina", RSK_STARTING_OCARINA }, { "Start with Fairy Ocarina", RSK_STARTING_OCARINA },
@ -672,6 +688,12 @@ void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
itemLocations[RC_UNKNOWN_CHECK] = RG_NONE; itemLocations[RC_UNKNOWN_CHECK] = RG_NONE;
} }
void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) {
ParseRequiredTrialsFile(spoilerFileName);
}
}
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
@ -788,6 +810,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD: case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD:
case RSK_SHUFFLE_COWS: case RSK_SHUFFLE_COWS:
case RSK_SHUFFLE_ADULT_TRADE: case RSK_SHUFFLE_ADULT_TRADE:
case RSK_SHUFFLE_MAGIC_BEANS:
case RSK_RANDOM_TRIALS: case RSK_RANDOM_TRIALS:
case RSK_STARTING_DEKU_SHIELD: case RSK_STARTING_DEKU_SHIELD:
case RSK_STARTING_KOKIRI_SWORD: case RSK_STARTING_KOKIRI_SWORD:
@ -1089,6 +1112,25 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
} }
} }
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
try {
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
json trialsJson = spoilerFileJson["requiredTrials"];
for (auto it = trialsJson.begin(); it != trialsJson.end(); it++) {
this->trialsRequired[spoilerFileTrialToEnum[it.value()]] = true;
}
} catch (const std::exception& e) {
return;
}
}
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) { void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName)); std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream) if (!spoilerFileStream)
@ -1139,6 +1181,10 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
} }
} }
bool Randomizer::IsTrialRequired(RandomizerInf trial) {
return this->trialsRequired.contains(trial);
}
s16 Randomizer::GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) { s16 Randomizer::GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) {
s16 itemId = GetItemFromActor(actorId, actorParams, sceneNum, ogId); s16 itemId = GetItemFromActor(actorId, actorParams, sceneNum, ogId);
return itemId; return itemId;
@ -1211,8 +1257,6 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) {
case RG_MAGIC_BEAN: case RG_MAGIC_BEAN:
return GI_BEAN; return GI_BEAN;
case RG_MAGIC_BEAN_PACK:
return GI_BEAN; //todo make it 10 of them
case RG_WEIRD_EGG: case RG_WEIRD_EGG:
return GI_WEIRD_EGG; return GI_WEIRD_EGG;
@ -2900,6 +2944,7 @@ void GenerateRandomizerImgui() {
cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0); cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0);
cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0); cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0);
cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0); cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0);
cvarSettings[RSK_SHUFFLE_MAGIC_BEANS] = CVar_GetS32("gRandomizeShuffleBeans", 0);
cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0); cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0);
// if we skip child zelda, we start with zelda's letter, and malon starts // if we skip child zelda, we start with zelda's letter, and malon starts
@ -2980,7 +3025,7 @@ void DrawRandoEditor(bool& open) {
const char* randoGerudoFortress[3] = { "Normal", "Fast", "Open" }; const char* randoGerudoFortress[3] = { "Normal", "Fast", "Open" };
const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions", const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions",
"Dungeon rewards", "Dungeons", "Tokens" }; "Dungeon rewards", "Dungeons", "Tokens" };
const char* randoGanonsTrial[2] = { "Off", "On" }; const char* randoGanonsTrial[3] = { "Skip", "Set Number", "Random Number" };
// World Settings // World Settings
const char* randoStartingAge[3] = { "Child", "Adult", "Random" }; const char* randoStartingAge[3] = { "Child", "Adult", "Random" };
@ -3383,23 +3428,20 @@ void DrawRandoEditor(bool& open) {
PaddedSeparator(); PaddedSeparator();
// Random Ganon's Trials // Random Ganon's Trials
/* ImGui::Text("Ganon's Trials");
ImGui::Text("Random Ganon's Trials");
InsertHelpHoverText("Sets a random number or required trials to enter\nGanon's Tower.");
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 2, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 0) {
ImGui::PopItemWidth();
SohImGui::EnhancementSliderInt("Ganon's Trial Count: %d", "##RandoTrialCount",
"gRandomizeGanonTrialCount", 0, 6, "", 6);
InsertHelpHoverText("Set the number of trials required to enter Ganon's Tower.");
RANDTODO: Switch back to slider when pre-completing some of Ganon's Trials is properly implemnted.
}
*/
SohImGui::EnhancementCheckbox("Skip Ganon's Trials", "gRandomizeGanonTrialCount");
InsertHelpHoverText( InsertHelpHoverText(
"Sets whether or not Ganon's Castle Trials are required to enter Ganon's Tower." "Sets the number of Ganon's Trials required to dispel the barrier\n\n"
"Skip - No Trials are required and the barrier is already dispelled.\n\n"
"Set Number - Select a number of trials that will be required from the"
"slider below. Which specific trials you need to complete will be random.\n\n"
"Random Number - A Random number and set of trials will be required."
); );
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 3, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 1) {
SohImGui::EnhancementSliderInt("Ganon's Trial Count: %d", "##RandoTrialCount",
"gRandomizeGanonTrialCount", 1, 6, "", 6);
InsertHelpHoverText("Set the number of trials required to enter Ganon's Tower.");
}
PaddedSeparator(); PaddedSeparator();
@ -4182,10 +4224,10 @@ void CreateGetItemMessages(std::vector<GetItemMessage> messageEntries) {
// Currently these are generated at runtime, one for each price between 0-95. We're soon going to migrate this // Currently these are generated at runtime, one for each price between 0-95. We're soon going to migrate this
// to being generated at save load, with only messages specific to each scrub. // to being generated at save load, with only messages specific to each scrub.
void CreateScrubMessages() { void CreateMerchantMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance; CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID); customMessageManager->AddCustomMessageTable(Randomizer::merchantMessageTableID);
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, 0, customMessageManager->CreateMessage(Randomizer::merchantMessageTableID, 0,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will give you a&%gmysterious 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&%gmysterious 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 %gmysteriösen&Gegenstand%w geben! Bitte nimm ihn!\x07\x10\xA3", "\x12\x38\x82\In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen %gmysteriösen&Gegenstand%w geben! Bitte nimm ihn!\x07\x10\xA3",
@ -4193,7 +4235,7 @@ void CreateScrubMessages() {
}); });
for (u32 price = 5; price <= 95; price += 5) { for (u32 price = 5; price <= 95; price += 5) {
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price, customMessageManager->CreateMessage(Randomizer::merchantMessageTableID, price,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, { 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" + "\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", std::to_string(price) + " Rupees%w it is!\x07\x10\xA3",
@ -4204,6 +4246,18 @@ void CreateScrubMessages() {
std::to_string(price) + " Rubis%w!\x07\x10\xA3" std::to_string(price) + " Rubis%w!\x07\x10\xA3"
}); });
} }
customMessageManager->CreateMessage(
Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN,
{
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",
"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 "
"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",
});
} }
void CreateRupeeMessages() { void CreateRupeeMessages() {
@ -4348,75 +4402,122 @@ void Randomizer::CreateCustomMessages() {
const std::vector<GetItemMessage> getItemMessages = { const std::vector<GetItemMessage> getItemMessages = {
GIMESSAGE(RG_ICE_TRAP, ITEM_NONE, "\x08\x06\x30You are a %bFOWL%w!\x0E\x20", GIMESSAGE(RG_ICE_TRAP, ITEM_NONE, "\x08\x06\x30You are a %bFOWL%w!\x0E\x20",
"\x08\x06\x15 Du bist ein %bDUMMKOPF%w!\x0E\x20", "\x08\x06\x50%bIDIOT%w\x0E\x20"), "\x08\x06\x15 Du bist ein %bDUMMKOPF%w!\x0E\x20", "\x08\x06\x50%bIDIOT%w\x0E\x20"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, GIMESSAGE_NO_GERMAN(
"You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!", RG_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE, "You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!",
"Vous obtenez une %rBouteille avec&une Flamme Bleue%w! Utilisez-la&pour faire fondre la %rGlace&Rouge%w!"), "Vous obtenez une %rBouteille avec&une Flamme Bleue%w! Utilisez-la&pour faire fondre la %rGlace&Rouge%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE, GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE,
"You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!", "You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!",
"Vous obtenez une %rBouteille avec&une Âme%w! Vendez-la au Marchand&d'Âme"), "Vous obtenez une %rBouteille avec&une Âme%w! Vendez-la au Marchand&d'Âme"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE, GIMESSAGE_NO_GERMAN(RG_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!", "You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Bleue%w! Buvez-la pour&restaurer votre %rénergie vitale%w&ainsi que votre %gmagie%w!"), "Vous obtenez une %rBouteille avec&une Potion Bleue%w! Buvez-la pour&restaurer votre "
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FISH, ITEM_FISH, "%rénergie vitale%w&ainsi que votre %gmagie%w!"),
"You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!", GIMESSAGE_NO_GERMAN(
"Vous obtenez une %rBouteille avec&un Poisson%w! Il a l'air délicieux!&Il paraîtrait que %bJabu-Jabu %wen&serait friand!"), RG_BOTTLE_WITH_FISH, ITEM_FISH,
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_BUGS, ITEM_BUG, "You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!",
"You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!", "Vous obtenez une %rBouteille avec&un Poisson%w! Il a l'air délicieux!&Il paraîtrait que %bJabu-Jabu "
"Vous obtenez une %rBouteille avec&des Insectes%w! Ils adorent creuser&dans la terre meuble!"), "%wen&serait friand!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FAIRY, ITEM_FAIRY, GIMESSAGE_NO_GERMAN(
"You got a %rFairy in a Bottle%w!&Use it wisely!", RG_BOTTLE_WITH_BUGS, ITEM_BUG, "You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!",
"Vous obtenez une %rBouteille avec&une Fée%w! Faites-en bon usage!"), "Vous obtenez une %rBouteille avec&des Insectes%w! Ils adorent creuser&dans la terre meuble!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!",
"You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!", "Vous obtenez une %rBouteille avec&une Fée%w! Faites-en bon usage!"),
"Vous obtenez une %rBouteille avec&une Potion Rouge%w! Buvez-la pour&restaurer votre %rénergie vitale%w!"), GIMESSAGE_NO_GERMAN(
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN, RG_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
"You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!", "You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!",
"Vous obtenez une %rBouteille avec&une Potion Verte%w! Buvez-la pour&restaurer votre %gmagie%w!"), "Vous obtenez une %rBouteille avec&une Potion Rouge%w! Buvez-la pour&restaurer votre %rénergie vitale%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_POE, ITEM_POE, GIMESSAGE_NO_GERMAN(
"You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this...", RG_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN,
"Vous obtenez une %rBouteille avec&un Esprit%w! Ça intéresserait&peut-être le vendeur d'Âme "), "You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!",
"Vous obtenez une %rBouteille avec&une Potion Verte%w! Buvez-la pour&restaurer votre %gmagie%w!"),
GIMESSAGE_NO_GERMAN(
RG_BOTTLE_WITH_POE, ITEM_POE,
"You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this...",
"Vous obtenez une %rBouteille avec&un Esprit%w! Ça intéresserait&peut-être le vendeur d'Âme "),
GIMESSAGE_NO_GERMAN(RG_GERUDO_FORTRESS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yThieves Hideout &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yRepaire des Voleurs%w!"), GIMESSAGE_NO_GERMAN(RG_GERUDO_FORTRESS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yThieves Hideout &%wSmall Key!",
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %gForest Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %gTemple de la Forêt%w!"), "Vous obtenez une %rPetite Clé %w&du %yRepaire des Voleurs%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rFire Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %rTemple du Feu%w!"), GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %gForest Temple &%wSmall Key!",
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %bWater Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %bTemple de l'Eau%w!"), "Vous obtenez une %rPetite Clé %w&du %gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %ySpirit Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yTemple de l'Esprit%w!"), GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rFire Temple &%wSmall Key!",
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pShadow Temple &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %pTemple de l'Ombre%w!"), "Vous obtenez une %rPetite Clé %w&du %rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pBottom of the &Well %wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %Puits%w!"), GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %bWater Temple &%wSmall Key!",
GIMESSAGE_NO_GERMAN(RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, ITEM_KEY_SMALL, "You found a %yGerudo Training &Grounds %wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %yGymnase Gerudo%w!"), "Vous obtenez une %rPetite Clé %w&du %bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rGanon's Castle &%wSmall Key!", "Vous obtenez une %rPetite Clé %w&du %Château de Ganon%w!"), GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %ySpirit Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %pShadow Temple &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_SMALL_KEY, ITEM_KEY_SMALL,
"You found a %pBottom of the &Well %wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %Puits%w!"),
GIMESSAGE_NO_GERMAN(RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, ITEM_KEY_SMALL,
"You found a %yGerudo Training &Grounds %wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %yGymnase Gerudo%w!"),
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_SMALL_KEY, ITEM_KEY_SMALL, "You found a %rGanon's Castle &%wSmall Key!",
"Vous obtenez une %rPetite Clé %w&du %Château de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %gForest Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%gTemple de la Forêt%w!"), GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %gForest Temple &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rFire Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%rTemple du Feu%w!"), "Vous obtenez la %rClé d'or %wdu&%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %bWater Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%bTemple de l'Eau%w!"), GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rFire Temple &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %ySpirit Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%yTemple de l'Esprit%w!"), "Vous obtenez la %rClé d'or %wdu&%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %pShadow Temple &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%pTemple de l'Ombre%w!"), GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %bWater Temple &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rGanon's Castle &%wBoss Key!", "Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"), "Vous obtenez la %rClé d'or %wdu&%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %ySpirit Temple &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, "You found the %gDeku Tree &%wMap!", "Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!"), "Vous obtenez la %rClé d'or %wdu&%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %rDodongo's Cavern &%wMap!", "Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!"), GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %pShadow Temple &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, "You found the %bJabu Jabu's Belly &%wMap!", "Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!"), "Vous obtenez la %rClé d'or %wdu&%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %gForest Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!"), GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_BOSS_KEY, ITEM_KEY_BOSS, "You found the %rGanon's Castle &%wBoss Key!",
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %rFire Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!"), "Vous obtenez la %rClé d'or %wdu&%rChâteau de Ganon%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %bWater Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %ySpirit Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %pShadow Temple &%wMap!", "Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, "You found the %pBottom of the &Well %wMap!", "Vous obtenez la %rCarte %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %cIce Cavern &%wMap!", "Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_COMPASS, ITEM_COMPASS, "You found the %gDeku Tree &%wCompass!", "Vous obtenez la %rBoussole %wde&l'%gArbre Mojo%w!"), GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, "You found the %gDeku Tree &%wMap!",
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_COMPASS, ITEM_COMPASS, "You found the %rDodongo's Cavern &%wCompass!", "Vous obtenez la %rBoussole %wde la&%rCaverne Dodongo%w!"), "Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_COMPASS, ITEM_COMPASS, "You found the %bJabu Jabu's Belly &%wCompass!", "Vous obtenez la %rBoussole %wdu &%bVentre de Jabu-Jabu%w!"), GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %rDodongo's Cavern &%wMap!",
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %gForest Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%gTemple de la Forêt%w!"), "Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %rFire Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%rTemple du Feu%w!"), GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, "You found the %bJabu Jabu's Belly &%wMap!",
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %bWater Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%bTemple de l'Eau%w!"), "Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %ySpirit Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%yTemple de l'Esprit%w!"), GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %gForest Temple &%wMap!",
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %pShadow Temple &%wCompass!", "Vous obtenez la %rBoussole %wdu &%pTemple de l'Ombre%w!"), "Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_COMPASS, ITEM_COMPASS, "You found the %pBottom of the &Well %wCompass!", "Vous obtenez la %rBoussole %wdu &%pPuits%w!"), GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %rFire Temple &%wMap!",
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_COMPASS, ITEM_COMPASS, "You found the %cIce Cavern &%wCompass!", "Vous obtenez la %rBoussole %wde &la %cCaverne Polaire%w!"), "Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %bWater Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %ySpirit Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %pShadow Temple &%wMap!",
"Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, "You found the %pBottom of the &Well %wMap!",
"Vous obtenez la %rCarte %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %cIce Cavern &%wMap!",
"Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!"),
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_COMPASS, ITEM_COMPASS, "You found the %gDeku Tree &%wCompass!",
"Vous obtenez la %rBoussole %wde&l'%gArbre Mojo%w!"),
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_COMPASS, ITEM_COMPASS, "You found the %rDodongo's Cavern &%wCompass!",
"Vous obtenez la %rBoussole %wde la&%rCaverne Dodongo%w!"),
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_COMPASS, ITEM_COMPASS, "You found the %bJabu Jabu's Belly &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%bVentre de Jabu-Jabu%w!"),
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %gForest Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%gTemple de la Forêt%w!"),
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %rFire Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%rTemple du Feu%w!"),
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %bWater Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%bTemple de l'Eau%w!"),
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %ySpirit Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%yTemple de l'Esprit%w!"),
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_COMPASS, ITEM_COMPASS, "You found the %pShadow Temple &%wCompass!",
"Vous obtenez la %rBoussole %wdu &%pTemple de l'Ombre%w!"),
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_COMPASS, ITEM_COMPASS,
"You found the %pBottom of the &Well %wCompass!",
"Vous obtenez la %rBoussole %wdu &%pPuits%w!"),
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_COMPASS, ITEM_COMPASS, "You found the %cIce Cavern &%wCompass!",
"Vous obtenez la %rBoussole %wde &la %cCaverne Polaire%w!"),
GIMESSAGE(RG_MAGIC_BEAN_PACK, ITEM_BEAN,
"You got a %rPack of Magic Beans%w!&Find a suitable spot for a garden&and plant them. Then, wait for&something fun to happen!",
"Du hast eine %rPackung&Magic Beans%w! Finde&einen geeigneten Platz fur einen&Garten und pflanze sie. Dann^warte auf etwas Lustiges passiert!",
"Vous avez un %rPack de&haricots magiques%w ! Trouvez&un endroit convenable pour un&jardin et plantez-les.^Ensuite, attendez quelque&chose d'amusant doit arriver !")
}; };
CreateGetItemMessages(getItemMessages); CreateGetItemMessages(getItemMessages);
CreateScrubMessages(); CreateMerchantMessages();
CreateRupeeMessages(); CreateRupeeMessages();
CreateNaviRandoMessages(); CreateNaviRandoMessages();
} }
@ -4513,6 +4614,7 @@ void InitRandoItemTable() {
GET_ITEM(RG_SHADOW_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_COMPASS), GET_ITEM(RG_SHADOW_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_COMPASS),
GET_ITEM(RG_BOTTOM_OF_THE_WELL_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_COMPASS), GET_ITEM(RG_BOTTOM_OF_THE_WELL_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_COMPASS),
GET_ITEM(RG_ICE_CAVERN_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_ICE_CAVERN_COMPASS), GET_ITEM(RG_ICE_CAVERN_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_ICE_CAVERN_COMPASS),
GET_ITEM(RG_MAGIC_BEAN_PACK, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, MOD_RANDOMIZER, RG_MAGIC_BEAN_PACK)
}; };
ItemTableManager::Instance->AddItemTable(MOD_RANDOMIZER); ItemTableManager::Instance->AddItemTable(MOD_RANDOMIZER);
for (int i = 0; i < ARRAY_COUNT(extendedVanillaGetItemTable); i++) { for (int i = 0; i < ARRAY_COUNT(extendedVanillaGetItemTable); i++) {

View File

@ -14,6 +14,7 @@ class Randomizer {
private: private:
std::unordered_map<RandomizerCheck, RandomizerGet> itemLocations; std::unordered_map<RandomizerCheck, RandomizerGet> itemLocations;
std::unordered_map<RandomizerCheck, std::string> hintLocations; std::unordered_map<RandomizerCheck, std::string> hintLocations;
std::unordered_map<RandomizerInf, bool> trialsRequired;
std::string childAltarText; std::string childAltarText;
std::string adultAltarText; std::string adultAltarText;
std::string ganonHintText; std::string ganonHintText;
@ -24,6 +25,7 @@ class Randomizer {
s16 GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, GetItemID ogItemId); s16 GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, GetItemID ogItemId);
void ParseRandomizerSettingsFile(const char* spoilerFileName); void ParseRandomizerSettingsFile(const char* spoilerFileName);
void ParseHintLocationsFile(const char* spoilerFileName); void ParseHintLocationsFile(const char* spoilerFileName);
void ParseRequiredTrialsFile(const char* spoilerFileName);
void ParseItemLocationsFile(const char* spoilerFileName, bool silent); void ParseItemLocationsFile(const char* spoilerFileName, bool silent);
bool IsItemVanilla(RandomizerGet randoGet); bool IsItemVanilla(RandomizerGet randoGet);
@ -34,7 +36,7 @@ class Randomizer {
static const std::string getItemMessageTableID; static const std::string getItemMessageTableID;
static const std::string hintMessageTableID; static const std::string hintMessageTableID;
static const std::string scrubMessageTableID; static const std::string merchantMessageTableID;
static const std::string rupeeMessageTableID; static const std::string rupeeMessageTableID;
static const std::string NaviRandoMessageTableID; static const std::string NaviRandoMessageTableID;
@ -44,7 +46,9 @@ class Randomizer {
bool SpoilerFileExists(const char* spoilerFileName); bool SpoilerFileExists(const char* spoilerFileName);
void LoadRandomizerSettings(const char* spoilerFileName); void LoadRandomizerSettings(const char* spoilerFileName);
void LoadHintLocations(const char* spoilerFileName); void LoadHintLocations(const char* spoilerFileName);
void LoadItemLocations(const char* spoilerFileName, bool silent); void LoadRequiredTrials(const char* spoilerFileName);
void LoadItemLocations(const char* spoilerFileName,bool silent);
bool IsTrialRequired(RandomizerInf trial);
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey); u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck GetCheckFromActor(s16 sceneNum, s16 actorId, s16 actorParams); RandomizerCheck GetCheckFromActor(s16 sceneNum, s16 actorId, s16 actorParams);
std::string GetChildAltarText() const; std::string GetChildAltarText() const;

View File

@ -1016,7 +1016,8 @@ typedef enum {
RSK_COMPLETE_MASK_QUEST, RSK_COMPLETE_MASK_QUEST,
RSK_ENABLE_GLITCH_CUTSCENES, RSK_ENABLE_GLITCH_CUTSCENES,
RSK_SKULLS_SUNS_SONG, RSK_SKULLS_SUNS_SONG,
RSK_SHUFFLE_ADULT_TRADE RSK_SHUFFLE_ADULT_TRADE,
RSK_SHUFFLE_MAGIC_BEANS
} RandomizerSettingKey; } RandomizerSettingKey;
typedef struct ScrubIdentity { typedef struct ScrubIdentity {

View File

@ -1562,10 +1562,18 @@ extern "C" void Randomizer_LoadHintLocations(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadHintLocations(spoilerFileName); OTRGlobals::Instance->gRandomizer->LoadHintLocations(spoilerFileName);
} }
extern "C" void Randomizer_LoadRequiredTrials(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadRequiredTrials(spoilerFileName);
}
extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) { extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) {
OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent); OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent);
} }
extern "C" bool Randomizer_IsTrialRequired(RandomizerInf trial) {
return OTRGlobals::Instance->gRandomizer->IsTrialRequired(trial);
}
extern "C" bool SpoilerFileExists(const char* spoilerFileName) { extern "C" bool SpoilerFileExists(const char* spoilerFileName) {
return OTRGlobals::Instance->gRandomizer->SpoilerFileExists(spoilerFileName); return OTRGlobals::Instance->gRandomizer->SpoilerFileExists(spoilerFileName);
} }
@ -1583,7 +1591,7 @@ extern "C" ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams,
} }
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(s16 itemPrice) { extern "C" CustomMessageEntry Randomizer_GetScrubMessage(s16 itemPrice) {
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, itemPrice); return CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, itemPrice);
} }
extern "C" CustomMessageEntry Randomizer_GetNaviMessage() { extern "C" CustomMessageEntry Randomizer_GetNaviMessage() {
@ -1721,6 +1729,8 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
// In rando, replace Navi's general overworld hints with rando-related gameplay tips // In rando, replace Navi's general overworld hints with rando-related gameplay tips
} else if (CVar_GetS32("gRandoRelevantNavi", 1) && textId >= 0x0140 && textId <= 0x015F) { } else if (CVar_GetS32("gRandoRelevantNavi", 1) && textId >= 0x0140 && textId <= 0x015F) {
messageEntry = Randomizer_GetNaviMessage(); messageEntry = Randomizer_GetNaviMessage();
} else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS) && textId == TEXT_BEAN_SALESMAN) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN);
} }
} }
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {

View File

@ -98,7 +98,9 @@ u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum); RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum);
ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData); ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
void Randomizer_LoadHintLocations(const char* spoilerFileName); void Randomizer_LoadHintLocations(const char* spoilerFileName);
void Randomizer_LoadRequiredTrials(const char* spoilerFileName);
void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent); void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent);
bool Randomizer_IsTrialRequired(RandomizerInf trial);
GetItemEntry Randomizer_GetRandomizedItem(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum); GetItemEntry Randomizer_GetRandomizedItem(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);
GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor); bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor);

View File

@ -1290,6 +1290,7 @@ void EnItem00_CustomItemsParticles(Actor* Parent, GlobalContext* globalCtx, GetI
switch (giEntry.itemId) { switch (giEntry.itemId) {
case RG_MAGIC_SINGLE: case RG_MAGIC_SINGLE:
case RG_MAGIC_DOUBLE: case RG_MAGIC_DOUBLE:
case RG_MAGIC_BEAN_PACK:
color_slot = 0; color_slot = 0;
break; break;
case RG_DOUBLE_DEFENSE: case RG_DOUBLE_DEFENSE:

View File

@ -2181,6 +2181,14 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
return RG_NONE; return RG_NONE;
} }
if (item == RG_MAGIC_BEAN_PACK) {
if (INV_CONTENT(ITEM_BEAN) == ITEM_NONE) {
INV_CONTENT(ITEM_BEAN) = ITEM_BEAN;
AMMO(ITEM_BEAN) = 10;
}
return RG_NONE;
}
if (item == RG_DOUBLE_DEFENSE) { if (item == RG_DOUBLE_DEFENSE) {
gSaveContext.doubleDefense = true; gSaveContext.doubleDefense = true;
gSaveContext.inventory.defenseHearts = 20; gSaveContext.inventory.defenseHearts = 20;
@ -2227,9 +2235,9 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
} }
} }
} else if ((item >= RG_FOREST_TEMPLE_SMALL_KEY && item <= RG_GANONS_CASTLE_SMALL_KEY) || } else if ((item >= RG_FOREST_TEMPLE_SMALL_KEY && item <= RG_GANONS_CASTLE_SMALL_KEY) ||
(item >= RG_FOREST_TEMPLE_BOSS_KEY && item <= RG_GANONS_CASTLE_BOSS_KEY) || (item >= RG_FOREST_TEMPLE_BOSS_KEY && item <= RG_GANONS_CASTLE_BOSS_KEY) ||
(item >= RG_DEKU_TREE_MAP && item <= RG_ICE_CAVERN_MAP) || (item >= RG_DEKU_TREE_MAP && item <= RG_ICE_CAVERN_MAP) ||
(item >= RG_DEKU_TREE_COMPASS && item <= RG_ICE_CAVERN_COMPASS)) { (item >= RG_DEKU_TREE_COMPASS && item <= RG_ICE_CAVERN_COMPASS)) {
int mapIndex = gSaveContext.mapIndex; int mapIndex = gSaveContext.mapIndex;
switch (item) { switch (item) {
case RG_DEKU_TREE_MAP: case RG_DEKU_TREE_MAP:
@ -2327,7 +2335,6 @@ u16 Randomizer_Item_Give(GlobalContext* globalCtx, GetItemEntry giEntry) {
return temp; return temp;
} }
u8 Item_CheckObtainability(u8 item) { u8 Item_CheckObtainability(u8 item) {
s16 i; s16 i;
s16 slot = SLOT(item); s16 slot = SLOT(item);

View File

@ -4,9 +4,9 @@
#include <string.h> #include <string.h>
#include <soh/Enhancements/randomizer/randomizerTypes.h> #include <soh/Enhancements/randomizer/randomizerTypes.h>
#include <soh/Enhancements/randomizer/randomizer_inf.h> #include <soh/Enhancements/randomizer/randomizer_inf.h>
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
#define NUM_DUNGEONS 8 #define NUM_DUNGEONS 8
#define NUM_TRIALS 6
#define NUM_COWS 10 #define NUM_COWS 10
#define NUM_SCRUBS 35 #define NUM_SCRUBS 35
@ -309,6 +309,11 @@ void GiveLinkDekuNutUpgrade(GetItemID giid) {
} }
} }
void GiveLinkSkullToken() {
gSaveContext.inventory.questItems |= gBitFlags[QUEST_SKULL_TOKEN];
gSaveContext.inventory.gsTokens++;
}
void GiveLinkMagic(GetItemID giid) { void GiveLinkMagic(GetItemID giid) {
if (giid == RG_MAGIC_SINGLE) { if (giid == RG_MAGIC_SINGLE) {
gSaveContext.magicLevel = 1; gSaveContext.magicLevel = 1;
@ -508,6 +513,53 @@ void GiveLinkDungeonItem(GetItemID getItemId) {
} }
} }
void GiveLinkAdultTradeItem(GetItemID giid) {
ItemID item;
switch (giid) {
case GI_POCKET_EGG:
item = ITEM_POCKET_EGG;
break;
case GI_POCKET_CUCCO:
item = ITEM_POCKET_CUCCO;
break;
case GI_COJIRO:
item = ITEM_COJIRO;
break;
case GI_ODD_MUSHROOM:
item = ITEM_ODD_MUSHROOM;
break;
case GI_ODD_POTION:
item = ITEM_ODD_POTION;
break;
case GI_SAW:
item = ITEM_SAW;
break;
case GI_SWORD_BROKEN:
item = ITEM_SWORD_BROKEN;
break;
case GI_PRESCRIPTION:
item = ITEM_PRESCRIPTION;
break;
case GI_FROG:
item = ITEM_FROG;
break;
case GI_EYEDROPS:
item = ITEM_EYEDROPS;
break;
case GI_CLAIM_CHECK:
item = ITEM_CLAIM_CHECK;
break;
}
if ((item == ITEM_SAW) && CVar_GetS32("gDekuNutUpgradeFix", 0) == 0) {
gSaveContext.itemGetInf[1] |= 0x8000;
}
if (item >= ITEM_POCKET_EGG) {
gSaveContext.adultTradeItems |= ADULT_TRADE_FLAG(item);
}
INV_CONTENT(ITEM_TRADE_ADULT) = item;
}
void GiveLinksPocketMedallion() { void GiveLinksPocketMedallion() {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, RG_NONE); GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, RG_NONE);
@ -709,6 +761,15 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.randomizerInf[i] = 0; gSaveContext.randomizerInf[i] = 0;
} }
// Set all trials to cleared if trial count is random or anything other than 6
if (Randomizer_GetSettingValue(RSK_RANDOM_TRIALS) || (Randomizer_GetSettingValue(RSK_TRIAL_COUNT) != 6)) {
for (u16 i = RAND_INF_TRIALS_DONE_LIGHT_TRIAL; i <= RAND_INF_TRIALS_DONE_SHADOW_TRIAL; i++) {
if (!Randomizer_IsTrialRequired(i)) {
Flags_SetRandomizerInf(i);
}
}
}
// Set Cutscene flags to skip them // Set Cutscene flags to skip them
gSaveContext.eventChkInf[0xC] |= 0x10; // returned to tot with medallions gSaveContext.eventChkInf[0xC] |= 0x10; // returned to tot with medallions
gSaveContext.eventChkInf[0xC] |= 0x20; //sheik at tot pedestal gSaveContext.eventChkInf[0xC] |= 0x20; //sheik at tot pedestal
@ -735,6 +796,11 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.eventChkInf[3] |= 0x800; gSaveContext.eventChkInf[3] |= 0x800;
gSaveContext.eventChkInf[12] |= 1; gSaveContext.eventChkInf[12] |= 1;
// shuffle adult trade quest
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) {
gSaveContext.adultTradeItems = 0;
}
// Give Link's pocket item // Give Link's pocket item
GiveLinksPocketMedallion(); GiveLinksPocketMedallion();
@ -849,6 +915,10 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
GiveLinkDekuStickUpgrade(giid); GiveLinkDekuStickUpgrade(giid);
} else if (giid == GI_NUT_UPGRADE_30 || giid == GI_NUT_UPGRADE_40) { } else if (giid == GI_NUT_UPGRADE_30 || giid == GI_NUT_UPGRADE_40) {
GiveLinkDekuNutUpgrade(giid); GiveLinkDekuNutUpgrade(giid);
} else if (giid == GI_SKULL_TOKEN) {
GiveLinkSkullToken();
} else if (giid >= GI_POCKET_EGG && giid <= GI_CLAIM_CHECK || giid == GI_COJIRO) {
GiveLinkAdultTradeItem(giid);
} else { } else {
s32 iid = getItem.itemId; s32 iid = getItem.itemId;
if (iid != -1) INV_CONTENT(iid) = iid; if (iid != -1) INV_CONTENT(iid) = iid;
@ -965,11 +1035,6 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
} }
} }
// shuffle adult trade quest
if (Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) {
gSaveContext.adultTradeItems = 0;
}
// complete mask quest // complete mask quest
if (Randomizer_GetSettingValue(RSK_COMPLETE_MASK_QUEST)) { if (Randomizer_GetSettingValue(RSK_COMPLETE_MASK_QUEST)) {
gSaveContext.itemGetInf[3] |= 0x100; // Sold Keaton Mask gSaveContext.itemGetInf[3] |= 0x100; // Sold Keaton Mask

View File

@ -64,12 +64,33 @@ static u8 sEnergyColors[] = {
/* Forest prim */ 255, 255, 170, /* env */ 0, 200, 0, /* Forest prim */ 255, 255, 170, /* env */ 0, 200, 0,
}; };
// Translates from the barrier's actor params to their corresponding randInf flags.
RandomizerInf trialParamToRandInf(u16 params) {
switch (params) {
case KEKKAI_LIGHT:
return RAND_INF_TRIALS_DONE_LIGHT_TRIAL;
case KEKKAI_FOREST:
return RAND_INF_TRIALS_DONE_FOREST_TRIAL;
case KEKKAI_FIRE:
return RAND_INF_TRIALS_DONE_FIRE_TRIAL;
case KEKKAI_WATER:
return RAND_INF_TRIALS_DONE_WATER_TRIAL;
case KEKKAI_SPIRIT:
return RAND_INF_TRIALS_DONE_SPIRIT_TRIAL;
case KEKKAI_SHADOW:
return RAND_INF_TRIALS_DONE_SHADOW_TRIAL;
}
}
s32 DemoKekkai_CheckEventFlag(s32 params) { s32 DemoKekkai_CheckEventFlag(s32 params) {
static s32 eventFlags[] = { 0xC3, 0xBC, 0xBF, 0xBE, 0xBD, 0xAD, 0xBB }; static s32 eventFlags[] = { 0xC3, 0xBC, 0xBF, 0xBE, 0xBD, 0xAD, 0xBB };
if ((params < KEKKAI_TOWER) || (params > KEKKAI_FOREST)) { if ((params < KEKKAI_TOWER) || (params > KEKKAI_FOREST)) {
return true; return true;
} }
if (gSaveContext.n64ddFlag) {
return Flags_GetRandomizerInf(trialParamToRandInf(params));
}
return Flags_GetEventChkInf(eventFlags[params]); return Flags_GetEventChkInf(eventFlags[params]);
} }
@ -128,8 +149,7 @@ void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx) {
this->collider2.dim.yShift = 300; this->collider2.dim.yShift = 300;
if (gSaveContext.n64ddFlag) { if (gSaveContext.n64ddFlag) {
int trialsToComplete = Randomizer_GetSettingValue(RSK_TRIAL_COUNT); if (TrialsDoneCount() == NUM_TRIALS) {
if (trialsToComplete <= TrialsDoneCount()) {
Actor_Kill(thisx); Actor_Kill(thisx);
return; return;
} }
@ -141,6 +161,10 @@ void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx) {
case KEKKAI_SHADOW: case KEKKAI_SHADOW:
case KEKKAI_SPIRIT: case KEKKAI_SPIRIT:
case KEKKAI_FOREST: case KEKKAI_FOREST:
if (gSaveContext.n64ddFlag && Flags_GetRandomizerInf(trialParamToRandInf(thisx->params))) {
Actor_Kill(thisx);
return;
}
this->energyAlpha = 1.0f; this->energyAlpha = 1.0f;
this->orbScale = 1.0f; this->orbScale = 1.0f;
Actor_SetScale(thisx, 0.1f); Actor_SetScale(thisx, 0.1f);
@ -247,27 +271,10 @@ void DemoKekkai_TrialBarrierDispel(Actor* thisx, GlobalContext* globalCtx) {
DemoKekkai* this = (DemoKekkai*)thisx; DemoKekkai* this = (DemoKekkai*)thisx;
if (gSaveContext.n64ddFlag) { if (gSaveContext.n64ddFlag) {
switch (thisx->params) { Flags_SetRandomizerInf(trialParamToRandInf(thisx->params));
case KEKKAI_WATER: // May or may not be needed. Not sure if needed for anything
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_WATER_TRIAL); // that randoInf isn't already covering. Leaving it for safety.
break; Flags_SetEventChkInf(eventFlags[thisx->params]);
case KEKKAI_LIGHT:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_LIGHT_TRIAL);
break;
case KEKKAI_FIRE:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_FIRE_TRIAL);
break;
case KEKKAI_SHADOW:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_SHADOW_TRIAL);
break;
case KEKKAI_SPIRIT:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_SPIRIT_TRIAL);
break;
case KEKKAI_FOREST:
Flags_SetRandomizerInf(RAND_INF_TRIALS_DONE_FOREST_TRIAL);
break;
}
Flags_SetEventChkInf(eventFlags[thisx->params]);
} }
if (globalCtx->csCtx.frames == csFrames[this->actor.params]) { if (globalCtx->csCtx.frames == csFrames[this->actor.params]) {

View File

@ -90,7 +90,16 @@ static void* sEyeTextures[] = {
gMalonChildEyeClosedTex, gMalonChildEyeClosedTex,
}; };
bool Randomizer_ObtainedMalonHCReward() {
return Flags_GetEventChkInf(0x12);
}
u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* thisx) { u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* thisx) {
// Special case for Malon Hyrule Castle Text. Placing it here at the beginning
// has the added benefit of circumventing mask text if wearing bunny hood.
if (gSaveContext.n64ddFlag && globalCtx->sceneNum == SCENE_SPOT15) {
return Randomizer_ObtainedMalonHCReward() ? 0x2044 : 0x2043;
}
u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x17); u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x17);
if (faceReaction != 0) { if (faceReaction != 0) {
@ -191,25 +200,34 @@ s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) {
if (!LINK_IS_CHILD) { if (!LINK_IS_CHILD) {
return 0; return 0;
} }
// Causes Malon to appear in the market if you haven't met her yet.
if (((globalCtx->sceneNum == SCENE_MARKET_NIGHT) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) && if (((globalCtx->sceneNum == SCENE_MARKET_NIGHT) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) &&
!(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) { !(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) {
return 1; return 1;
} }
if ((globalCtx->sceneNum == SCENE_SPOT15) && !(gSaveContext.eventChkInf[1] & 0x10)) { if ((globalCtx->sceneNum == SCENE_SPOT15) && // if we're at hyrule castle
if (gSaveContext.infTable[8] & 0x800) { (!(gSaveContext.eventChkInf[1] & 0x10) || // and talon hasn't left
return 1; (gSaveContext.n64ddFlag &&
} else { !Randomizer_ObtainedMalonHCReward()))) { // or we're rando'd and haven't gotten malon's HC check
gSaveContext.infTable[8] |= 0x800; if (gSaveContext.infTable[8] & 0x800) { // if we've met malon
return 0; return 1; // make her appear at the castle
} else { // if we haven't met malon
gSaveContext.infTable[8] |= 0x800; // set the flag for meeting malon
return 0; // don't make her appear at the castle
} }
} }
// Malon asleep in her bed if Talon has left Hyrule Castle and it is nighttime.
if ((globalCtx->sceneNum == SCENE_SOUKO) && IS_NIGHT && (gSaveContext.eventChkInf[1] & 0x10)) { if ((globalCtx->sceneNum == SCENE_SOUKO) && IS_NIGHT && (gSaveContext.eventChkInf[1] & 0x10)) {
return 1; return 1;
} }
// Don't spawn Malon if none of the above are true and we are not in Lon Lon Ranch.
if (globalCtx->sceneNum != SCENE_SPOT20) { if (globalCtx->sceneNum != SCENE_SPOT20) {
return 0; return 0;
} }
if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10)) { // If we've gotten this far, we're in Lon Lon Ranch. Spawn Malon if it is daytime, Talon has left Hyrule Castle, and
// either we are not randomized, or we are and we have received Malon's item at Hyrule Castle.
if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10) &&
((gSaveContext.n64ddFlag && Randomizer_ObtainedMalonHCReward()) || !gSaveContext.n64ddFlag)) {
return 1; return 1;
} }
return 0; return 0;
@ -290,10 +308,16 @@ void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) {
this->actor.targetMode = 6; this->actor.targetMode = 6;
this->unk_1E8.unk_00 = 0; this->unk_1E8.unk_00 = 0;
if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) || // To avoid missing a check, we want Malon to have the actionFunc for singing, but not reacting to Ocarina, if any of
// the following are true.
// 1. Talon has not left Hyrule Castle.
// 2. We are Randomized and have not obtained Malon's Weird Egg Check.
// 3. We are not Randomized and have obtained Epona's Song
if (!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag) ||
(gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) { (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F))) {
this->actionFunc = func_80AA0D88; this->actionFunc = func_80AA0D88;
EnMa1_ChangeAnim(this, ENMA1_ANIM_2); EnMa1_ChangeAnim(this, ENMA1_ANIM_2);
// If none of the above conditions were true, set Malon up to teach Epona's Song.
} else { } else {
if (gSaveContext.n64ddFlag) { // Skip straight to "let's sing it together" textbox in the ranch if (gSaveContext.n64ddFlag) { // Skip straight to "let's sing it together" textbox in the ranch
gSaveContext.eventChkInf[1] |= 0x40; gSaveContext.eventChkInf[1] |= 0x40;
@ -322,9 +346,16 @@ void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) {
} }
} }
if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10)) { // We want to Kill Malon's Actor outside of randomizer when Talon is freed. In Randomizer we don't kill Malon's
// Actor here, otherwise if we wake up Talon first and then get her check she will spontaneously
// disappear.
if ((globalCtx->sceneNum == SCENE_SPOT15) && (!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[1] & 0x10)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else if (!(gSaveContext.eventChkInf[1] & 0x10) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) { // We want Malon to give the Weird Egg Check (see function below) in the following situations:
// 1. Talon as not left Hyrule Castle (Vanilla) OR
// 2. We haven't obtained Malon's Weird Egg Check (Randomizer only) OR
// 3. We have Epona's Song? (Vanilla only, not sure why it's here but I didn't write that one)
} else if ((!(gSaveContext.eventChkInf[1] & 0x10) || (gSaveContext.n64ddFlag && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !gSaveContext.n64ddFlag)) {
if (this->unk_1E8.unk_00 == 2) { if (this->unk_1E8.unk_00 == 2) {
this->actionFunc = func_80AA0EA0; this->actionFunc = func_80AA0EA0;
globalCtx->msgCtx.stateTimer = 4; globalCtx->msgCtx.stateTimer = 4;

View File

@ -125,11 +125,19 @@ void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) {
} else if (Message_ShouldAdvance(globalCtx)) { } else if (Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) { switch (globalCtx->msgCtx.choiceIndex) {
case 0: // yes case 0: // yes
if (gSaveContext.rupees < sPrices[BEANS_BOUGHT]) { if (gSaveContext.rupees <
((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS))
? 60
: sPrices[BEANS_BOUGHT])) {
Message_ContinueTextbox(globalCtx, 0x4069); // not enough rupees text Message_ContinueTextbox(globalCtx, 0x4069); // not enough rupees text
return; return;
} }
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f); if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) {
GiveItemEntryFromActor(&this->actor, globalCtx,
Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN), 90.0f, 10.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
}
this->actionFunc = EnMs_Sell; this->actionFunc = EnMs_Sell;
return; return;
case 1: // no case 1: // no
@ -142,11 +150,18 @@ void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) {
void EnMs_Sell(EnMs* this, GlobalContext* globalCtx) { void EnMs_Sell(EnMs* this, GlobalContext* globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) { if (Actor_HasParent(&this->actor, globalCtx)) {
Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]); Rupees_ChangeBy((gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? -60 : -sPrices[BEANS_BOUGHT]);
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = EnMs_TalkAfterPurchase; this->actionFunc =
(gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? EnMs_Wait : EnMs_TalkAfterPurchase;
} else { } else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f); if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) {
GiveItemEntryFromActor(&this->actor, globalCtx,
Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN), 90.0f, 10.0f);
BEANS_BOUGHT = 10;
} else {
func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f);
}
} }
} }

View File

@ -135,6 +135,27 @@ void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx) {
this->effects[5].size = 0.0f; this->effects[5].size = 0.0f;
} }
if (gSaveContext.n64ddFlag) {
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_FOREST_TRIAL)) {
this->effects[0].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_WATER_TRIAL)) {
this->effects[1].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_SHADOW_TRIAL)) {
this->effects[2].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_FIRE_TRIAL)) {
this->effects[3].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_LIGHT_TRIAL)) {
this->effects[4].size = 0.0f;
}
if (Flags_GetRandomizerInf(RAND_INF_TRIALS_DONE_SPIRIT_TRIAL)) {
this->effects[5].size = 0.0f;
}
}
if (gSaveContext.cutsceneTrigger != 0) { if (gSaveContext.cutsceneTrigger != 0) {
if (gSaveContext.entranceIndex == 0x0538) { if (gSaveContext.entranceIndex == 0x0538) {
this->effects[0].size = 0.1f; this->effects[0].size = 0.1f;

View File

@ -433,6 +433,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
const char* fileLoc = CVar_GetString("gSpoilerLog", ""); const char* fileLoc = CVar_GetString("gSpoilerLog", "");
Randomizer_LoadSettings(fileLoc); Randomizer_LoadSettings(fileLoc);
Randomizer_LoadHintLocations(fileLoc); Randomizer_LoadHintLocations(fileLoc);
Randomizer_LoadRequiredTrials(fileLoc);
Randomizer_LoadItemLocations(fileLoc, silent); Randomizer_LoadItemLocations(fileLoc, silent);
fileSelectSpoilerFileLoaded = true; fileSelectSpoilerFileLoaded = true;
} }