mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-10 11:35:19 -05:00
00a862a252
* use trial enum instead of magic numbers * simplify trial count/random trial logic * set default to set number Co-authored-by: briaguya <briaguya@alice>
4631 lines
252 KiB
C++
4631 lines
252 KiB
C++
#include "randomizer.h"
|
|
#include <nlohmann/json.hpp>
|
|
#include "3drando/settings.hpp"
|
|
#include <fstream>
|
|
#include <variables.h>
|
|
#include <macros.h>
|
|
#include <objects/gameplay_keep/gameplay_keep.h>
|
|
#include <functions.h>
|
|
#include <Cvar.h>
|
|
#include <textures/icon_item_static/icon_item_static.h>
|
|
#include <textures/icon_item_24_static/icon_item_24_static.h>
|
|
#include <ImGuiImpl.h>
|
|
#include <thread>
|
|
#include "3drando/rando_main.hpp"
|
|
#include "3drando/random.hpp"
|
|
#include "../../UIWidgets.hpp"
|
|
#include <ImGui/imgui_internal.h>
|
|
#include "../custom-message/CustomMessageTypes.h"
|
|
#include "../item-tables/ItemTableManager.h"
|
|
#include "../presets.h"
|
|
#include "../../../src/overlays/actors/ovl_En_GirlA/z_en_girla.h"
|
|
#include <stdexcept>
|
|
#include "randomizer_check_objects.h"
|
|
#include <sstream>
|
|
#include <tuple>
|
|
#include <functional>
|
|
#include "draw.h"
|
|
#include "rando_hash.h"
|
|
|
|
extern "C" uint32_t ResourceMgr_IsGameMasterQuest();
|
|
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
|
|
|
|
using json = nlohmann::json;
|
|
using namespace std::literals::string_literals;
|
|
|
|
std::unordered_map<std::string, RandomizerCheck> SpoilerfileCheckNameToEnum;
|
|
std::unordered_map<std::string, RandomizerGet> SpoilerfileGetNameToEnum;
|
|
std::unordered_map<RandomizerGet, std::vector<std::string>> EnumToSpoilerfileGetName;
|
|
std::multimap<std::tuple<s16, s16, s32>, RandomizerCheckObject> checkFromActorMultimap;
|
|
std::set<RandomizerCheck> excludedLocations;
|
|
|
|
u8 generated;
|
|
|
|
const std::string Randomizer::getItemMessageTableID = "Randomizer";
|
|
const std::string Randomizer::hintMessageTableID = "RandomizerHints";
|
|
const std::string Randomizer::merchantMessageTableID = "RandomizerMerchants";
|
|
const std::string Randomizer::rupeeMessageTableID = "RandomizerRupees";
|
|
const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
|
|
const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap";
|
|
|
|
static const char* englishRupeeNames[80] = {
|
|
"Rupees", "Bitcoin", "Bananas", "Cornflakes", "Gummybears", "Floopies", "Dollars",
|
|
"Lemmings", "Emeralds", "Bucks", "Rubles", "Diamonds", "Moons", "Stars",
|
|
"Mana", "Doll Hairs", "Dogecoin", "Mushrooms", "Experience", "Friends", "Coins",
|
|
"Rings", "Gil", "Pokédollars", "Bells", "Orbs", "Bottle Caps", "Simoleons",
|
|
"Pokémon", "Toys", "Smackaroos", "Zorkmids", "Zenny", "Bones", "Souls",
|
|
"Studs", "Munny", "Rubies", "Gald", "Gold", "Shillings", "Pounds",
|
|
"Glimmer", "Potch", "Robux", "V-Bucks", "Bratwürste", "Mesetas", "Coal",
|
|
"Euro", "Spoons", "Cucumbers", "FPS", "Shekels", "Yen", "Canadian Dollars",
|
|
"Dollarydoos", "Copper", "Silver", "Platinum", "Gems", "Minerals", "Vespene Gas",
|
|
"Lumber", "Jiggies", "Mumbo Tokens", "KF7 Ammo", "Remote Mines", "Credits", "Doubloons",
|
|
"Ether", "Doge", "Cards", "Talent", "Poko", "Lira", "Kroner",
|
|
"Store Credit", "Social Credit", "Cocoa Beans"
|
|
};
|
|
|
|
static const char* germanRupeeNames[41] = {
|
|
"Rubine", "Mäuse", "Kröten", "Münzen", "Euro", "Mark", "Bananen",
|
|
"Gummibären", "Bonbons", "Diamanten", "Bratwürste", "Bitcoin", "Dogecoin", "Monde",
|
|
"Sterne", "Brause UFOs", "Taler", "Sternis", "Schillinge", "Freunde", "Seelen",
|
|
"Gil", "Zenny", "Pfandflaschen", "Knochen", "Pilze", "Smaragde", "Kronkorken",
|
|
"Pokédollar", "Brötchen", "EXP", "Wagenchips", "Moos", "Knete", "Kohle",
|
|
"Kies", "Radieschen", "Diridari", "Steine", "Kartoffeln", "Penunze"
|
|
};
|
|
|
|
static const char* frenchRupeeNames[36] = {
|
|
"Rubis", "Bitcoin", "Bananes", "Euros", "Dollars", "Émeraudes", "Joyaux", "Diamants",
|
|
"Balles", "Pokémon", "Pièces", "Lunes", "Étoiles", "Dogecoin", "Anneaux", "Radis",
|
|
"Pokédollars", "Zennies", "Pépètes", "Mailles", "Éthers", "Clochettes", "Capsules", "Gils",
|
|
"Champignons", "Blés", "Halos", "Munnies", "Orens", "Florens", "Crédits", "Galds",
|
|
"Bling", "Orbes", "Baguettes", "Croissants"
|
|
};
|
|
|
|
Randomizer::Randomizer() {
|
|
for (auto [randomizerCheck, rcObject] : RandomizerCheckObjects::GetAllRCObjects()) {
|
|
SpoilerfileCheckNameToEnum[rcObject.rcSpoilerName] = rcObject.rc;
|
|
checkFromActorMultimap.emplace(std::make_tuple((s16)rcObject.actorId, (s16)rcObject.sceneId, rcObject.actorParams), rcObject);
|
|
}
|
|
SpoilerfileCheckNameToEnum["Invalid Location"] = RC_UNKNOWN_CHECK;
|
|
SpoilerfileCheckNameToEnum["Link's Pocket"] = RC_LINKS_POCKET;
|
|
|
|
for (auto item: *RandoMain::GetFullItemTable()) {
|
|
// Easiest way to filter out all the empty values from the array, since we still technically want the 0/RG_NONE entry
|
|
if (item.GetName().english.empty()) continue;
|
|
SpoilerfileGetNameToEnum[item.GetName().english] = item.GetRandomizerGet();
|
|
SpoilerfileGetNameToEnum[item.GetName().french] = item.GetRandomizerGet();
|
|
EnumToSpoilerfileGetName[item.GetRandomizerGet()] = {
|
|
item.GetName().english,
|
|
item.GetName().english,
|
|
item.GetName().french,
|
|
};
|
|
}
|
|
}
|
|
|
|
Sprite* Randomizer::GetSeedTexture(uint8_t index) {
|
|
return &gSeedTextures[index];
|
|
}
|
|
|
|
Randomizer::~Randomizer() {
|
|
this->randoSettings.clear();
|
|
this->itemLocations.clear();
|
|
this->merchantPrices.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<std::string, SceneID> spoilerFileDungeonToScene = {
|
|
{ "Deku Tree", SCENE_YDAN },
|
|
{ "Dodongo's Cavern", SCENE_DDAN },
|
|
{ "Jabu Jabu's Belly", SCENE_BDAN },
|
|
{ "Forest Temple", SCENE_BMORI1 },
|
|
{ "Fire Temple", SCENE_HIDAN },
|
|
{ "Water Temple", SCENE_MIZUSIN },
|
|
{ "Spirit Temple", SCENE_JYASINZOU },
|
|
{ "Shadow Temple", SCENE_HAKADAN },
|
|
{ "Bottom of the Well", SCENE_HAKADANCH },
|
|
{ "Ice Cavern", SCENE_ICE_DOUKUTO },
|
|
{ "Gerudo Training Grounds", SCENE_MEN },
|
|
{ "Ganon's Castle", SCENE_GANONTIKA }
|
|
};
|
|
|
|
std::unordered_map<s16, s16>
|
|
getItemIdToItemId = { { GI_BOW, ITEM_BOW },
|
|
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE },
|
|
{ GI_DINS_FIRE, ITEM_DINS_FIRE },
|
|
{ GI_SLINGSHOT, ITEM_SLINGSHOT },
|
|
{ GI_OCARINA_FAIRY, ITEM_OCARINA_FAIRY },
|
|
{ GI_OCARINA_OOT, ITEM_OCARINA_TIME },
|
|
{ GI_HOOKSHOT, ITEM_HOOKSHOT },
|
|
{ GI_LONGSHOT, ITEM_LONGSHOT },
|
|
{ GI_ARROW_ICE, ITEM_ARROW_ICE },
|
|
{ GI_FARORES_WIND, ITEM_FARORES_WIND },
|
|
{ GI_BOOMERANG, ITEM_BOOMERANG },
|
|
{ GI_LENS, ITEM_LENS },
|
|
{ GI_HAMMER, ITEM_HAMMER },
|
|
{ GI_ARROW_LIGHT, ITEM_ARROW_LIGHT },
|
|
{ GI_NAYRUS_LOVE, ITEM_NAYRUS_LOVE },
|
|
{ GI_BOTTLE, ITEM_BOTTLE },
|
|
{ GI_POTION_RED, ITEM_POTION_RED },
|
|
{ GI_POTION_GREEN, ITEM_POTION_GREEN },
|
|
{ GI_POTION_BLUE, ITEM_POTION_BLUE },
|
|
{ GI_FAIRY, ITEM_FAIRY },
|
|
{ GI_FISH, ITEM_FISH },
|
|
{ GI_MILK_BOTTLE, ITEM_MILK_BOTTLE },
|
|
{ GI_LETTER_RUTO, ITEM_LETTER_RUTO },
|
|
{ GI_BLUE_FIRE, ITEM_BLUE_FIRE },
|
|
{ GI_BUGS, ITEM_BUG },
|
|
{ GI_BIG_POE, ITEM_BIG_POE },
|
|
{ GI_POE, ITEM_POE },
|
|
{ GI_WEIRD_EGG, ITEM_WEIRD_EGG },
|
|
{ GI_LETTER_ZELDA, ITEM_LETTER_ZELDA },
|
|
{ GI_POCKET_EGG, ITEM_POCKET_EGG },
|
|
{ GI_COJIRO, ITEM_COJIRO },
|
|
{ GI_ODD_MUSHROOM, ITEM_ODD_MUSHROOM },
|
|
{ GI_ODD_POTION, ITEM_ODD_POTION },
|
|
{ GI_SAW, ITEM_SAW },
|
|
{ GI_SWORD_BROKEN, ITEM_SWORD_BROKEN },
|
|
{ GI_PRESCRIPTION, ITEM_PRESCRIPTION },
|
|
{ GI_FROG, ITEM_FROG },
|
|
{ GI_EYEDROPS, ITEM_EYEDROPS },
|
|
{ GI_CLAIM_CHECK, ITEM_CLAIM_CHECK } };
|
|
|
|
std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEnum = {
|
|
{ "Open Settings:Forest", RSK_FOREST },
|
|
{ "Open Settings:Kakariko Gate", RSK_KAK_GATE },
|
|
{ "Open Settings:Door of Time", RSK_DOOR_OF_TIME },
|
|
{ "Open Settings:Zora's Fountain", RSK_ZORAS_FOUNTAIN },
|
|
{ "Open Settings:Gerudo Fortress", RSK_GERUDO_FORTRESS },
|
|
{ "Open Settings:Rainbow Bridge", RSK_RAINBOW_BRIDGE },
|
|
{ "Open Settings:Stone Count", RSK_RAINBOW_BRIDGE_STONE_COUNT },
|
|
{ "Open Settings:Medallion Count", RSK_RAINBOW_BRIDGE_MEDALLION_COUNT },
|
|
{ "Open Settings:Reward Count", RSK_RAINBOW_BRIDGE_REWARD_COUNT },
|
|
{ "Open Settings:Dungeon Count", RSK_RAINBOW_BRIDGE_DUNGEON_COUNT },
|
|
{ "Open Settings:Token Count", RSK_RAINBOW_BRIDGE_TOKEN_COUNT },
|
|
{ "Shuffle Settings:Link's Pocket", RSK_LINKS_POCKET},
|
|
{ "Shuffle Settings:Shuffle Gerudo Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD },
|
|
{ "Shuffle Settings:Shopsanity", RSK_SHOPSANITY },
|
|
{ "Shuffle Settings:Scrub Shuffle", RSK_SHUFFLE_SCRUBS },
|
|
{ "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS },
|
|
{ "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS },
|
|
{ "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE },
|
|
{ "Shuffle Settings:Shuffle Magic Beans", RSK_SHUFFLE_MAGIC_BEANS },
|
|
{ "Shuffle Settings:Shuffle Merchants", RSK_SHUFFLE_MERCHANTS },
|
|
{ "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD },
|
|
{ "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD },
|
|
{ "Start with Fairy Ocarina", RSK_STARTING_OCARINA },
|
|
{ "Shuffle Dungeon Items:Maps/Compasses", RSK_STARTING_MAPS_COMPASSES },
|
|
{ "Shuffle Dungeon Items:Small Keys", RSK_KEYSANITY },
|
|
{ "Shuffle Dungeon Items:Gerudo Fortress Keys", RSK_GERUDO_KEYS },
|
|
{ "Shuffle Dungeon Items:Boss Keys", RSK_BOSS_KEYSANITY },
|
|
{ "Shuffle Dungeon Items:Ganon's Boss Key", RSK_GANONS_BOSS_KEY },
|
|
{ "Shuffle Dungeon Items:Medallion Count", RSK_LACS_MEDALLION_COUNT },
|
|
{ "Shuffle Dungeon Items:Stone Count", RSK_LACS_STONE_COUNT },
|
|
{ "Shuffle Dungeon Items:Reward Count", RSK_LACS_REWARD_COUNT },
|
|
{ "Shuffle Dungeon Items:Dungeon Count", RSK_LACS_DUNGEON_COUNT },
|
|
{ "Shuffle Dungeon Items:Token Count", RSK_LACS_TOKEN_COUNT },
|
|
{ "World Settings:Starting Age", RSK_STARTING_AGE },
|
|
{ "World Settings:Ammo Drops", RSK_ENABLE_BOMBCHU_DROPS },
|
|
{ "World Settings:Bombchus in Logic", RSK_BOMBCHUS_IN_LOGIC },
|
|
{ "World Settings:Shuffle Entrances", RSK_SHUFFLE_ENTRANCES },
|
|
{ "World Settings:Dungeon Entrances", RSK_SHUFFLE_DUNGEON_ENTRANCES },
|
|
{ "World Settings:Overworld Entrances", RSK_SHUFFLE_OVERWORLD_ENTRANCES },
|
|
{ "World Settings:Interior Entrances", RSK_SHUFFLE_INTERIOR_ENTRANCES },
|
|
{ "World Settings:Grottos Entrances", RSK_SHUFFLE_GROTTO_ENTRANCES },
|
|
{ "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS },
|
|
{ "Misc Settings:Hint Clarity", RSK_HINT_CLARITY },
|
|
{ "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION },
|
|
{ "Misc Settings:Blue Fire Arrows", RSK_BLUE_FIRE_ARROWS },
|
|
{ "Misc Settings:Sunlight Arrows", RSK_SUNLIGHT_ARROWS },
|
|
{ "Skip Child Zelda", RSK_SKIP_CHILD_ZELDA },
|
|
{ "Start with Consumables", RSK_STARTING_CONSUMABLES },
|
|
{ "Start with Max Rupees", RSK_FULL_WALLETS },
|
|
{ "Gold Skulltula Tokens", RSK_STARTING_SKULLTULA_TOKEN },
|
|
{ "Timesaver Settings:Cuccos to return", RSK_CUCCO_COUNT },
|
|
{ "Timesaver Settings:Big Poe Target Count", RSK_BIG_POE_COUNT },
|
|
{ "Timesaver Settings:Skip Child Stealth", RSK_SKIP_CHILD_STEALTH },
|
|
{ "Timesaver Settings:Skip Epona Race", RSK_SKIP_EPONA_RACE },
|
|
{ "Timesaver Settings:Skip Tower Escape", RSK_SKIP_TOWER_ESCAPE },
|
|
{ "Timesaver Settings:Complete Mask Quest", RSK_COMPLETE_MASK_QUEST },
|
|
{ "Timesaver Settings:Skip Scarecrow's Song", RSK_SKIP_SCARECROWS_SONG },
|
|
{ "Timesaver Settings:Enable Glitch-Useful Cutscenes", RSK_ENABLE_GLITCH_CUTSCENES },
|
|
{ "World Settings:MQ Dungeon Count", RSK_MQ_DUNGEON_COUNT }
|
|
};
|
|
|
|
std::string sanitize(std::string stringValue) {
|
|
// Add backslashes.
|
|
for (auto i = stringValue.begin();;) {
|
|
auto const pos =
|
|
std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
|
if (pos == stringValue.end()) {
|
|
break;
|
|
}
|
|
i = std::next(stringValue.insert(pos, '\\'), 2);
|
|
}
|
|
|
|
// Removes others.
|
|
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(),
|
|
[](char const c) { return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }),
|
|
stringValue.end());
|
|
|
|
return stringValue;
|
|
}
|
|
|
|
#pragma optimize("", off)
|
|
#pragma GCC push_options
|
|
#pragma GCC optimize ("O0")
|
|
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#pragma GCC pop_options
|
|
#pragma optimize("", on)
|
|
|
|
void Randomizer::LoadRandomizerSettings(const char* spoilerFileName) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
ParseRandomizerSettingsFile(spoilerFileName);
|
|
}
|
|
|
|
for(auto randoSetting : gSaveContext.randoSettings) {
|
|
this->randoSettings[randoSetting.key] = randoSetting.value;
|
|
}
|
|
}
|
|
|
|
void Randomizer::LoadHintLocations(const char* spoilerFileName) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
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;
|
|
this->ganonText = gSaveContext.ganonText;
|
|
|
|
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 });
|
|
}
|
|
}
|
|
|
|
std::vector<RandomizerCheck> shopItemRandomizerChecks = {
|
|
RC_KF_SHOP_ITEM_1,
|
|
RC_KF_SHOP_ITEM_2,
|
|
RC_KF_SHOP_ITEM_3,
|
|
RC_KF_SHOP_ITEM_4,
|
|
RC_KF_SHOP_ITEM_5,
|
|
RC_KF_SHOP_ITEM_6,
|
|
RC_KF_SHOP_ITEM_7,
|
|
RC_KF_SHOP_ITEM_8,
|
|
RC_GC_SHOP_ITEM_1,
|
|
RC_GC_SHOP_ITEM_2,
|
|
RC_GC_SHOP_ITEM_3,
|
|
RC_GC_SHOP_ITEM_4,
|
|
RC_GC_SHOP_ITEM_5,
|
|
RC_GC_SHOP_ITEM_6,
|
|
RC_GC_SHOP_ITEM_7,
|
|
RC_GC_SHOP_ITEM_8,
|
|
RC_ZD_SHOP_ITEM_1,
|
|
RC_ZD_SHOP_ITEM_2,
|
|
RC_ZD_SHOP_ITEM_3,
|
|
RC_ZD_SHOP_ITEM_4,
|
|
RC_ZD_SHOP_ITEM_5,
|
|
RC_ZD_SHOP_ITEM_6,
|
|
RC_ZD_SHOP_ITEM_7,
|
|
RC_ZD_SHOP_ITEM_8,
|
|
RC_KAK_BAZAAR_ITEM_1,
|
|
RC_KAK_BAZAAR_ITEM_2,
|
|
RC_KAK_BAZAAR_ITEM_3,
|
|
RC_KAK_BAZAAR_ITEM_4,
|
|
RC_KAK_BAZAAR_ITEM_5,
|
|
RC_KAK_BAZAAR_ITEM_6,
|
|
RC_KAK_BAZAAR_ITEM_7,
|
|
RC_KAK_BAZAAR_ITEM_8,
|
|
RC_KAK_POTION_SHOP_ITEM_1,
|
|
RC_KAK_POTION_SHOP_ITEM_2,
|
|
RC_KAK_POTION_SHOP_ITEM_3,
|
|
RC_KAK_POTION_SHOP_ITEM_4,
|
|
RC_KAK_POTION_SHOP_ITEM_5,
|
|
RC_KAK_POTION_SHOP_ITEM_6,
|
|
RC_KAK_POTION_SHOP_ITEM_7,
|
|
RC_KAK_POTION_SHOP_ITEM_8,
|
|
RC_MARKET_BAZAAR_ITEM_1,
|
|
RC_MARKET_BAZAAR_ITEM_2,
|
|
RC_MARKET_BAZAAR_ITEM_3,
|
|
RC_MARKET_BAZAAR_ITEM_4,
|
|
RC_MARKET_BAZAAR_ITEM_5,
|
|
RC_MARKET_BAZAAR_ITEM_6,
|
|
RC_MARKET_BAZAAR_ITEM_7,
|
|
RC_MARKET_BAZAAR_ITEM_8,
|
|
RC_MARKET_POTION_SHOP_ITEM_1,
|
|
RC_MARKET_POTION_SHOP_ITEM_2,
|
|
RC_MARKET_POTION_SHOP_ITEM_3,
|
|
RC_MARKET_POTION_SHOP_ITEM_4,
|
|
RC_MARKET_POTION_SHOP_ITEM_5,
|
|
RC_MARKET_POTION_SHOP_ITEM_6,
|
|
RC_MARKET_POTION_SHOP_ITEM_7,
|
|
RC_MARKET_POTION_SHOP_ITEM_8,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_1,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_2,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_3,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_4,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_5,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_6,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_7,
|
|
RC_MARKET_BOMBCHU_SHOP_ITEM_8,
|
|
};
|
|
|
|
// Reference soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h
|
|
std::unordered_map<RandomizerGet, EnGirlAShopItem> randomizerGetToEnGirlShopItem = {
|
|
{ RG_BUY_DEKU_NUT_5, SI_DEKU_NUTS_5 },
|
|
{ RG_BUY_ARROWS_30, SI_ARROWS_30 },
|
|
{ RG_BUY_ARROWS_50, SI_ARROWS_50 },
|
|
{ RG_BUY_BOMBS_525, SI_BOMBS_5_R25 },
|
|
{ RG_BUY_DEKU_NUT_10, SI_DEKU_NUTS_10 },
|
|
{ RG_BUY_DEKU_STICK_1, SI_DEKU_STICK },
|
|
{ RG_BUY_BOMBS_10, SI_BOMBS_10 },
|
|
{ RG_BUY_FISH, SI_FISH },
|
|
{ RG_BUY_RED_POTION_30, SI_RED_POTION_R30 },
|
|
{ RG_BUY_GREEN_POTION, SI_GREEN_POTION },
|
|
{ RG_BUY_BLUE_POTION, SI_BLUE_POTION },
|
|
{ RG_BUY_HYLIAN_SHIELD, SI_HYLIAN_SHIELD },
|
|
{ RG_BUY_DEKU_SHIELD, SI_DEKU_SHIELD },
|
|
{ RG_BUY_GORON_TUNIC, SI_GORON_TUNIC },
|
|
{ RG_BUY_ZORA_TUNIC, SI_ZORA_TUNIC },
|
|
{ RG_BUY_HEART, SI_HEART },
|
|
{ RG_BUY_BOMBCHU_10, SI_BOMBCHU_10_1 },
|
|
{ RG_BUY_BOMBCHU_20, SI_BOMBCHU_20_1 },
|
|
{ RG_BUY_DEKU_SEEDS_30, SI_DEKU_SEEDS_30 },
|
|
{ RG_BUY_BLUE_FIRE, SI_BLUE_FIRE },
|
|
{ RG_BUY_BOTTLE_BUG, SI_BUGS },
|
|
{ RG_BUY_POE, SI_POE },
|
|
{ RG_BUY_FAIRYS_SPIRIT, SI_FAIRY },
|
|
{ RG_BUY_ARROWS_10, SI_ARROWS_10 },
|
|
{ RG_BUY_BOMBS_20, SI_BOMBS_20 },
|
|
{ RG_BUY_BOMBS_30, SI_BOMBS_30 },
|
|
{ RG_BUY_BOMBS_535, SI_BOMBS_5_R35 },
|
|
{ RG_BUY_RED_POTION_40, SI_RED_POTION_R40 },
|
|
{ RG_BUY_RED_POTION_50, SI_RED_POTION_R50 },
|
|
};
|
|
|
|
void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
|
|
CustomMessageManager::Instance->ClearMessageTable(Randomizer::merchantMessageTableID);
|
|
CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::merchantMessageTableID);
|
|
|
|
// Prices have a chance of being 0, and the "sell" message below doesn't really make sense for a free item, so adding a "free" variation here
|
|
CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE,
|
|
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
|
|
"\x12\x38\x82" "All right! You win! In return for&sparing me, I will give you a&%g{{item}}%w!&Please, take it!\x07\x10\xA3",
|
|
"\x12\x38\x82" "In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen &%g{{item}}%w geben!\x07\x10\xA3",
|
|
"\x12\x38\x82" "J'me rends! Laisse-moi partir et en&échange, je te donne un &%g{{item}}%w! Vas-y prends le!\x07\x10\xA3",
|
|
});
|
|
CustomMessageManager::Instance->CreateMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM,
|
|
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
|
|
"\x12\x38\x82" "All right! You win! In return for&sparing me, I will sell you a&%g{{item}}%w!&%r{{price}} Rupees%w it is!\x07\x10\xA3",
|
|
"\x12\x38\x82" "Aufgeben! Ich verkaufe dir einen&%g{{item}}%w&für %r{{price}} Rubine%w!\x07\x10\xA3",
|
|
"\x12\x38\x82" "J'abandonne! Tu veux bien m'acheter&un %g{{item}}%w?&Ça fera %r{{price}} Rubis%w!\x07\x10\xA3"
|
|
});
|
|
CustomMessageManager::Instance->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",
|
|
});
|
|
|
|
|
|
//Setup for merchant text boxes
|
|
//Medigoron
|
|
//RANDOTODO: Implement obscure/ambiguous hints
|
|
CustomMessageManager::Instance->CreateMessage(
|
|
Randomizer::merchantMessageTableID, TEXT_MEDIGORON,
|
|
{
|
|
TEXTBOX_TYPE_BLACK,
|
|
TEXTBOX_POS_BOTTOM,
|
|
"How about buying %r&{{item}}%w for %g200 rupees%w?\x1B&%gYes&No%w",
|
|
"Wie wäre es mit %r&{{item}}%w für %g200 Rubine?%w\x1B&%gJa!&Nein!%w",
|
|
"Veux-tu acheter %r&{{item}}%w pour %g200 rubis?%w\x1B&%gOui&Non&w"
|
|
});
|
|
|
|
//Carpet Salesman
|
|
//RANDOTODO: Implement obscure/ambiguous hints
|
|
std::vector<std::string> cgBoxTwoText;
|
|
if (Randomizer::GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) == 2) { //If merchant hints are clear...
|
|
cgBoxTwoText = {
|
|
"!%w&It's real, I promise!&A lonely man such as myself&wouldn't %rlie%w to you, hmm?^",
|
|
"!%w&Ich kann versichern es ist ein&aufrichtiges Angebot!^Ein einsamer Mann wie ich würde dich&doch nicht %ranlügen%w, oder?^",
|
|
"!%w&C'est vrai! J'te jure!&Un gars comme moi ne te %rmentirai%w pas&tu ne crois pas?^"
|
|
};
|
|
} else {
|
|
cgBoxTwoText = {
|
|
"!%w&I won't tell you what it is until I see&the money...^",
|
|
"!%w&Erst kommt das Geld, dann die Ware...^",
|
|
"!%w&Je ne te dirai pas ce que c'est avant&d'être payé rubis sur l'ongle...^"
|
|
};
|
|
}
|
|
CustomMessageManager::Instance->CreateMessage(
|
|
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_1,
|
|
{
|
|
TEXTBOX_TYPE_BLACK,
|
|
TEXTBOX_POS_BOTTOM,
|
|
"Welcome!^I am selling stuff, strange and rare, &from all over the world to everybody.&Today's special is...^%r{{item}}" + cgBoxTwoText[0] +
|
|
"How about %g200 Rupees?%w\x1B&&%gYes&No%w",
|
|
"Sei gegrüßt!^Ich verkaufe allerlei Kuriorisäten.&Stets sonderliche und seltene Ware&aus aller Welt für jedermann.&Das heutige Angebot bleibt...^%r{{item}}" +
|
|
cgBoxTwoText[1] + "Wie wäre es mit %g200 Rubinen?%w\x1B&&%gJa!&Nein!%w",
|
|
"Bienvenue!^Je vends des trucs étranges et rares,&de partout dans le monde et à tout le&monde! L'objet du jour est...^%r{{item}}" +
|
|
cgBoxTwoText[2] + "Alors, marché conclu pour %g200 rubis?%w\x1B&&%gOui&Non%w"
|
|
}
|
|
);
|
|
|
|
CustomMessageManager::Instance->CreateMessage(
|
|
Randomizer::merchantMessageTableID, TEXT_CARPET_SALESMAN_2,
|
|
{
|
|
TEXTBOX_TYPE_BLACK,
|
|
TEXTBOX_POS_TOP,
|
|
"Finally! Now I can go back to being &an %rarms dealer!%w",
|
|
"Endlich! Schon bald kann ich wieder &%rKrabbelminen-Händler%w sein!",
|
|
"Squalala! Je vais enfin pouvoir &%rprendre des vacances!%w"
|
|
}
|
|
);
|
|
|
|
// Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are
|
|
// prompted buy/don't buy
|
|
CustomMessageManager::Instance->CreateMessage(
|
|
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE,
|
|
"\x08%r{{item}} {{price}} Rupees&%wSpecial deal! ONE LEFT!&Get it while it lasts!\x0A\x02",
|
|
"\x08%r{{item}} {{price}} Rubine&%wSonderangebot! NUR NOCH EINES VERFÜGBAR!&Beeilen Sie sich!\x0A\x02",
|
|
"\x08%r{{item}} {{price}} Rubis&%wOffre spéciale! DERNIER EN STOCK!&Faites vite!\x0A\x02",
|
|
});
|
|
CustomMessageManager::Instance->CreateMessage(
|
|
Randomizer::merchantMessageTableID, TEXT_SHOP_ITEM_RANDOM_CONFIRM, { TEXTBOX_TYPE_BLACK, TEXTBOX_POS_VARIABLE,
|
|
"\x08{{item}} {{price}} Rupees\x09&&\x1B%gBuy&Don't buy%w\x09\x02",
|
|
"\x08{{item}} {{price}} Rubine\x09&&\x1B%gKaufen&Nicht kaufen%w\x09\x02",
|
|
"\x08{{item}} {{price}} Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w\x09\x02",
|
|
});
|
|
}
|
|
|
|
void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
ParseItemLocationsFile(spoilerFileName, silent);
|
|
}
|
|
|
|
for (auto itemLocation : gSaveContext.itemLocations) {
|
|
this->itemLocations[itemLocation.check] = itemLocation.get;
|
|
}
|
|
|
|
itemLocations[RC_UNKNOWN_CHECK].rgID = itemLocations[RC_UNKNOWN_CHECK].fakeRgID = RG_NONE;
|
|
}
|
|
|
|
void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
ParseRequiredTrialsFile(spoilerFileName);
|
|
}
|
|
}
|
|
|
|
void Randomizer::LoadEntranceOverrides(const char* spoilerFileName, bool silent){
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
ParseEntranceDataFile(spoilerFileName, silent);
|
|
}
|
|
}
|
|
|
|
void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
|
|
if (strcmp(spoilerFileName, "") != 0) {
|
|
ParseMasterQuestDungeonsFile(spoilerFileName);
|
|
}
|
|
gSaveContext.mqDungeonCount = this->masterQuestDungeons.size();
|
|
}
|
|
|
|
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream)
|
|
return;
|
|
|
|
bool success = false;
|
|
|
|
try {
|
|
// clear out existing settings
|
|
for(size_t i = 0; i < RSK_MAX; i++) {
|
|
gSaveContext.randoSettings[i].key = RSK_NONE;
|
|
gSaveContext.randoSettings[i].value = 0;
|
|
}
|
|
|
|
json spoilerFileJson;
|
|
spoilerFileStream >> spoilerFileJson;
|
|
json settingsJson = spoilerFileJson["settings"];
|
|
|
|
for (auto it = settingsJson.begin(); it != settingsJson.end(); ++it) {
|
|
// todo load into cvars for UI
|
|
|
|
std::string numericValueString;
|
|
if(SpoilerfileSettingNameToEnum.count(it.key())) {
|
|
RandomizerSettingKey index = SpoilerfileSettingNameToEnum[it.key()];
|
|
gSaveContext.randoSettings[index].key = SpoilerfileSettingNameToEnum[it.key()];
|
|
// this is annoying but the same strings are used in different orders
|
|
// and i don't want the spoilerfile to just have numbers instead of
|
|
// human readable settings values so it'll have to do for now
|
|
switch(gSaveContext.randoSettings[index].key) {
|
|
case RSK_FOREST:
|
|
if(it.value() == "Closed") {
|
|
gSaveContext.randoSettings[index].value = RO_FOREST_CLOSED;
|
|
} else if(it.value() == "Open") {
|
|
gSaveContext.randoSettings[index].value = RO_FOREST_OPEN;
|
|
} else if(it.value() == "Closed Deku") {
|
|
gSaveContext.randoSettings[index].value = RO_FOREST_CLOSED_DEKU;
|
|
}
|
|
break;
|
|
case RSK_KAK_GATE:
|
|
if(it.value() == "Closed") {
|
|
gSaveContext.randoSettings[index].value = RO_KAK_GATE_CLOSED;
|
|
} else if(it.value() == "Open") {
|
|
gSaveContext.randoSettings[index].value = RO_KAK_GATE_OPEN;
|
|
}
|
|
break;
|
|
case RSK_DOOR_OF_TIME:
|
|
if(it.value() == "Open") {
|
|
gSaveContext.randoSettings[index].value = RO_DOOROFTIME_OPEN;
|
|
} else if(it.value() == "Song only") {
|
|
gSaveContext.randoSettings[index].value = RO_DOOROFTIME_SONGONLY;
|
|
} else if(it.value() == "Closed") {
|
|
gSaveContext.randoSettings[index].value = RO_DOOROFTIME_CLOSED;
|
|
}
|
|
break;
|
|
case RSK_ZORAS_FOUNTAIN:
|
|
if(it.value() == "Closed") {
|
|
gSaveContext.randoSettings[index].value = RO_ZF_CLOSED;
|
|
} else if(it.value() == "Closed as child") {
|
|
gSaveContext.randoSettings[index].value = RO_ZF_CLOSED_CHILD;
|
|
} else if(it.value() == "Open") {
|
|
gSaveContext.randoSettings[index].value = RO_ZF_OPEN;
|
|
}
|
|
break;
|
|
case RSK_STARTING_AGE:
|
|
if(it.value() == "Child") {
|
|
gSaveContext.randoSettings[index].value = RO_AGE_CHILD;
|
|
} else if (it.value() == "Adult") {
|
|
gSaveContext.randoSettings[index].value = RO_AGE_ADULT;
|
|
}
|
|
break;
|
|
case RSK_GERUDO_FORTRESS:
|
|
if(it.value() == "Normal") {
|
|
gSaveContext.randoSettings[index].value = RO_GF_NORMAL;
|
|
} else if(it.value() == "Fast") {
|
|
gSaveContext.randoSettings[index].value = RO_GF_FAST;
|
|
} else if(it.value() == "Open") {
|
|
gSaveContext.randoSettings[index].value = RO_GF_OPEN;
|
|
}
|
|
break;
|
|
case RSK_RAINBOW_BRIDGE:
|
|
if(it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_VANILLA;
|
|
} else if(it.value() == "Always open") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_ALWAYS_OPEN;
|
|
} else if(it.value() == "Stones") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_STONES;
|
|
} else if(it.value() == "Medallions") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_MEDALLIONS;
|
|
} else if(it.value() == "Dungeon rewards") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_DUNGEON_REWARDS;
|
|
} else if(it.value() == "Dungeons") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_DUNGEONS;
|
|
} else if(it.value() == "Tokens") {
|
|
gSaveContext.randoSettings[index].value = RO_BRIDGE_TOKENS;
|
|
}
|
|
break;
|
|
case RSK_RAINBOW_BRIDGE_STONE_COUNT:
|
|
case RSK_RAINBOW_BRIDGE_MEDALLION_COUNT:
|
|
case RSK_RAINBOW_BRIDGE_REWARD_COUNT:
|
|
case RSK_RAINBOW_BRIDGE_DUNGEON_COUNT:
|
|
case RSK_RAINBOW_BRIDGE_TOKEN_COUNT:
|
|
case RSK_LACS_STONE_COUNT:
|
|
case RSK_LACS_MEDALLION_COUNT:
|
|
case RSK_LACS_REWARD_COUNT:
|
|
case RSK_LACS_DUNGEON_COUNT:
|
|
case RSK_LACS_TOKEN_COUNT:
|
|
case RSK_BIG_POE_COUNT:
|
|
case RSK_CUCCO_COUNT:
|
|
case RSK_STARTING_SKULLTULA_TOKEN:
|
|
numericValueString = it.value();
|
|
gSaveContext.randoSettings[index].value = std::stoi(numericValueString);
|
|
break;
|
|
case RSK_SHOPSANITY:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_OFF;
|
|
} else if(it.value() == "0 Items") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_ZERO;
|
|
} else if(it.value() == "1 Item") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_ONE;
|
|
} else if(it.value() == "2 Items") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_TWO;
|
|
} else if(it.value() == "3 Items") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_THREE;
|
|
} else if(it.value() == "4 Items") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_FOUR;
|
|
} else if(it.value() == "Random") {
|
|
gSaveContext.randoSettings[index].value = SHOPSANITY_RANDOM;
|
|
}
|
|
break;
|
|
case RSK_SHUFFLE_SCRUBS:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_SCRUBS_OFF;
|
|
} else if(it.value() == "Affordable") {
|
|
gSaveContext.randoSettings[index].value = RO_SCRUBS_AFFORDABLE;
|
|
} else if(it.value() == "Expensive") {
|
|
gSaveContext.randoSettings[index].value = RO_SCRUBS_EXPENSIVE;
|
|
} else if(it.value() == "Random Prices") {
|
|
gSaveContext.randoSettings[index].value = RO_SCRUBS_RANDOM;
|
|
}
|
|
break;
|
|
case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD:
|
|
case RSK_SHUFFLE_COWS:
|
|
case RSK_SHUFFLE_ADULT_TRADE:
|
|
case RSK_SHUFFLE_MAGIC_BEANS:
|
|
case RSK_RANDOM_MQ_DUNGEONS:
|
|
case RSK_STARTING_DEKU_SHIELD:
|
|
case RSK_STARTING_KOKIRI_SWORD:
|
|
case RSK_COMPLETE_MASK_QUEST:
|
|
case RSK_SKIP_SCARECROWS_SONG:
|
|
case RSK_ENABLE_GLITCH_CUTSCENES:
|
|
case RSK_BLUE_FIRE_ARROWS:
|
|
case RSK_SUNLIGHT_ARROWS:
|
|
case RSK_BOMBCHUS_IN_LOGIC:
|
|
case RSK_SHUFFLE_ENTRANCES:
|
|
case RSK_SHUFFLE_OVERWORLD_ENTRANCES:
|
|
case RSK_SHUFFLE_GROTTO_ENTRANCES:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_OFF;
|
|
} else if(it.value() == "On") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_ON;
|
|
}
|
|
break;
|
|
case RSK_SHUFFLE_MERCHANTS:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_SHUFFLE_MERCHANTS_OFF;
|
|
} else if (it.value() == "On (No Hints)") {
|
|
gSaveContext.randoSettings[index].value = RO_SHUFFLE_MERCHANTS_ON_NO_HINT;
|
|
} else if (it.value() == "On (With Hints)") {
|
|
gSaveContext.randoSettings[index].value = RO_SHUFFLE_MERCHANTS_ON_HINT;
|
|
}
|
|
break;
|
|
// Uses Ammo Drops option for now. "Off" not yet implemented
|
|
case RSK_ENABLE_BOMBCHU_DROPS:
|
|
if (it.value() == "On") {
|
|
gSaveContext.randoSettings[index].value = RO_AMMO_DROPS_ON;
|
|
} else if (it.value() == "On + Bombchu") {
|
|
gSaveContext.randoSettings[index].value = RO_AMMO_DROPS_ON_PLUS_BOMBCHU;
|
|
} else if (it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_AMMO_DROPS_OFF;
|
|
}
|
|
break;
|
|
case RSK_STARTING_MAPS_COMPASSES:
|
|
if(it.value() == "Start With") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_STARTWITH;
|
|
} else if(it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_VANILLA;
|
|
} else if(it.value() == "Own Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OWN_DUNGEON;
|
|
} else if(it.value() == "Any Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANY_DUNGEON;
|
|
} else if(it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OVERWORLD;
|
|
} else if(it.value() == "Anywhere") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANYWHERE;
|
|
}
|
|
break;
|
|
case RSK_STARTING_OCARINA:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_STARTING_OCARINA_OFF;
|
|
} else if(it.value() == "Fairy Ocarina") {
|
|
gSaveContext.randoSettings[index].value = RO_STARTING_OCARINA_FAIRY;
|
|
}
|
|
break;
|
|
case RSK_ITEM_POOL:
|
|
if(it.value() == "Plentiful") {
|
|
gSaveContext.randoSettings[index].value = RO_ITEM_POOL_PLENTIFUL;
|
|
} else if(it.value() == "Balanced") {
|
|
gSaveContext.randoSettings[index].value = RO_ITEM_POOL_BALANCED;
|
|
} else if(it.value() == "Scarce") {
|
|
gSaveContext.randoSettings[index].value = RO_ITEM_POOL_SCARCE;
|
|
} else if(it.value() == "Minimal") {
|
|
gSaveContext.randoSettings[index].value = RO_ITEM_POOL_MINIMAL;
|
|
}
|
|
case RSK_ICE_TRAPS:
|
|
if(it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_ICE_TRAPS_OFF;
|
|
} else if(it.value() == "Normal") {
|
|
gSaveContext.randoSettings[index].value = RO_ICE_TRAPS_NORMAL;
|
|
} else if(it.value() == "Extra") {
|
|
gSaveContext.randoSettings[index].value = RO_ICE_TRAPS_EXTRA;
|
|
} else if(it.value() == "Mayhem") {
|
|
gSaveContext.randoSettings[index].value = RO_ICE_TRAPS_MAYHEM;
|
|
} else if(it.value() == "Onslaught") {
|
|
gSaveContext.randoSettings[index].value = RO_ICE_TRAPS_ONSLAUGHT;
|
|
}
|
|
case RSK_GOSSIP_STONE_HINTS:
|
|
if(it.value() == "No Hints") {
|
|
gSaveContext.randoSettings[index].value = RO_GOSSIP_STONES_NONE;
|
|
} else if(it.value() == "Need Nothing") {
|
|
gSaveContext.randoSettings[index].value = RO_GOSSIP_STONES_NEED_NOTHING;
|
|
} else if(it.value() == "Mask of Truth") {
|
|
gSaveContext.randoSettings[index].value = RO_GOSSIP_STONES_NEED_TRUTH;
|
|
} else if(it.value() == "Stone of Agony") {
|
|
gSaveContext.randoSettings[index].value = RO_GOSSIP_STONES_NEED_STONE;
|
|
}
|
|
break;
|
|
case RSK_HINT_CLARITY:
|
|
if(it.value() == "Obscure") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_CLARITY_OBSCURE;
|
|
} else if(it.value() == "Ambiguous") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_CLARITY_AMBIGUOUS;
|
|
} else if(it.value() == "Clear") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_CLARITY_CLEAR;
|
|
}
|
|
break;
|
|
case RSK_HINT_DISTRIBUTION:
|
|
if(it.value() == "Useless") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_DIST_USELESS;
|
|
} else if(it.value() == "Balanced") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_DIST_BALANCED;
|
|
} else if(it.value() == "Strong") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_DIST_STRONG;
|
|
} else if(it.value() == "Very Strong") {
|
|
gSaveContext.randoSettings[index].value = RO_HINT_DIST_VERY_STRONG;
|
|
}
|
|
break;
|
|
case RSK_GERUDO_KEYS:
|
|
if (it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_GERUDO_KEYS_VANILLA;
|
|
} else if (it.value() == "Any Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_GERUDO_KEYS_ANY_DUNGEON;
|
|
} else if (it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_GERUDO_KEYS_OVERWORLD;
|
|
} else if (it.value() == "Anywhere") {
|
|
gSaveContext.randoSettings[index].value = RO_GERUDO_KEYS_ANYWHERE;
|
|
}
|
|
break;
|
|
case RSK_KEYSANITY:
|
|
if(it.value() == "Start With") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_STARTWITH;
|
|
} else if(it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_VANILLA;
|
|
} else if(it.value() == "Own Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OWN_DUNGEON;
|
|
} else if(it.value() == "Any Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANY_DUNGEON;
|
|
} else if(it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OVERWORLD;
|
|
} else if(it.value() == "Anywhere") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANYWHERE;
|
|
}
|
|
break;
|
|
case RSK_BOSS_KEYSANITY:
|
|
if(it.value() == "Start With") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_STARTWITH;
|
|
} else if(it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_VANILLA;
|
|
} else if(it.value() == "Own Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OWN_DUNGEON;
|
|
} else if(it.value() == "Any Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANY_DUNGEON;
|
|
} else if(it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_OVERWORLD;
|
|
} else if(it.value() == "Anywhere") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ITEM_LOC_ANYWHERE;
|
|
}
|
|
break;
|
|
case RSK_GANONS_BOSS_KEY:
|
|
if(it.value() == "Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_VANILLA;
|
|
} else if(it.value() == "Own dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_OWN_DUNGEON;
|
|
} else if(it.value() == "Start with") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_STARTWITH;
|
|
} else if(it.value() == "Any Dungeon") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_ANY_DUNGEON;
|
|
} else if(it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_OVERWORLD;
|
|
} else if(it.value() == "Anywhere") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_ANYWHERE;
|
|
} else if(it.value() == "LACS-Vanilla") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_VANILLA;
|
|
} else if(it.value() == "LACS-Medallions") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_MEDALLIONS;
|
|
} else if(it.value() == "LACS-Stones") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_STONES;
|
|
} else if(it.value() == "LACS-Rewards") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_REWARDS;
|
|
} else if(it.value() == "LACS-Dungeons") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_DUNGEONS;
|
|
} else if(it.value() == "LACS-Tokens") {
|
|
gSaveContext.randoSettings[index].value = RO_GANON_BOSS_KEY_LACS_TOKENS;
|
|
}
|
|
break;
|
|
case RSK_SKIP_CHILD_ZELDA:
|
|
gSaveContext.randoSettings[index].value = it.value();
|
|
break;
|
|
case RSK_STARTING_CONSUMABLES:
|
|
case RSK_FULL_WALLETS:
|
|
if(it.value() == "No") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_NO;
|
|
} else if(it.value() == "Yes") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_YES;
|
|
}
|
|
break;
|
|
case RSK_SKIP_CHILD_STEALTH:
|
|
case RSK_SKIP_EPONA_RACE:
|
|
case RSK_SKIP_TOWER_ESCAPE:
|
|
if(it.value() == "Don't Skip") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_DONT_SKIP;
|
|
} else if (it.value() == "Skip") {
|
|
gSaveContext.randoSettings[index].value = RO_GENERIC_SKIP;
|
|
}
|
|
break;
|
|
case RSK_SHUFFLE_TOKENS:
|
|
if (it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_TOKENSANITY_OFF;
|
|
} else if (it.value() == "Dungeons") {
|
|
gSaveContext.randoSettings[index].value = RO_TOKENSANITY_DUNGEONS;
|
|
} else if (it.value() == "Overworld") {
|
|
gSaveContext.randoSettings[index].value = RO_TOKENSANITY_OVERWORLD;
|
|
} else if (it.value() == "All Tokens") {
|
|
gSaveContext.randoSettings[index].value = RO_TOKENSANITY_ALL;
|
|
}
|
|
break;
|
|
case RSK_LINKS_POCKET:
|
|
if (it.value() == "Dungeon Reward") {
|
|
gSaveContext.randoSettings[index].value = RO_LINKS_POCKET_DUNGEON_REWARD;
|
|
} else if (it.value() == "Advancement") {
|
|
gSaveContext.randoSettings[index].value = RO_LINKS_POCKET_ADVANCEMENT;
|
|
} else if (it.value() == "Anything") {
|
|
gSaveContext.randoSettings[index].value = RO_LINKS_POCKET_ANYTHING;
|
|
} else if (it.value() == "Nothing") {
|
|
gSaveContext.randoSettings[index].value = RO_LINKS_POCKET_NOTHING;
|
|
}
|
|
break;
|
|
case RSK_MQ_DUNGEON_COUNT:
|
|
if (it.value() == "Random") {
|
|
gSaveContext.randoSettings[index].value = 13;
|
|
break;
|
|
}
|
|
numericValueString = it.value();
|
|
gSaveContext.randoSettings[index].value = std::stoi(numericValueString);
|
|
break;
|
|
case RSK_SHUFFLE_DUNGEON_ENTRANCES:
|
|
if (it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ENTRANCE_SHUFFLE_OFF;
|
|
} else if (it.value() == "On") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ENTRANCE_SHUFFLE_ON;
|
|
} else if (it.value() == "On + Ganon") {
|
|
gSaveContext.randoSettings[index].value = RO_DUNGEON_ENTRANCE_SHUFFLE_ON_PLUS_GANON;
|
|
}
|
|
break;
|
|
case RSK_SHUFFLE_INTERIOR_ENTRANCES:
|
|
if (it.value() == "Off") {
|
|
gSaveContext.randoSettings[index].value = RO_INTERIOR_ENTRANCE_SHUFFLE_OFF;
|
|
} else if (it.value() == "Simple") {
|
|
gSaveContext.randoSettings[index].value = RO_INTERIOR_ENTRANCE_SHUFFLE_SIMPLE;
|
|
} else if (it.value() == "All") {
|
|
gSaveContext.randoSettings[index].value = RO_INTERIOR_ENTRANCE_SHUFFLE_ALL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
success = true;
|
|
} catch (const std::exception& e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::string AltarIconString(char iconChar) {
|
|
std::string iconString = "";
|
|
switch (iconChar) {
|
|
case '0':
|
|
// Kokiri Emerald
|
|
iconString += 0x13;
|
|
iconString += 0x6C;
|
|
break;
|
|
case '1':
|
|
// Goron Ruby
|
|
iconString += 0x13;
|
|
iconString += 0x6D;
|
|
break;
|
|
case '2':
|
|
// Zora Sapphire
|
|
iconString += 0x13;
|
|
iconString += 0x6E;
|
|
break;
|
|
case '3':
|
|
// Forest Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x66;
|
|
break;
|
|
case '4':
|
|
// Fire Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x67;
|
|
break;
|
|
case '5':
|
|
// Water Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x68;
|
|
break;
|
|
case '6':
|
|
// Spirit Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x69;
|
|
break;
|
|
case '7':
|
|
// Shadow Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x6A;
|
|
break;
|
|
case '8':
|
|
// Light Medallion
|
|
iconString += 0x13;
|
|
iconString += 0x6B;
|
|
break;
|
|
case 'o':
|
|
// Open DOT (master sword)
|
|
iconString += 0x13;
|
|
iconString += 0x3C;
|
|
break;
|
|
case 'c':
|
|
// Closed DOT (fairy ocarina)
|
|
iconString += 0x13;
|
|
iconString += 0x07;
|
|
break;
|
|
case 'i':
|
|
// Intended DOT (oot)
|
|
iconString += 0x13;
|
|
iconString += 0x08;
|
|
break;
|
|
case 'l':
|
|
// Light Arrow (for bridge reqs)
|
|
iconString += 0x13;
|
|
iconString += 0x12;
|
|
break;
|
|
case 'b':
|
|
// Boss Key (ganon boss key location)
|
|
iconString += 0x13;
|
|
iconString += 0x74;
|
|
break;
|
|
case 'L':
|
|
// Bow with Light Arrow
|
|
iconString += 0x13;
|
|
iconString += 0x3A;
|
|
break;
|
|
case 'k':
|
|
// Kokiri Tunic
|
|
iconString += 0x13;
|
|
iconString += 0x41;
|
|
break;
|
|
}
|
|
return iconString;
|
|
}
|
|
|
|
std::string FormatJsonHintText(std::string jsonHint) {
|
|
std::string formattedHintMessage = jsonHint;
|
|
|
|
// add icons to altar text
|
|
for (char iconChar : {'0', '1', '2', '3', '4', '5', '6', '7', '8', 'o', 'c', 'i', 'l', 'b', 'L', 'k'}) {
|
|
std::string textToReplace = "$";
|
|
textToReplace += iconChar;
|
|
size_t start_pos = formattedHintMessage.find(textToReplace);
|
|
if(!(start_pos == std::string::npos)) {
|
|
std::string iconString = AltarIconString(iconChar);
|
|
formattedHintMessage.replace(start_pos, textToReplace.length(), iconString);
|
|
}
|
|
}
|
|
|
|
formattedHintMessage += 0x02;
|
|
|
|
return formattedHintMessage;
|
|
}
|
|
|
|
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream)
|
|
return;
|
|
|
|
bool success = false;
|
|
|
|
// Have all these use strncpy so that the null terminator is copied
|
|
// and also set the last index to null for safety
|
|
try {
|
|
json spoilerFileJson;
|
|
spoilerFileStream >> spoilerFileJson;
|
|
|
|
std::string childAltarJsonText = spoilerFileJson["childAltarText"].get<std::string>();
|
|
std::string formattedChildAltarText = FormatJsonHintText(childAltarJsonText);
|
|
strncpy(gSaveContext.childAltarText, formattedChildAltarText.c_str(), sizeof(gSaveContext.childAltarText) - 1);
|
|
gSaveContext.childAltarText[sizeof(gSaveContext.childAltarText) - 1] = 0;
|
|
|
|
std::string adultAltarJsonText = spoilerFileJson["adultAltarText"].get<std::string>();
|
|
std::string formattedAdultAltarText = FormatJsonHintText(adultAltarJsonText);
|
|
strncpy(gSaveContext.adultAltarText, formattedAdultAltarText.c_str(), sizeof(gSaveContext.adultAltarText) - 1);
|
|
gSaveContext.adultAltarText[sizeof(gSaveContext.adultAltarText) - 1] = 0;
|
|
|
|
std::string ganonHintJsonText = spoilerFileJson["ganonHintText"].get<std::string>();
|
|
std::string formattedGanonHintJsonText = FormatJsonHintText(ganonHintJsonText);
|
|
strncpy(gSaveContext.ganonHintText, formattedGanonHintJsonText.c_str(), sizeof(gSaveContext.ganonHintText) - 1);
|
|
gSaveContext.ganonHintText[sizeof(gSaveContext.ganonHintText) - 1] = 0;
|
|
|
|
std::string ganonJsonText = spoilerFileJson["ganonText"].get<std::string>();
|
|
std::string formattedGanonJsonText = FormatJsonHintText(ganonJsonText);
|
|
strncpy(gSaveContext.ganonText, formattedGanonJsonText.c_str(), sizeof(gSaveContext.ganonText) - 1);
|
|
gSaveContext.ganonText[sizeof(gSaveContext.ganonText) - 1] = 0;
|
|
|
|
json hintsJson = spoilerFileJson["hints"];
|
|
int index = 0;
|
|
for (auto it = hintsJson.begin(); it != hintsJson.end(); ++it) {
|
|
gSaveContext.hintLocations[index].check = SpoilerfileCheckNameToEnum[it.key()];
|
|
|
|
std::string hintMessage = FormatJsonHintText(it.value());
|
|
size_t maxHintTextSize = sizeof(gSaveContext.hintLocations[index].hintText);
|
|
strncpy(gSaveContext.hintLocations[index].hintText, hintMessage.c_str(), maxHintTextSize - 1);
|
|
gSaveContext.hintLocations[index].hintText[maxHintTextSize - 1] = 0;
|
|
|
|
index++;
|
|
}
|
|
|
|
success = true;
|
|
} catch (const std::exception& e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream) {
|
|
return;
|
|
}
|
|
|
|
this->trialsRequired.clear();
|
|
|
|
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::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream) {
|
|
return;
|
|
}
|
|
|
|
this->masterQuestDungeons.clear();
|
|
|
|
try {
|
|
json spoilerFileJson;
|
|
spoilerFileStream >> spoilerFileJson;
|
|
json mqDungeonsJson = spoilerFileJson["masterQuestDungeons"];
|
|
|
|
for (auto it = mqDungeonsJson.begin(); it != mqDungeonsJson.end(); it++) {
|
|
this->masterQuestDungeons.emplace(spoilerFileDungeonToScene[it.value()]);
|
|
}
|
|
} catch (const std::exception& e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream)
|
|
return;
|
|
|
|
bool success = false;
|
|
|
|
try {
|
|
json spoilerFileJson;
|
|
spoilerFileStream >> spoilerFileJson;
|
|
json locationsJson = spoilerFileJson["locations"];
|
|
json hashJson = spoilerFileJson["file_hash"];
|
|
|
|
int index = 0;
|
|
for (auto it = hashJson.begin(); it != hashJson.end(); ++it) {
|
|
gSaveContext.seedIcons[index] = gSeedTextures[it.value()].id;
|
|
index++;
|
|
}
|
|
|
|
for (auto it = locationsJson.begin(); it != locationsJson.end(); ++it) {
|
|
RandomizerCheck randomizerCheck = SpoilerfileCheckNameToEnum[it.key()];
|
|
if (it->is_structured()) {
|
|
json itemJson = *it;
|
|
for (auto itemit = itemJson.begin(); itemit != itemJson.end(); ++itemit) {
|
|
// todo handle prices
|
|
if (itemit.key() == "item") {
|
|
gSaveContext.itemLocations[randomizerCheck].check = randomizerCheck;
|
|
gSaveContext.itemLocations[randomizerCheck].get.rgID = SpoilerfileGetNameToEnum[itemit.value()];
|
|
} else if (itemit.key() == "price") {
|
|
merchantPrices[gSaveContext.itemLocations[randomizerCheck].check] = itemit.value();
|
|
} else if (itemit.key() == "model") {
|
|
gSaveContext.itemLocations[randomizerCheck].get.fakeRgID =
|
|
SpoilerfileGetNameToEnum[itemit.value()];
|
|
} else if (itemit.key() == "trickName") {
|
|
strncpy(gSaveContext.itemLocations[randomizerCheck].get.trickName,
|
|
std::string(itemit.value()).c_str(), MAX_TRICK_NAME_SIZE);
|
|
}
|
|
}
|
|
} else {
|
|
gSaveContext.itemLocations[randomizerCheck].check = SpoilerfileCheckNameToEnum[it.key()];
|
|
gSaveContext.itemLocations[randomizerCheck].get.rgID = SpoilerfileGetNameToEnum[it.value()];
|
|
gSaveContext.itemLocations[randomizerCheck].get.fakeRgID = RG_NONE;
|
|
}
|
|
}
|
|
|
|
if(!silent) {
|
|
Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
|
}
|
|
success = true;
|
|
} catch (const std::exception& e) {
|
|
Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) {
|
|
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
|
if (!spoilerFileStream) {
|
|
return;
|
|
}
|
|
|
|
// set all the entrances to be 0 to indicate an unshuffled entrance
|
|
for (auto &entranceOveride : gSaveContext.entranceOverrides) {
|
|
entranceOveride.index = 0;
|
|
entranceOveride.destination = 0;
|
|
entranceOveride.blueWarp = 0;
|
|
entranceOveride.override = 0;
|
|
entranceOveride.overrideDestination = 0;
|
|
}
|
|
|
|
try {
|
|
json spoilerFileJson;
|
|
spoilerFileStream >> spoilerFileJson;
|
|
json EntrancesJson = spoilerFileJson["entrances"];
|
|
|
|
size_t i = 0;
|
|
for (auto it = EntrancesJson.begin(); it != EntrancesJson.end(); ++it, i++) {
|
|
json entranceJson = *it;
|
|
|
|
for (auto entranceIt = entranceJson.begin(); entranceIt != entranceJson.end(); ++entranceIt) {
|
|
if (entranceIt.key() == "index") {
|
|
gSaveContext.entranceOverrides[i].index = entranceIt.value();
|
|
} else if (entranceIt.key() == "destination") {
|
|
gSaveContext.entranceOverrides[i].destination = entranceIt.value();
|
|
} else if (entranceIt.key() == "blueWarp") {
|
|
gSaveContext.entranceOverrides[i].blueWarp = entranceIt.value();
|
|
} else if (entranceIt.key() == "override") {
|
|
gSaveContext.entranceOverrides[i].override = entranceIt.value();
|
|
} else if (entranceIt.key() == "overrideDestination") {
|
|
gSaveContext.entranceOverrides[i].overrideDestination = entranceIt.value();
|
|
}
|
|
}
|
|
}
|
|
} catch (const std::exception& e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool Randomizer::IsTrialRequired(RandomizerInf trial) {
|
|
return this->trialsRequired.contains(trial);
|
|
}
|
|
|
|
RandomizerGetData Randomizer::GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {
|
|
return this->itemLocations[GetCheckFromActor(actorId, sceneNum, actorParams)];
|
|
}
|
|
|
|
RandomizerGetData Randomizer::GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck) {
|
|
return this->itemLocations[randomizerCheck];
|
|
}
|
|
|
|
GetItemEntry Randomizer::GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId, bool checkObtainability) {
|
|
RandomizerGetData rgData = this->itemLocations[GetCheckFromActor(actorId, sceneNum, actorParams)];
|
|
return GetItemEntryFromRGData(rgData, ogItemId, checkObtainability);
|
|
}
|
|
|
|
ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck) {
|
|
return GetItemObtainabilityFromRandomizerGet(GetRandomizerGetDataFromKnownCheck(randomizerCheck).rgID);
|
|
}
|
|
|
|
ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGet randoGet) {
|
|
|
|
// Shopsanity with at least one item shuffled allows for a third wallet upgrade.
|
|
// This is needed since Plentiful item pool also adds a third progressive wallet
|
|
// but we should *not* get Tycoon's Wallet in that mode.
|
|
u8 numWallets = GetRandoSettingValue(RSK_SHOPSANITY) > 1 ? 3 : 2;
|
|
switch (randoGet) {
|
|
case RG_NONE:
|
|
case RG_TRIFORCE:
|
|
case RG_HINT:
|
|
case RG_MAX:
|
|
case RG_SOLD_OUT:
|
|
return CANT_OBTAIN_MISC;
|
|
|
|
// Equipment
|
|
case RG_KOKIRI_SWORD:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_SWORD, 0) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BIGGORON_SWORD:
|
|
return !gSaveContext.bgsFlag ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DEKU_SHIELD:
|
|
case RG_BUY_DEKU_SHIELD:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_SHIELD, 0) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_HYLIAN_SHIELD:
|
|
case RG_BUY_HYLIAN_SHIELD:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_SHIELD, 1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_MIRROR_SHIELD:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_SHIELD, 2) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GORON_TUNIC:
|
|
case RG_BUY_GORON_TUNIC:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ZORA_TUNIC:
|
|
case RG_BUY_ZORA_TUNIC:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_TUNIC, 2) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_IRON_BOOTS:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_BOOTS, 1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_HOVER_BOOTS:
|
|
return !CHECK_OWNED_EQUIP(EQUIP_BOOTS, 2) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Inventory Items
|
|
case RG_PROGRESSIVE_STICK_UPGRADE:
|
|
return CUR_UPG_VALUE(UPG_STICKS) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_NUT_UPGRADE:
|
|
return CUR_UPG_VALUE(UPG_NUTS) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_BOMB_BAG:
|
|
return CUR_UPG_VALUE(UPG_BOMB_BAG) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BOMBS_5:
|
|
case RG_BOMBS_10:
|
|
case RG_BOMBS_20:
|
|
case RG_BUY_BOMBS_525:
|
|
case RG_BUY_BOMBS_535:
|
|
case RG_BUY_BOMBS_10:
|
|
case RG_BUY_BOMBS_20:
|
|
case RG_BUY_BOMBS_30:
|
|
return CUR_UPG_VALUE(UPG_BOMB_BAG) ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE;
|
|
case RG_PROGRESSIVE_BOW:
|
|
return CUR_UPG_VALUE(UPG_QUIVER) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ARROWS_5:
|
|
case RG_ARROWS_10:
|
|
case RG_ARROWS_30:
|
|
case RG_BUY_ARROWS_10:
|
|
case RG_BUY_ARROWS_30:
|
|
case RG_BUY_ARROWS_50:
|
|
return CUR_UPG_VALUE(UPG_QUIVER) ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE;
|
|
case RG_PROGRESSIVE_SLINGSHOT:
|
|
return CUR_UPG_VALUE(UPG_BULLET_BAG) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DEKU_SEEDS_30:
|
|
case RG_BUY_DEKU_SEEDS_30:
|
|
return CUR_UPG_VALUE(UPG_BULLET_BAG) ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE;
|
|
case RG_PROGRESSIVE_OCARINA:
|
|
switch (INV_CONTENT(ITEM_OCARINA_FAIRY)) {
|
|
case ITEM_NONE:
|
|
case ITEM_OCARINA_FAIRY:
|
|
return CAN_OBTAIN;
|
|
case ITEM_OCARINA_TIME:
|
|
default:
|
|
return CANT_OBTAIN_ALREADY_HAVE;
|
|
}
|
|
case RG_BOMBCHU_5:
|
|
case RG_BOMBCHU_10:
|
|
case RG_BOMBCHU_20:
|
|
case RG_PROGRESSIVE_BOMBCHUS:
|
|
return CAN_OBTAIN;
|
|
case RG_BUY_BOMBCHU_10:
|
|
case RG_BUY_BOMBCHU_20:
|
|
case RG_BOMBCHU_DROP:
|
|
// If Bombchus aren't in logic, you need a bomb bag to purchase them
|
|
// If they are in logic, you need to have already obtained them somewhere else
|
|
// Bombchu Drop is only used as a bowling reward, so it needs the same logic
|
|
if (GetRandoSettingValue(RSK_BOMBCHUS_IN_LOGIC)) {
|
|
return INV_CONTENT(ITEM_BOMBCHU) == ITEM_BOMBCHU ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE;
|
|
} else {
|
|
return CUR_UPG_VALUE(UPG_BOMB_BAG) ? CAN_OBTAIN : CANT_OBTAIN_NEED_UPGRADE;
|
|
}
|
|
case RG_PROGRESSIVE_HOOKSHOT:
|
|
switch (INV_CONTENT(ITEM_HOOKSHOT)) {
|
|
case ITEM_NONE:
|
|
case ITEM_HOOKSHOT:
|
|
return CAN_OBTAIN;
|
|
case ITEM_LONGSHOT:
|
|
default:
|
|
return CANT_OBTAIN_ALREADY_HAVE;
|
|
}
|
|
case RG_BOOMERANG:
|
|
return INV_CONTENT(ITEM_BOOMERANG) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_LENS_OF_TRUTH:
|
|
return INV_CONTENT(ITEM_LENS) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_MAGIC_BEAN:
|
|
case RG_MAGIC_BEAN_PACK:
|
|
return AMMO(ITEM_BEAN) < 10 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_MEGATON_HAMMER:
|
|
return INV_CONTENT(ITEM_HAMMER) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_ARROWS:
|
|
return INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ICE_ARROWS:
|
|
return INV_CONTENT(ITEM_ARROW_ICE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_LIGHT_ARROWS:
|
|
return INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DINS_FIRE:
|
|
return INV_CONTENT(ITEM_DINS_FIRE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FARORES_WIND:
|
|
return INV_CONTENT(ITEM_FARORES_WIND) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_NAYRUS_LOVE:
|
|
return INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Bottles
|
|
case RG_EMPTY_BOTTLE:
|
|
case RG_BOTTLE_WITH_MILK:
|
|
case RG_BOTTLE_WITH_RED_POTION:
|
|
case RG_BOTTLE_WITH_GREEN_POTION:
|
|
case RG_BOTTLE_WITH_BLUE_POTION:
|
|
case RG_BOTTLE_WITH_FAIRY:
|
|
case RG_BOTTLE_WITH_FISH:
|
|
case RG_BOTTLE_WITH_BLUE_FIRE:
|
|
case RG_BOTTLE_WITH_BUGS:
|
|
case RG_BOTTLE_WITH_POE:
|
|
case RG_RUTOS_LETTER:
|
|
case RG_BOTTLE_WITH_BIG_POE:
|
|
return Inventory_HasEmptyBottleSlot() ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Bottle Refills
|
|
case RG_MILK:
|
|
case RG_RED_POTION_REFILL:
|
|
case RG_GREEN_POTION_REFILL:
|
|
case RG_BLUE_POTION_REFILL:
|
|
case RG_BUY_FISH:
|
|
case RG_BUY_RED_POTION_30:
|
|
case RG_BUY_GREEN_POTION:
|
|
case RG_BUY_BLUE_POTION:
|
|
case RG_BUY_BLUE_FIRE:
|
|
case RG_BUY_BOTTLE_BUG:
|
|
case RG_BUY_POE:
|
|
case RG_BUY_FAIRYS_SPIRIT:
|
|
case RG_BUY_RED_POTION_40:
|
|
case RG_BUY_RED_POTION_50:
|
|
return Inventory_HasEmptyBottle() ? CAN_OBTAIN : CANT_OBTAIN_NEED_EMPTY_BOTTLE;
|
|
|
|
// Trade Items
|
|
// TODO: Do we want to be strict about any of this?
|
|
// case RG_WEIRD_EGG:
|
|
// case RG_ZELDAS_LETTER:
|
|
// case RG_POCKET_EGG:
|
|
// case RG_COJIRO:
|
|
// case RG_ODD_MUSHROOM:
|
|
// case RG_ODD_POTION:
|
|
// case RG_POACHERS_SAW:
|
|
// case RG_BROKEN_SWORD:
|
|
// case RG_PRESCRIPTION:
|
|
// case RG_EYEBALL_FROG:
|
|
// case RG_EYEDROPS:
|
|
// case RG_CLAIM_CHECK:
|
|
// case RG_PROGRESSIVE_GORONSWORD:
|
|
// case RG_GIANTS_KNIFE:
|
|
|
|
// Misc Items
|
|
case RG_STONE_OF_AGONY:
|
|
return !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GERUDO_MEMBERSHIP_CARD:
|
|
return !CHECK_QUEST_ITEM(QUEST_GERUDO_CARD) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DOUBLE_DEFENSE:
|
|
return !gSaveContext.isDoubleDefenseAcquired ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GOLD_SKULLTULA_TOKEN:
|
|
return gSaveContext.inventory.gsTokens < 100 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_STRENGTH:
|
|
return CUR_UPG_VALUE(UPG_STRENGTH) < 3 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_WALLET:
|
|
return CUR_UPG_VALUE(UPG_WALLET) < numWallets ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_SCALE:
|
|
return CUR_UPG_VALUE(UPG_SCALE) < 2 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PROGRESSIVE_MAGIC_METER:
|
|
case RG_MAGIC_SINGLE:
|
|
case RG_MAGIC_DOUBLE:
|
|
return gSaveContext.magicLevel < 2 ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Songs
|
|
case RG_ZELDAS_LULLABY:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_EPONAS_SONG:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_EPONA) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SARIAS_SONG:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_SARIA) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SUNS_SONG:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_SUN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SONG_OF_TIME:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_TIME) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SONG_OF_STORMS:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_STORMS) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_MINUET_OF_FOREST:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_MINUET) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BOLERO_OF_FIRE:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_BOLERO) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SERENADE_OF_WATER:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_SERENADE) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_REQUIEM_OF_SPIRIT:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_REQUIEM) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_NOCTURNE_OF_SHADOW:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_NOCTURNE) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_PRELUDE_OF_LIGHT:
|
|
return !CHECK_QUEST_ITEM(QUEST_SONG_PRELUDE) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Dungeon Items
|
|
case RG_DEKU_TREE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_YDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DODONGOS_CAVERN_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_DDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_JABU_JABUS_BELLY_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_BDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FOREST_TEMPLE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_BMORI1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_TEMPLE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_HIDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_WATER_TEMPLE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_MIZUSIN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SPIRIT_TEMPLE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_JYASINZOU) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SHADOW_TEMPLE_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_HAKADAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BOTTOM_OF_THE_WELL_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_HAKADANCH) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ICE_CAVERN_MAP:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_MAP, SCENE_ICE_DOUKUTO) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DEKU_TREE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_YDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_DODONGOS_CAVERN_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_DDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_JABU_JABUS_BELLY_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_BDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FOREST_TEMPLE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_BMORI1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_TEMPLE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_HIDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_WATER_TEMPLE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_MIZUSIN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SPIRIT_TEMPLE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_JYASINZOU) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SHADOW_TEMPLE_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_HAKADAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BOTTOM_OF_THE_WELL_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_HAKADANCH) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ICE_CAVERN_COMPASS:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, SCENE_ICE_DOUKUTO) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FOREST_TEMPLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_BMORI1) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_TEMPLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_HIDAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_WATER_TEMPLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_MIZUSIN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SPIRIT_TEMPLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_JYASINZOU) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SHADOW_TEMPLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_HAKADAN) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GANONS_CASTLE_BOSS_KEY:
|
|
return !CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, SCENE_GANON) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FOREST_TEMPLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_BMORI1] < FOREST_TEMPLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_TEMPLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_HIDAN] < FIRE_TEMPLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_WATER_TEMPLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_MIZUSIN] < WATER_TEMPLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SPIRIT_TEMPLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_JYASINZOU] < SPIRIT_TEMPLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SHADOW_TEMPLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_HAKADAN] < SHADOW_TEMPLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_HAKADANCH] < BOTTOM_OF_THE_WELL_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_MEN] < GERUDO_TRAINING_GROUNDS_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GERUDO_FORTRESS_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_GERUDOWAY] < GERUDO_FORTRESS_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GANONS_CASTLE_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_GANONTIKA] < GANONS_CASTLE_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_TREASURE_GAME_SMALL_KEY:
|
|
return gSaveContext.inventory.dungeonKeys[SCENE_TAKARAYA] < TREASURE_GAME_SMALL_KEY_MAX ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
// Dungeon Rewards
|
|
case RG_KOKIRI_EMERALD:
|
|
return !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_GORON_RUBY:
|
|
return !CHECK_QUEST_ITEM(QUEST_GORON_RUBY) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_ZORA_SAPPHIRE:
|
|
return !CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FOREST_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_FIRE_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_WATER_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SPIRIT_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_SHADOW_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
case RG_LIGHT_MEDALLION:
|
|
return !CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT) ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
|
|
|
|
case RG_RECOVERY_HEART:
|
|
case RG_GREEN_RUPEE:
|
|
case RG_BLUE_RUPEE:
|
|
case RG_RED_RUPEE:
|
|
case RG_PURPLE_RUPEE:
|
|
case RG_HUGE_RUPEE:
|
|
case RG_PIECE_OF_HEART:
|
|
case RG_HEART_CONTAINER:
|
|
case RG_ICE_TRAP:
|
|
case RG_DEKU_NUTS_5:
|
|
case RG_DEKU_NUTS_10:
|
|
case RG_DEKU_STICK_1:
|
|
case RG_TREASURE_GAME_HEART:
|
|
case RG_TREASURE_GAME_GREEN_RUPEE:
|
|
case RG_BUY_DEKU_NUT_5:
|
|
case RG_BUY_DEKU_NUT_10:
|
|
case RG_BUY_DEKU_STICK_1:
|
|
case RG_BUY_HEART:
|
|
default:
|
|
return CAN_OBTAIN;
|
|
}
|
|
}
|
|
|
|
GetItemID Randomizer::GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId) {
|
|
switch (randoGet) {
|
|
case RG_NONE:
|
|
return ogItemId;
|
|
case RG_TRIFORCE:
|
|
case RG_HINT:
|
|
case RG_MAX:
|
|
case RG_SOLD_OUT:
|
|
return GI_NONE;
|
|
|
|
// Equipment
|
|
case RG_KOKIRI_SWORD:
|
|
return GI_SWORD_KOKIRI;
|
|
case RG_PROGRESSIVE_GORONSWORD: //todo progressive?
|
|
return GI_SWORD_BGS;
|
|
case RG_GIANTS_KNIFE:
|
|
return GI_SWORD_KNIFE;
|
|
case RG_BIGGORON_SWORD:
|
|
return GI_SWORD_BGS;
|
|
case RG_DEKU_SHIELD:
|
|
case RG_BUY_DEKU_SHIELD:
|
|
return GI_SHIELD_DEKU;
|
|
case RG_HYLIAN_SHIELD:
|
|
case RG_BUY_HYLIAN_SHIELD:
|
|
return GI_SHIELD_HYLIAN;
|
|
case RG_MIRROR_SHIELD:
|
|
return GI_SHIELD_MIRROR;
|
|
case RG_GORON_TUNIC:
|
|
case RG_BUY_GORON_TUNIC:
|
|
return GI_TUNIC_GORON;
|
|
case RG_ZORA_TUNIC:
|
|
case RG_BUY_ZORA_TUNIC:
|
|
return GI_TUNIC_ZORA;
|
|
case RG_IRON_BOOTS:
|
|
return GI_BOOTS_IRON;
|
|
case RG_HOVER_BOOTS:
|
|
return GI_BOOTS_HOVER;
|
|
|
|
// Inventory Items
|
|
case RG_PROGRESSIVE_STICK_UPGRADE:
|
|
switch (CUR_UPG_VALUE(UPG_STICKS)) {
|
|
case 0:
|
|
case 1:
|
|
return GI_STICK_UPGRADE_20;
|
|
case 2:
|
|
return GI_STICK_UPGRADE_30;
|
|
}
|
|
case RG_PROGRESSIVE_NUT_UPGRADE:
|
|
switch (CUR_UPG_VALUE(UPG_NUTS)) {
|
|
case 0:
|
|
case 1:
|
|
return GI_NUT_UPGRADE_30;
|
|
case 2:
|
|
return GI_NUT_UPGRADE_40;
|
|
}
|
|
case RG_PROGRESSIVE_BOMB_BAG:
|
|
switch (CUR_UPG_VALUE(UPG_BOMB_BAG)) {
|
|
case 0:
|
|
return GI_BOMB_BAG_20;
|
|
case 1:
|
|
return GI_BOMB_BAG_30;
|
|
case 2:
|
|
return GI_BOMB_BAG_40;
|
|
}
|
|
case RG_BOMBS_5:
|
|
case RG_BUY_BOMBS_525:
|
|
case RG_BUY_BOMBS_535:
|
|
return GI_BOMBS_5;
|
|
case RG_BOMBS_10:
|
|
case RG_BUY_BOMBS_10:
|
|
return GI_BOMBS_10;
|
|
case RG_BOMBS_20:
|
|
case RG_BUY_BOMBS_20:
|
|
return GI_BOMBS_20;
|
|
case RG_BUY_BOMBS_30:
|
|
return GI_BOMBS_30;
|
|
case RG_PROGRESSIVE_BOW:
|
|
switch (CUR_UPG_VALUE(UPG_QUIVER)) {
|
|
case 0:
|
|
return GI_BOW;
|
|
case 1:
|
|
return GI_QUIVER_40;
|
|
case 2:
|
|
return GI_QUIVER_50;
|
|
}
|
|
case RG_ARROWS_5:
|
|
case RG_BUY_ARROWS_10:
|
|
return GI_ARROWS_SMALL;
|
|
case RG_ARROWS_10:
|
|
case RG_BUY_ARROWS_30:
|
|
return GI_ARROWS_MEDIUM;
|
|
case RG_ARROWS_30:
|
|
case RG_BUY_ARROWS_50:
|
|
return GI_ARROWS_LARGE;
|
|
case RG_PROGRESSIVE_SLINGSHOT:
|
|
switch (CUR_UPG_VALUE(UPG_BULLET_BAG)) {
|
|
case 0:
|
|
return GI_SLINGSHOT;
|
|
case 1:
|
|
return GI_BULLET_BAG_40;
|
|
case 2:
|
|
return GI_BULLET_BAG_50;
|
|
}
|
|
case RG_DEKU_SEEDS_30:
|
|
case RG_BUY_DEKU_SEEDS_30:
|
|
return GI_SEEDS_30;
|
|
case RG_PROGRESSIVE_OCARINA:
|
|
switch (INV_CONTENT(ITEM_OCARINA_FAIRY)) {
|
|
case ITEM_NONE:
|
|
return GI_OCARINA_FAIRY;
|
|
case ITEM_OCARINA_FAIRY:
|
|
return GI_OCARINA_OOT;
|
|
}
|
|
case RG_PROGRESSIVE_BOMBCHUS:
|
|
if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) {
|
|
return GI_BOMBCHUS_20;
|
|
}
|
|
if (AMMO(ITEM_BOMBCHU) < 5) {
|
|
return GI_BOMBCHUS_10;
|
|
}
|
|
return GI_BOMBCHUS_5;
|
|
case RG_BOMBCHU_5:
|
|
case RG_BOMBCHU_DROP:
|
|
return GI_BOMBCHUS_5;
|
|
case RG_BOMBCHU_10:
|
|
case RG_BUY_BOMBCHU_10:
|
|
return GI_BOMBCHUS_10;
|
|
case RG_BOMBCHU_20:
|
|
case RG_BUY_BOMBCHU_20:
|
|
return GI_BOMBCHUS_20;
|
|
case RG_PROGRESSIVE_HOOKSHOT:
|
|
switch (INV_CONTENT(ITEM_HOOKSHOT)) {
|
|
case ITEM_NONE:
|
|
return GI_HOOKSHOT;
|
|
case ITEM_HOOKSHOT:
|
|
return GI_LONGSHOT;
|
|
}
|
|
case RG_BOOMERANG:
|
|
return GI_BOOMERANG;
|
|
case RG_LENS_OF_TRUTH:
|
|
return GI_LENS;
|
|
case RG_MAGIC_BEAN:
|
|
return GI_BEAN;
|
|
case RG_MEGATON_HAMMER:
|
|
return GI_HAMMER;
|
|
case RG_FIRE_ARROWS:
|
|
return GI_ARROW_FIRE;
|
|
case RG_ICE_ARROWS:
|
|
return GI_ARROW_ICE;
|
|
case RG_LIGHT_ARROWS:
|
|
return GI_ARROW_LIGHT;
|
|
case RG_DINS_FIRE:
|
|
return GI_DINS_FIRE;
|
|
case RG_FARORES_WIND:
|
|
return GI_FARORES_WIND;
|
|
case RG_NAYRUS_LOVE:
|
|
return GI_NAYRUS_LOVE;
|
|
|
|
// Bottles
|
|
case RG_EMPTY_BOTTLE:
|
|
return GI_BOTTLE;
|
|
case RG_BOTTLE_WITH_MILK:
|
|
return GI_MILK_BOTTLE;
|
|
case RG_RUTOS_LETTER:
|
|
return GI_LETTER_RUTO;
|
|
|
|
// Bottle Refills
|
|
case RG_MILK:
|
|
return GI_MILK;
|
|
case RG_RED_POTION_REFILL:
|
|
case RG_BUY_RED_POTION_30:
|
|
case RG_BUY_RED_POTION_40:
|
|
case RG_BUY_RED_POTION_50:
|
|
return GI_POTION_RED;
|
|
case RG_GREEN_POTION_REFILL:
|
|
case RG_BUY_GREEN_POTION:
|
|
return GI_POTION_GREEN;
|
|
case RG_BLUE_POTION_REFILL:
|
|
case RG_BUY_BLUE_POTION:
|
|
return GI_POTION_BLUE;
|
|
case RG_BUY_FISH:
|
|
return GI_FISH;
|
|
case RG_BUY_BLUE_FIRE:
|
|
return GI_BLUE_FIRE;
|
|
case RG_BUY_BOTTLE_BUG:
|
|
return GI_BUGS;
|
|
case RG_BUY_POE:
|
|
return GI_POE;
|
|
case RG_BUY_FAIRYS_SPIRIT:
|
|
return GI_FAIRY;
|
|
|
|
// Trade Items
|
|
case RG_WEIRD_EGG:
|
|
return GI_WEIRD_EGG;
|
|
case RG_ZELDAS_LETTER:
|
|
return GI_LETTER_ZELDA;
|
|
case RG_POCKET_EGG:
|
|
return GI_POCKET_EGG;
|
|
case RG_COJIRO:
|
|
return GI_COJIRO;
|
|
case RG_ODD_MUSHROOM:
|
|
return GI_ODD_MUSHROOM;
|
|
case RG_ODD_POTION:
|
|
return GI_ODD_POTION;
|
|
case RG_POACHERS_SAW:
|
|
return GI_SAW;
|
|
case RG_BROKEN_SWORD:
|
|
return GI_SWORD_BROKEN;
|
|
case RG_PRESCRIPTION:
|
|
return GI_PRESCRIPTION;
|
|
case RG_EYEBALL_FROG:
|
|
return GI_FROG;
|
|
case RG_EYEDROPS:
|
|
return GI_EYEDROPS;
|
|
case RG_CLAIM_CHECK:
|
|
return GI_CLAIM_CHECK;
|
|
|
|
// Misc Items
|
|
case RG_STONE_OF_AGONY:
|
|
return GI_STONE_OF_AGONY;
|
|
case RG_GERUDO_MEMBERSHIP_CARD:
|
|
return GI_GERUDO_CARD;
|
|
case RG_GOLD_SKULLTULA_TOKEN:
|
|
return GI_SKULL_TOKEN;
|
|
case RG_PROGRESSIVE_STRENGTH:
|
|
switch (CUR_UPG_VALUE(UPG_STRENGTH)) {
|
|
case 0:
|
|
return GI_BRACELET;
|
|
case 1:
|
|
return GI_GAUNTLETS_SILVER;
|
|
case 2:
|
|
return GI_GAUNTLETS_GOLD;
|
|
}
|
|
case RG_PROGRESSIVE_WALLET:
|
|
switch (CUR_UPG_VALUE(UPG_WALLET)) {
|
|
case 0:
|
|
return GI_WALLET_ADULT;
|
|
case 1:
|
|
return GI_WALLET_GIANT;
|
|
case 2:
|
|
return (GetItemID)RG_TYCOON_WALLET;
|
|
}
|
|
case RG_PROGRESSIVE_SCALE:
|
|
switch (CUR_UPG_VALUE(UPG_SCALE)) {
|
|
case 0:
|
|
return GI_SCALE_SILVER;
|
|
case 1:
|
|
return GI_SCALE_GOLD;
|
|
}
|
|
case RG_PROGRESSIVE_MAGIC_METER:
|
|
switch (gSaveContext.magicLevel) {
|
|
case 0:
|
|
return (GetItemID)RG_MAGIC_SINGLE;
|
|
case 1:
|
|
return (GetItemID)RG_MAGIC_DOUBLE;
|
|
}
|
|
|
|
case RG_DEKU_TREE_MAP:
|
|
case RG_DODONGOS_CAVERN_MAP:
|
|
case RG_JABU_JABUS_BELLY_MAP:
|
|
case RG_FOREST_TEMPLE_MAP:
|
|
case RG_FIRE_TEMPLE_MAP:
|
|
case RG_WATER_TEMPLE_MAP:
|
|
case RG_SPIRIT_TEMPLE_MAP:
|
|
case RG_SHADOW_TEMPLE_MAP:
|
|
case RG_BOTTOM_OF_THE_WELL_MAP:
|
|
case RG_ICE_CAVERN_MAP:
|
|
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) < 3) {
|
|
return GI_MAP;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
|
|
case RG_DEKU_TREE_COMPASS:
|
|
case RG_DODONGOS_CAVERN_COMPASS:
|
|
case RG_JABU_JABUS_BELLY_COMPASS:
|
|
case RG_FOREST_TEMPLE_COMPASS:
|
|
case RG_FIRE_TEMPLE_COMPASS:
|
|
case RG_WATER_TEMPLE_COMPASS:
|
|
case RG_SPIRIT_TEMPLE_COMPASS:
|
|
case RG_SHADOW_TEMPLE_COMPASS:
|
|
case RG_BOTTOM_OF_THE_WELL_COMPASS:
|
|
case RG_ICE_CAVERN_COMPASS:
|
|
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) < 3) {
|
|
return GI_COMPASS;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
|
|
case RG_FOREST_TEMPLE_BOSS_KEY:
|
|
case RG_FIRE_TEMPLE_BOSS_KEY:
|
|
case RG_WATER_TEMPLE_BOSS_KEY:
|
|
case RG_SPIRIT_TEMPLE_BOSS_KEY:
|
|
case RG_SHADOW_TEMPLE_BOSS_KEY:
|
|
if (GetRandoSettingValue(RSK_BOSS_KEYSANITY) < 3) {
|
|
return GI_KEY_BOSS;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
case RG_GANONS_CASTLE_BOSS_KEY:
|
|
if (GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_VANILLA ||
|
|
GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_OWN_DUNGEON ||
|
|
GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_STARTWITH) {
|
|
return GI_KEY_BOSS;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
|
|
case RG_FOREST_TEMPLE_SMALL_KEY:
|
|
case RG_FIRE_TEMPLE_SMALL_KEY:
|
|
case RG_WATER_TEMPLE_SMALL_KEY:
|
|
case RG_SPIRIT_TEMPLE_SMALL_KEY:
|
|
case RG_SHADOW_TEMPLE_SMALL_KEY:
|
|
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
|
|
case RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY:
|
|
case RG_GANONS_CASTLE_SMALL_KEY:
|
|
if (GetRandoSettingValue(RSK_KEYSANITY) == RO_DUNGEON_ITEM_LOC_STARTWITH ||
|
|
GetRandoSettingValue(RSK_KEYSANITY) == RO_DUNGEON_ITEM_LOC_VANILLA ||
|
|
GetRandoSettingValue(RSK_KEYSANITY) == RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) {
|
|
return GI_KEY_SMALL;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
case RG_GERUDO_FORTRESS_SMALL_KEY:
|
|
if (GetRandoSettingValue(RSK_GERUDO_KEYS) == 0) {
|
|
return GI_KEY_SMALL;
|
|
} else {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
|
|
case RG_RECOVERY_HEART:
|
|
case RG_BUY_HEART:
|
|
return GI_HEART;
|
|
case RG_GREEN_RUPEE:
|
|
return GI_RUPEE_GREEN;
|
|
case RG_BLUE_RUPEE:
|
|
return GI_RUPEE_BLUE;
|
|
case RG_RED_RUPEE:
|
|
return GI_RUPEE_RED;
|
|
case RG_PURPLE_RUPEE:
|
|
return GI_RUPEE_PURPLE;
|
|
case RG_HUGE_RUPEE:
|
|
return GI_RUPEE_GOLD;
|
|
case RG_PIECE_OF_HEART:
|
|
return GI_HEART_PIECE;
|
|
case RG_HEART_CONTAINER:
|
|
return GI_HEART_CONTAINER;
|
|
|
|
case RG_DEKU_NUTS_5:
|
|
case RG_BUY_DEKU_NUT_5:
|
|
return GI_NUTS_5;
|
|
case RG_DEKU_NUTS_10:
|
|
case RG_BUY_DEKU_NUT_10:
|
|
return GI_NUTS_10;
|
|
case RG_DEKU_STICK_1:
|
|
case RG_BUY_DEKU_STICK_1:
|
|
return GI_STICKS_1;
|
|
case RG_TREASURE_GAME_SMALL_KEY:
|
|
return GI_DOOR_KEY;
|
|
case RG_TREASURE_GAME_HEART:
|
|
return GI_HEART_PIECE_WIN;
|
|
case RG_TREASURE_GAME_GREEN_RUPEE:
|
|
return GI_RUPEE_GREEN_LOSE;
|
|
default:
|
|
if (!IsItemVanilla(randoGet)) {
|
|
return (GetItemID)randoGet;
|
|
}
|
|
return ogItemId;
|
|
}
|
|
}
|
|
|
|
bool Randomizer::IsItemVanilla(RandomizerGet randoGet) {
|
|
switch (randoGet) {
|
|
case RG_NONE:
|
|
case RG_KOKIRI_SWORD:
|
|
case RG_GIANTS_KNIFE:
|
|
case RG_BIGGORON_SWORD:
|
|
case RG_DEKU_SHIELD:
|
|
case RG_HYLIAN_SHIELD:
|
|
case RG_MIRROR_SHIELD:
|
|
case RG_GORON_TUNIC:
|
|
case RG_ZORA_TUNIC:
|
|
case RG_IRON_BOOTS:
|
|
case RG_HOVER_BOOTS:
|
|
case RG_BOOMERANG:
|
|
case RG_LENS_OF_TRUTH:
|
|
case RG_MEGATON_HAMMER:
|
|
case RG_STONE_OF_AGONY:
|
|
case RG_DINS_FIRE:
|
|
case RG_FARORES_WIND:
|
|
case RG_NAYRUS_LOVE:
|
|
case RG_FIRE_ARROWS:
|
|
case RG_ICE_ARROWS:
|
|
case RG_LIGHT_ARROWS:
|
|
case RG_GERUDO_MEMBERSHIP_CARD:
|
|
case RG_MAGIC_BEAN:
|
|
case RG_WEIRD_EGG:
|
|
case RG_ZELDAS_LETTER:
|
|
case RG_RUTOS_LETTER:
|
|
case RG_POCKET_EGG:
|
|
case RG_COJIRO:
|
|
case RG_ODD_MUSHROOM:
|
|
case RG_ODD_POTION:
|
|
case RG_POACHERS_SAW:
|
|
case RG_BROKEN_SWORD:
|
|
case RG_PRESCRIPTION:
|
|
case RG_EYEBALL_FROG:
|
|
case RG_EYEDROPS:
|
|
case RG_CLAIM_CHECK:
|
|
case RG_GOLD_SKULLTULA_TOKEN:
|
|
case RG_PROGRESSIVE_HOOKSHOT:
|
|
case RG_PROGRESSIVE_STRENGTH:
|
|
case RG_PROGRESSIVE_BOMB_BAG:
|
|
case RG_PROGRESSIVE_BOW:
|
|
case RG_PROGRESSIVE_SLINGSHOT:
|
|
case RG_PROGRESSIVE_SCALE:
|
|
case RG_PROGRESSIVE_NUT_UPGRADE:
|
|
case RG_PROGRESSIVE_STICK_UPGRADE:
|
|
case RG_PROGRESSIVE_BOMBCHUS:
|
|
case RG_PROGRESSIVE_OCARINA:
|
|
case RG_PROGRESSIVE_GORONSWORD:
|
|
case RG_EMPTY_BOTTLE:
|
|
case RG_BOTTLE_WITH_MILK:
|
|
case RG_RECOVERY_HEART:
|
|
case RG_GREEN_RUPEE:
|
|
case RG_BLUE_RUPEE:
|
|
case RG_RED_RUPEE:
|
|
case RG_PURPLE_RUPEE:
|
|
case RG_HUGE_RUPEE:
|
|
case RG_PIECE_OF_HEART:
|
|
case RG_HEART_CONTAINER:
|
|
case RG_MILK:
|
|
case RG_BOMBS_5:
|
|
case RG_BOMBS_10:
|
|
case RG_BOMBS_20:
|
|
case RG_BOMBCHU_5:
|
|
case RG_BOMBCHU_10:
|
|
case RG_BOMBCHU_20:
|
|
case RG_BOMBCHU_DROP:
|
|
case RG_ARROWS_5:
|
|
case RG_ARROWS_10:
|
|
case RG_ARROWS_30:
|
|
case RG_DEKU_NUTS_5:
|
|
case RG_DEKU_NUTS_10:
|
|
case RG_DEKU_SEEDS_30:
|
|
case RG_DEKU_STICK_1:
|
|
case RG_RED_POTION_REFILL:
|
|
case RG_GREEN_POTION_REFILL:
|
|
case RG_BLUE_POTION_REFILL:
|
|
case RG_TREASURE_GAME_HEART:
|
|
case RG_TREASURE_GAME_GREEN_RUPEE:
|
|
case RG_BUY_DEKU_NUT_5:
|
|
case RG_BUY_ARROWS_30:
|
|
case RG_BUY_ARROWS_50:
|
|
case RG_BUY_BOMBS_525:
|
|
case RG_BUY_DEKU_NUT_10:
|
|
case RG_BUY_DEKU_STICK_1:
|
|
case RG_BUY_BOMBS_10:
|
|
case RG_BUY_FISH:
|
|
case RG_BUY_RED_POTION_30:
|
|
case RG_BUY_GREEN_POTION:
|
|
case RG_BUY_BLUE_POTION:
|
|
case RG_BUY_HYLIAN_SHIELD:
|
|
case RG_BUY_DEKU_SHIELD:
|
|
case RG_BUY_GORON_TUNIC:
|
|
case RG_BUY_ZORA_TUNIC:
|
|
case RG_BUY_HEART:
|
|
case RG_BUY_BOMBCHU_10:
|
|
case RG_BUY_BOMBCHU_20:
|
|
case RG_BUY_DEKU_SEEDS_30:
|
|
case RG_SOLD_OUT:
|
|
case RG_BUY_BLUE_FIRE:
|
|
case RG_BUY_BOTTLE_BUG:
|
|
case RG_BUY_POE:
|
|
case RG_BUY_FAIRYS_SPIRIT:
|
|
case RG_BUY_ARROWS_10:
|
|
case RG_BUY_BOMBS_20:
|
|
case RG_BUY_BOMBS_30:
|
|
case RG_BUY_BOMBS_535:
|
|
case RG_BUY_RED_POTION_40:
|
|
case RG_BUY_RED_POTION_50:
|
|
return true;
|
|
case RG_PROGRESSIVE_WALLET:
|
|
if (CUR_UPG_VALUE(UPG_WALLET) < 2) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
case RG_FOREST_TEMPLE_SMALL_KEY:
|
|
case RG_FIRE_TEMPLE_SMALL_KEY:
|
|
case RG_WATER_TEMPLE_SMALL_KEY:
|
|
case RG_SPIRIT_TEMPLE_SMALL_KEY:
|
|
case RG_SHADOW_TEMPLE_SMALL_KEY:
|
|
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
|
|
case RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY:
|
|
case RG_GANONS_CASTLE_SMALL_KEY:
|
|
if (GetRandoSettingValue(RSK_KEYSANITY) > 2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
case RG_GERUDO_FORTRESS_SMALL_KEY:
|
|
if (GetRandoSettingValue(RSK_GERUDO_KEYS) != 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
case RG_FOREST_TEMPLE_BOSS_KEY:
|
|
case RG_FIRE_TEMPLE_BOSS_KEY:
|
|
case RG_WATER_TEMPLE_BOSS_KEY:
|
|
case RG_SPIRIT_TEMPLE_BOSS_KEY:
|
|
case RG_SHADOW_TEMPLE_BOSS_KEY:
|
|
if (GetRandoSettingValue(RSK_BOSS_KEYSANITY) > 2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
case RG_GANONS_CASTLE_BOSS_KEY:
|
|
if (GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_VANILLA ||
|
|
GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_OWN_DUNGEON ||
|
|
GetRandoSettingValue(RSK_GANONS_BOSS_KEY) == RO_GANON_BOSS_KEY_STARTWITH) {
|
|
return true;
|
|
}
|
|
return false;
|
|
case RG_DEKU_TREE_COMPASS:
|
|
case RG_DODONGOS_CAVERN_COMPASS:
|
|
case RG_JABU_JABUS_BELLY_COMPASS:
|
|
case RG_FOREST_TEMPLE_COMPASS:
|
|
case RG_FIRE_TEMPLE_COMPASS:
|
|
case RG_WATER_TEMPLE_COMPASS:
|
|
case RG_SPIRIT_TEMPLE_COMPASS:
|
|
case RG_SHADOW_TEMPLE_COMPASS:
|
|
case RG_BOTTOM_OF_THE_WELL_COMPASS:
|
|
case RG_ICE_CAVERN_COMPASS:
|
|
case RG_DEKU_TREE_MAP:
|
|
case RG_DODONGOS_CAVERN_MAP:
|
|
case RG_JABU_JABUS_BELLY_MAP:
|
|
case RG_FOREST_TEMPLE_MAP:
|
|
case RG_FIRE_TEMPLE_MAP:
|
|
case RG_WATER_TEMPLE_MAP:
|
|
case RG_SPIRIT_TEMPLE_MAP:
|
|
case RG_SHADOW_TEMPLE_MAP:
|
|
case RG_BOTTOM_OF_THE_WELL_MAP:
|
|
case RG_ICE_CAVERN_MAP:
|
|
if (GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES) > 2) {
|
|
return false;
|
|
}
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Randomizer::CheckContainsVanillaItem(RandomizerCheck randoCheck) {
|
|
RandomizerGet randoGet = this->itemLocations[randoCheck].rgID;
|
|
return IsItemVanilla(randoGet);
|
|
}
|
|
|
|
std::string Randomizer::GetAdultAltarText() const {
|
|
return this->adultAltarText;
|
|
}
|
|
|
|
std::string Randomizer::GetChildAltarText() const {
|
|
return this->childAltarText;
|
|
}
|
|
|
|
std::string Randomizer::GetGanonText() const {
|
|
return ganonText;
|
|
}
|
|
|
|
std::string Randomizer::GetGanonHintText() const {
|
|
return ganonHintText;
|
|
}
|
|
|
|
// There has been some talk about potentially just using the RC identifier to store flags rather than randomizer inf, so
|
|
// for now we're not going to store randomzierInf in the randomizer check objects, we're just going to map them 1:1 here
|
|
std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
|
|
{ RC_KF_LINKS_HOUSE_COW, RAND_INF_COWS_MILKED_KF_LINKS_HOUSE_COW },
|
|
{ RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT },
|
|
{ RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT },
|
|
{ RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE },
|
|
{ RC_LW_DEKU_SCRUB_GROTTO_REAR, RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR },
|
|
{ RC_LW_DEKU_SCRUB_GROTTO_FRONT, RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT },
|
|
{ RC_SFM_DEKU_SCRUB_GROTTO_REAR, RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR },
|
|
{ RC_SFM_DEKU_SCRUB_GROTTO_FRONT, RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT },
|
|
{ RC_HF_DEKU_SCRUB_GROTTO, RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO },
|
|
{ RC_HF_COW_GROTTO_COW, RAND_INF_COWS_MILKED_HF_COW_GROTTO_COW },
|
|
{ RC_LH_DEKU_SCRUB_GROTTO_LEFT, RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT },
|
|
{ RC_LH_DEKU_SCRUB_GROTTO_RIGHT, RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT },
|
|
{ RC_LH_DEKU_SCRUB_GROTTO_CENTER, RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER },
|
|
{ RC_GV_DEKU_SCRUB_GROTTO_REAR, RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR },
|
|
{ RC_GV_DEKU_SCRUB_GROTTO_FRONT, RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT },
|
|
{ RC_GV_COW, RAND_INF_COWS_MILKED_GV_COW },
|
|
{ RC_COLOSSUS_DEKU_SCRUB_GROTTO_REAR, RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR },
|
|
{ RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT },
|
|
{ RC_KAK_IMPAS_HOUSE_COW, RAND_INF_COWS_MILKED_KAK_IMPAS_HOUSE_COW },
|
|
{ RC_DMT_COW_GROTTO_COW, RAND_INF_COWS_MILKED_DMT_COW_GROTTO_COW },
|
|
{ RC_GC_DEKU_SCRUB_GROTTO_LEFT, RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT },
|
|
{ RC_GC_DEKU_SCRUB_GROTTO_RIGHT, RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT },
|
|
{ RC_GC_DEKU_SCRUB_GROTTO_CENTER, RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER },
|
|
{ RC_DMC_DEKU_SCRUB, RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB },
|
|
{ RC_DMC_DEKU_SCRUB_GROTTO_LEFT, RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT },
|
|
{ RC_DMC_DEKU_SCRUB_GROTTO_RIGHT, RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT },
|
|
{ RC_DMC_DEKU_SCRUB_GROTTO_CENTER, RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER },
|
|
{ RC_ZR_DEKU_SCRUB_GROTTO_REAR, RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR },
|
|
{ RC_ZR_DEKU_SCRUB_GROTTO_FRONT, RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT },
|
|
{ RC_LLR_DEKU_SCRUB_GROTTO_LEFT, RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT },
|
|
{ RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT },
|
|
{ RC_LLR_DEKU_SCRUB_GROTTO_CENTER, RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER },
|
|
{ RC_LLR_STABLES_LEFT_COW, RAND_INF_COWS_MILKED_LLR_STABLES_LEFT_COW },
|
|
{ RC_LLR_STABLES_RIGHT_COW, RAND_INF_COWS_MILKED_LLR_STABLES_RIGHT_COW },
|
|
{ RC_LLR_TOWER_LEFT_COW, RAND_INF_COWS_MILKED_LLR_TOWER_LEFT_COW },
|
|
{ RC_LLR_TOWER_RIGHT_COW, RAND_INF_COWS_MILKED_LLR_TOWER_RIGHT_COW },
|
|
{ RC_DEKU_TREE_MQ_DEKU_SCRUB, RAND_INF_SCRUBS_PURCHASED_DEKU_TREE_MQ_DEKU_SCRUB },
|
|
{ RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT },
|
|
{ RC_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS },
|
|
{ RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT },
|
|
{ RC_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY },
|
|
{ RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_REAR, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_REAR },
|
|
{ RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT },
|
|
{ RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_STAIRCASE, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_MQ_DEKU_SCRUB_STAIRCASE },
|
|
{ RC_DODONGOS_CAVERN_MQ_DEKU_SCRUB_SIDE_ROOM_NEAR_LOWER_LIZALFOS, RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_MQ_DEKU_SCRUB_SIDE_ROOM_NEAR_LOWER_LIZALFOS },
|
|
{ RC_JABU_JABUS_BELLY_DEKU_SCRUB, RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB },
|
|
{ RC_JABU_JABUS_BELLY_MQ_COW, RAND_INF_COWS_MILKED_JABU_JABUS_BELLY_MQ_COW },
|
|
{ RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT },
|
|
{ RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT },
|
|
{ RC_GANONS_CASTLE_DEKU_SCRUB_RIGHT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT },
|
|
{ RC_GANONS_CASTLE_DEKU_SCRUB_LEFT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT },
|
|
{ RC_GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT },
|
|
{ RC_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_LEFT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_LEFT },
|
|
{ RC_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER },
|
|
{ RC_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_RIGHT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_RIGHT },
|
|
{ RC_GANONS_CASTLE_MQ_DEKU_SCRUB_LEFT, RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_MQ_DEKU_SCRUB_LEFT },
|
|
{ RC_KF_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1 },
|
|
{ RC_KF_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_2 },
|
|
{ RC_KF_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_3 },
|
|
{ RC_KF_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_4 },
|
|
{ RC_KF_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_5 },
|
|
{ RC_KF_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_6 },
|
|
{ RC_KF_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_7 },
|
|
{ RC_KF_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_8 },
|
|
{ RC_GC_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_1 },
|
|
{ RC_GC_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_2 },
|
|
{ RC_GC_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_3 },
|
|
{ RC_GC_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_4 },
|
|
{ RC_GC_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_5 },
|
|
{ RC_GC_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_6 },
|
|
{ RC_GC_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_7 },
|
|
{ RC_GC_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_GC_SHOP_ITEM_8 },
|
|
{ RC_ZD_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_1 },
|
|
{ RC_ZD_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_2 },
|
|
{ RC_ZD_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_3 },
|
|
{ RC_ZD_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_4 },
|
|
{ RC_ZD_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_5 },
|
|
{ RC_ZD_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_6 },
|
|
{ RC_ZD_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_7 },
|
|
{ RC_ZD_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_ZD_SHOP_ITEM_8 },
|
|
{ RC_KAK_BAZAAR_ITEM_1, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_1 },
|
|
{ RC_KAK_BAZAAR_ITEM_2, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_2 },
|
|
{ RC_KAK_BAZAAR_ITEM_3, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_3 },
|
|
{ RC_KAK_BAZAAR_ITEM_4, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_4 },
|
|
{ RC_KAK_BAZAAR_ITEM_5, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_5 },
|
|
{ RC_KAK_BAZAAR_ITEM_6, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_6 },
|
|
{ RC_KAK_BAZAAR_ITEM_7, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_7 },
|
|
{ RC_KAK_BAZAAR_ITEM_8, RAND_INF_SHOP_ITEMS_KAK_BAZAAR_ITEM_8 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_1 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_2 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_3 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_4 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_5 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_6 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_7 },
|
|
{ RC_KAK_POTION_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_KAK_POTION_SHOP_ITEM_8 },
|
|
{ RC_MARKET_BAZAAR_ITEM_1, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_1 },
|
|
{ RC_MARKET_BAZAAR_ITEM_2, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_2 },
|
|
{ RC_MARKET_BAZAAR_ITEM_3, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_3 },
|
|
{ RC_MARKET_BAZAAR_ITEM_4, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_4 },
|
|
{ RC_MARKET_BAZAAR_ITEM_5, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_5 },
|
|
{ RC_MARKET_BAZAAR_ITEM_6, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_6 },
|
|
{ RC_MARKET_BAZAAR_ITEM_7, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_7 },
|
|
{ RC_MARKET_BAZAAR_ITEM_8, RAND_INF_SHOP_ITEMS_MARKET_BAZAAR_ITEM_8 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_1 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_2 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_3 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_4 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_5 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_6 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_7 },
|
|
{ RC_MARKET_POTION_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_MARKET_POTION_SHOP_ITEM_8 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_1, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_1 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_2, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_2 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_3, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_3 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_4, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_4 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_5, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_5 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_6, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_6 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_7, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_7 },
|
|
{ RC_MARKET_BOMBCHU_SHOP_ITEM_8, RAND_INF_SHOP_ITEMS_MARKET_BOMBCHU_SHOP_ITEM_8 },
|
|
{ RC_GC_MEDIGORON, RAND_INF_MERCHANTS_MEDIGORON },
|
|
{ RC_WASTELAND_BOMBCHU_SALESMAN, RAND_INF_MERCHANTS_CARPET_SALESMAN },
|
|
|
|
};
|
|
|
|
RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) {
|
|
RandomizerCheck specialRc = RC_UNKNOWN_CHECK;
|
|
// TODO: Migrate these special cases into table, or at least document why they are special
|
|
switch(sceneNum) {
|
|
case SCENE_TAKARAYA:
|
|
if(actorParams == 20170) specialRc = RC_MARKET_TREASURE_CHEST_GAME_REWARD;
|
|
|
|
// RANDOTODO update logic to match 3ds rando when we implement keysanity
|
|
// keep keys og
|
|
if ((actorParams & 0x60) == 0x20) break;
|
|
|
|
if (GetRandoSettingValue(RSK_SHUFFLE_CHEST_MINIGAME)) {
|
|
if((actorParams & 0xF) < 2) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_1;
|
|
if((actorParams & 0xF) < 4) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_2;
|
|
if((actorParams & 0xF) < 6) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_3;
|
|
if((actorParams & 0xF) < 8) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_4;
|
|
if((actorParams & 0xF) < 10) specialRc = RC_MARKET_TREASURE_CHEST_GAME_ITEM_5;
|
|
}
|
|
break;
|
|
case SCENE_SHRINE:
|
|
case SCENE_SHRINE_N:
|
|
case SCENE_SHRINE_R:
|
|
switch (actorParams) {
|
|
case 14342:
|
|
specialRc = RC_TOT_LEFT_GOSSIP_STONE;
|
|
break;
|
|
case 14599:
|
|
specialRc = RC_TOT_LEFT_CENTER_GOSSIP_STONE;
|
|
break;
|
|
case 14862:
|
|
specialRc = RC_TOT_RIGHT_CENTER_GOSSIP_STONE;
|
|
break;
|
|
case 15120:
|
|
specialRc = RC_TOT_RIGHT_GOSSIP_STONE;
|
|
break;
|
|
}
|
|
break;
|
|
case SCENE_SPOT01:
|
|
switch (actorId) {
|
|
case ACTOR_EN_NIW_LADY:
|
|
if (LINK_IS_ADULT) {
|
|
specialRc = RC_KAK_ANJU_AS_ADULT;
|
|
} else {
|
|
specialRc = RC_KAK_ANJU_AS_CHILD;
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_SPOT06:
|
|
switch (actorId) {
|
|
case ACTOR_ITEM_ETCETERA:
|
|
if (LINK_IS_ADULT) {
|
|
specialRc = RC_LH_SUN;
|
|
} else {
|
|
specialRc = RC_LH_UNDERWATER_ITEM;
|
|
}
|
|
}
|
|
break;
|
|
case SCENE_SPOT08:
|
|
switch (actorParams) {
|
|
case 15362:
|
|
case 14594:
|
|
specialRc = RC_JABU_GOSSIP_STONE;
|
|
break;
|
|
case 14849:
|
|
case 14337:
|
|
specialRc = RC_FAIRY_GOSSIP_STONE;
|
|
break;
|
|
}
|
|
break;
|
|
case SCENE_DDAN:
|
|
// special case for MQ DC Gossip Stone
|
|
if (actorId == ACTOR_EN_GS && actorParams == 15892 && ResourceMgr_IsGameMasterQuest()) {
|
|
specialRc = RC_DODONGOS_CAVERN_GOSSIP_STONE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (specialRc != RC_UNKNOWN_CHECK) {
|
|
return RandomizerCheckObjects::GetAllRCObjects()[specialRc];
|
|
}
|
|
|
|
auto range = checkFromActorMultimap.equal_range(std::make_tuple(actorId, sceneNum, actorParams));
|
|
|
|
for (auto it = range.first; it != range.second; ++it) {
|
|
if (
|
|
it->second.vOrMQ == RCVORMQ_BOTH ||
|
|
(it->second.vOrMQ == RCVORMQ_VANILLA && !ResourceMgr_IsGameMasterQuest()) ||
|
|
(it->second.vOrMQ == RCVORMQ_MQ && ResourceMgr_IsGameMasterQuest())
|
|
) {
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
return RandomizerCheckObjects::GetAllRCObjects()[RC_UNKNOWN_CHECK];
|
|
}
|
|
|
|
ScrubIdentity Randomizer::IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData) {
|
|
struct ScrubIdentity scrubIdentity;
|
|
|
|
scrubIdentity.randomizerInf = RAND_INF_MAX;
|
|
scrubIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
|
|
scrubIdentity.getItemId = GI_NONE;
|
|
scrubIdentity.itemPrice = -1;
|
|
scrubIdentity.isShuffled = false;
|
|
|
|
// Scrubs that are 0x06 are loaded as 0x03 when child, switching from selling arrows to seeds
|
|
if (actorParams == 0x06) actorParams = 0x03;
|
|
|
|
if (sceneNum == SCENE_KAKUSIANA) {
|
|
actorParams = TWO_ACTOR_PARAMS(actorParams, respawnData);
|
|
}
|
|
|
|
RandomizerCheckObject rcObject = GetCheckObjectFromActor(ACTOR_EN_DNS, sceneNum, actorParams);
|
|
|
|
if (rcObject.rc != RC_UNKNOWN_CHECK) {
|
|
scrubIdentity.randomizerInf = rcToRandomizerInf[rcObject.rc];
|
|
scrubIdentity.randomizerCheck = rcObject.rc;
|
|
scrubIdentity.getItemId = rcObject.ogItemId;
|
|
scrubIdentity.isShuffled = GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) > 0;
|
|
|
|
if (rcObject.rc == RC_HF_DEKU_SCRUB_GROTTO || rcObject.rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT || rcObject.rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE) {
|
|
scrubIdentity.isShuffled = true;
|
|
}
|
|
|
|
if (merchantPrices.find(scrubIdentity.randomizerCheck) != merchantPrices.end()) {
|
|
scrubIdentity.itemPrice = merchantPrices[scrubIdentity.randomizerCheck];
|
|
}
|
|
}
|
|
|
|
return scrubIdentity;
|
|
}
|
|
|
|
ShopItemIdentity Randomizer::IdentifyShopItem(s32 sceneNum, u8 slotIndex) {
|
|
ShopItemIdentity shopItemIdentity;
|
|
|
|
shopItemIdentity.randomizerInf = RAND_INF_MAX;
|
|
shopItemIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
|
|
shopItemIdentity.ogItemId = GI_NONE;
|
|
shopItemIdentity.itemPrice = -1;
|
|
shopItemIdentity.enGirlAShopItem = 0x32;
|
|
|
|
RandomizerCheckObject rcObject = GetCheckObjectFromActor(ACTOR_EN_GIRLA,
|
|
// Bazaar (SHOP1) scene is reused, so if entering from Kak use debug scene to identify
|
|
(sceneNum == SCENE_SHOP1 && gSaveContext.entranceIndex == 0xB7) ? SCENE_TEST01 : sceneNum, slotIndex);
|
|
|
|
if (rcObject.rc != RC_UNKNOWN_CHECK) {
|
|
shopItemIdentity.randomizerInf = rcToRandomizerInf[rcObject.rc];
|
|
shopItemIdentity.randomizerCheck = rcObject.rc;
|
|
shopItemIdentity.ogItemId = rcObject.ogItemId;
|
|
|
|
RandomizerGetData randoGet = GetRandomizerGetDataFromKnownCheck(shopItemIdentity.randomizerCheck);
|
|
if (randomizerGetToEnGirlShopItem.find(randoGet.rgID) != randomizerGetToEnGirlShopItem.end()) {
|
|
shopItemIdentity.enGirlAShopItem = randomizerGetToEnGirlShopItem[randoGet.rgID];
|
|
}
|
|
|
|
if (merchantPrices.find(shopItemIdentity.randomizerCheck) != merchantPrices.end()) {
|
|
shopItemIdentity.itemPrice = merchantPrices[shopItemIdentity.randomizerCheck];
|
|
}
|
|
}
|
|
|
|
return shopItemIdentity;
|
|
}
|
|
|
|
CowIdentity Randomizer::IdentifyCow(s32 sceneNum, s32 posX, s32 posZ) {
|
|
struct CowIdentity cowIdentity;
|
|
|
|
cowIdentity.randomizerInf = RAND_INF_MAX;
|
|
cowIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
|
|
|
|
s32 actorParams = 0x00;
|
|
// Only need to pass params if in a scene with two cows
|
|
if (sceneNum == SCENE_KAKUSIANA || sceneNum == SCENE_MALON_STABLE || sceneNum == SCENE_SOUKO) {
|
|
actorParams = TWO_ACTOR_PARAMS(posX, posZ);
|
|
}
|
|
|
|
RandomizerCheckObject rcObject = GetCheckObjectFromActor(ACTOR_EN_COW, sceneNum, actorParams);
|
|
|
|
if (rcObject.rc != RC_UNKNOWN_CHECK) {
|
|
cowIdentity.randomizerInf = rcToRandomizerInf[rcObject.rc];
|
|
cowIdentity.randomizerCheck = rcObject.rc;
|
|
}
|
|
|
|
return cowIdentity;
|
|
}
|
|
|
|
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
|
|
return this->randoSettings[randoSettingKey];
|
|
}
|
|
|
|
GetItemEntry Randomizer::GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability) {
|
|
// Go ahead and early return the ogItemId's entry if we somehow get RG_NONE.
|
|
if (rgData.rgID == RG_NONE) {
|
|
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, ogItemId);
|
|
}
|
|
if (checkObtainability && OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerGet(rgData.rgID) != CAN_OBTAIN) {
|
|
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GI_RUPEE_BLUE);
|
|
}
|
|
// Can't get RG_ICE_TRAP if the rgID corresponds to a vanilla item
|
|
if (IsItemVanilla(rgData.rgID)) {
|
|
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GetItemIdFromRandomizerGet(rgData.rgID, ogItemId));
|
|
}
|
|
// After this point we can assume we are dealing with a randomizer exclusive item.
|
|
GetItemEntry giEntry = ItemTableManager::Instance->RetrieveItemEntry(
|
|
MOD_RANDOMIZER, GetItemIdFromRandomizerGet(rgData.rgID, ogItemId));
|
|
// If we have an ice trap, we want to change the GID and DrawFunc to the fakeRgID's values.
|
|
if (rgData.rgID == RG_ICE_TRAP) {
|
|
ModIndex modIndex;
|
|
if (IsItemVanilla(rgData.fakeRgID)) {
|
|
modIndex = MOD_NONE;
|
|
} else {
|
|
modIndex = MOD_RANDOMIZER;
|
|
}
|
|
GetItemEntry fakeGiEntry = ItemTableManager::Instance->RetrieveItemEntry(modIndex, GetItemIdFromRandomizerGet(rgData.fakeRgID, ogItemId));
|
|
giEntry.gid = fakeGiEntry.gid;
|
|
giEntry.gi = fakeGiEntry.gi;
|
|
giEntry.drawFunc = fakeGiEntry.drawFunc;
|
|
}
|
|
return giEntry;
|
|
}
|
|
|
|
GetItemEntry Randomizer::GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability) {
|
|
RandomizerGetData rgData = this->itemLocations[randomizerCheck];
|
|
return GetItemEntryFromRGData(rgData, ogItemId, checkObtainability);
|
|
}
|
|
|
|
RandomizerCheck Randomizer::GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {
|
|
return GetCheckObjectFromActor(actorId, sceneNum, actorParams).rc;
|
|
}
|
|
|
|
RandomizerCheck Randomizer::GetCheckFromRandomizerInf(RandomizerInf randomizerInf) {
|
|
for (auto const& [key, value] : rcToRandomizerInf) {
|
|
if (value == randomizerInf) return key;
|
|
}
|
|
|
|
return RC_UNKNOWN_CHECK;
|
|
}
|
|
|
|
std::thread randoThread;
|
|
|
|
void GenerateRandomizerImgui() {
|
|
CVar_SetS32("gRandoGenerating", 1);
|
|
CVar_Save();
|
|
|
|
std::unordered_map<RandomizerSettingKey, u8> cvarSettings;
|
|
cvarSettings[RSK_LOGIC_RULES] = CVar_GetS32("gRandomizeLogicRules", 0);
|
|
cvarSettings[RSK_FOREST] = CVar_GetS32("gRandomizeForest", RO_FOREST_CLOSED);
|
|
cvarSettings[RSK_KAK_GATE] = CVar_GetS32("gRandomizeKakarikoGate", RO_KAK_GATE_CLOSED);
|
|
cvarSettings[RSK_DOOR_OF_TIME] = CVar_GetS32("gRandomizeDoorOfTime", RO_DOOROFTIME_CLOSED);
|
|
cvarSettings[RSK_ZORAS_FOUNTAIN] = CVar_GetS32("gRandomizeZorasFountain", 0);
|
|
cvarSettings[RSK_STARTING_AGE] = CVar_GetS32("gRandomizeStartingAge", 0);
|
|
cvarSettings[RSK_GERUDO_FORTRESS] = CVar_GetS32("gRandomizeGerudoFortress", RO_GF_NORMAL);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE] = CVar_GetS32("gRandomizeRainbowBridge", 0);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE_STONE_COUNT] = CVar_GetS32("gRandomizeStoneCount", 3);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE_MEDALLION_COUNT] = CVar_GetS32("gRandomizeMedallionCount", 6);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE_REWARD_COUNT] = CVar_GetS32("gRandomizeRewardCount", 9);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT] = CVar_GetS32("gRandomizeDungeonCount", 8);
|
|
cvarSettings[RSK_RAINBOW_BRIDGE_TOKEN_COUNT] = CVar_GetS32("gRandomizeTokenCount", 100);
|
|
cvarSettings[RSK_GANONS_TRIALS] = CVar_GetS32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SET_NUMBER);
|
|
cvarSettings[RSK_TRIAL_COUNT] = CVar_GetS32("gRandomizeGanonTrialCount", 6);
|
|
cvarSettings[RSK_STARTING_OCARINA] = CVar_GetS32("gRandomizeStartingOcarina", 0);
|
|
cvarSettings[RSK_SHUFFLE_OCARINA] = CVar_GetS32("gRandomizeShuffleOcarinas", 0) ||
|
|
CVar_GetS32("gRandomizeStartingOcarina", 0);
|
|
cvarSettings[RSK_STARTING_KOKIRI_SWORD] = CVar_GetS32("gRandomizeStartingKokiriSword", 0);
|
|
cvarSettings[RSK_SHUFFLE_KOKIRI_SWORD] = CVar_GetS32("gRandomizeShuffleKokiriSword", 0) ||
|
|
CVar_GetS32("gRandomizeStartingKokiriSword", 0);
|
|
cvarSettings[RSK_STARTING_DEKU_SHIELD] = CVar_GetS32("gRandomizeStartingDekuShield", 0);
|
|
cvarSettings[RSK_STARTING_SKULLTULA_TOKEN] = CVar_GetS32("gRandomizeStartingSkulltulaToken", 0);
|
|
cvarSettings[RSK_STARTING_MAPS_COMPASSES] = CVar_GetS32("gRandomizeStartingMapsCompasses", 2);
|
|
cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS] = CVar_GetS32("gRandomizeShuffleDungeonReward", RO_DUNGEON_REWARDS_END_OF_DUNGEON);
|
|
cvarSettings[RSK_SHUFFLE_SONGS] = CVar_GetS32("gRandomizeShuffleSongs", 0);
|
|
cvarSettings[RSK_SHUFFLE_TOKENS] = CVar_GetS32("gRandomizeShuffleTokens", 0);
|
|
cvarSettings[RSK_SHOPSANITY] = CVar_GetS32("gRandomizeShopsanity", 0);
|
|
cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0);
|
|
cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0);
|
|
cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0);
|
|
cvarSettings[RSK_SHUFFLE_MAGIC_BEANS] = CVar_GetS32("gRandomizeShuffleBeans", 0);
|
|
cvarSettings[RSK_SHUFFLE_MERCHANTS] = CVar_GetS32("gRandomizeShuffleMerchants", 0);
|
|
cvarSettings[RSK_ENABLE_BOMBCHU_DROPS] = CVar_GetS32("gRandomizeEnableBombchuDrops", 0);
|
|
cvarSettings[RSK_BOMBCHUS_IN_LOGIC] = CVar_GetS32("gRandomizeBombchusInLogic", 0);
|
|
cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0);
|
|
|
|
// if we skip child zelda, we start with zelda's letter, and malon starts
|
|
// at the ranch, so we should *not* shuffle the weird egg
|
|
cvarSettings[RSK_SHUFFLE_WEIRD_EGG] = ((CVar_GetS32("gRandomizeSkipChildZelda", 0) == 0) &&
|
|
CVar_GetS32("gRandomizeShuffleWeirdEgg", 0));
|
|
|
|
cvarSettings[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] = CVar_GetS32("gRandomizeShuffleGerudoToken", 0);
|
|
cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES] = CVar_GetS32("gRandomizeShuffleFrogSongRupees", 0);
|
|
cvarSettings[RSK_ITEM_POOL] = CVar_GetS32("gRandomizeItemPool", RO_ITEM_POOL_BALANCED);
|
|
cvarSettings[RSK_ICE_TRAPS] = CVar_GetS32("gRandomizeIceTraps", RO_ICE_TRAPS_NORMAL);
|
|
cvarSettings[RSK_GOSSIP_STONE_HINTS] = CVar_GetS32("gRandomizeGossipStoneHints", 1);
|
|
cvarSettings[RSK_HINT_CLARITY] = CVar_GetS32("gRandomizeHintClarity", 2);
|
|
cvarSettings[RSK_HINT_DISTRIBUTION] = CVar_GetS32("gRandomizeHintDistribution", 1);
|
|
cvarSettings[RSK_BLUE_FIRE_ARROWS] = CVar_GetS32("gRandomizeBlueFireArrows", 0);
|
|
cvarSettings[RSK_SUNLIGHT_ARROWS] = CVar_GetS32("gRandomizeSunlightArrows", 0);
|
|
cvarSettings[RSK_KEYSANITY] = CVar_GetS32("gRandomizeKeysanity", RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
|
|
cvarSettings[RSK_GERUDO_KEYS] = CVar_GetS32("gRandomizeGerudoKeys", 0);
|
|
cvarSettings[RSK_KEYRINGS] = CVar_GetS32("gRandomizeShuffleKeyRings", 0);
|
|
cvarSettings[RSK_KEYRINGS_RANDOM_COUNT] = CVar_GetS32("gRandomizeShuffleKeyRingsRandomCount", 8);
|
|
cvarSettings[RSK_KEYRINGS_FOREST_TEMPLE] = CVar_GetS32("gRandomizeShuffleKeyRingsForestTemple", 0);
|
|
cvarSettings[RSK_KEYRINGS_FIRE_TEMPLE] = CVar_GetS32("gRandomizeShuffleKeyRingsFireTemple", 0);
|
|
cvarSettings[RSK_KEYRINGS_WATER_TEMPLE] = CVar_GetS32("gRandomizeShuffleKeyRingsWaterTemple", 0);
|
|
cvarSettings[RSK_KEYRINGS_SPIRIT_TEMPLE] = CVar_GetS32("gRandomizeShuffleKeyRingsSpiritTemple", 0);
|
|
cvarSettings[RSK_KEYRINGS_SHADOW_TEMPLE] = CVar_GetS32("gRandomizeShuffleKeyRingsShadowTemple", 0);
|
|
cvarSettings[RSK_KEYRINGS_BOTTOM_OF_THE_WELL] = CVar_GetS32("gRandomizeShuffleKeyRingsBottomOfTheWell", 0);
|
|
cvarSettings[RSK_KEYRINGS_GTG] = CVar_GetS32("gRandomizeShuffleKeyRingsGTG", 0);
|
|
cvarSettings[RSK_KEYRINGS_GANONS_CASTLE] = CVar_GetS32("gRandomizeShuffleKeyRingsGanonsCastle", 0);
|
|
cvarSettings[RSK_BOSS_KEYSANITY] = CVar_GetS32("gRandomizeBossKeysanity", 2);
|
|
cvarSettings[RSK_GANONS_BOSS_KEY] = CVar_GetS32("gRandomizeShuffleGanonBossKey", RO_GANON_BOSS_KEY_VANILLA);
|
|
cvarSettings[RSK_LACS_STONE_COUNT] = CVar_GetS32("gRandomizeLacsStoneCount", 3);
|
|
cvarSettings[RSK_LACS_MEDALLION_COUNT] = CVar_GetS32("gRandomizeLacsMedallionCount", 6);
|
|
cvarSettings[RSK_LACS_REWARD_COUNT] = CVar_GetS32("gRandomizeLacsRewardCount", 9);
|
|
cvarSettings[RSK_LACS_DUNGEON_COUNT] = CVar_GetS32("gRandomizeLacsDungeonCount", 8);
|
|
cvarSettings[RSK_LACS_TOKEN_COUNT] = CVar_GetS32("gRandomizeLacsTokenCount", 100);
|
|
cvarSettings[RSK_STARTING_CONSUMABLES] = CVar_GetS32("gRandomizeStartingConsumables", 0);
|
|
cvarSettings[RSK_FULL_WALLETS] = CVar_GetS32("gRandomizeFullWallets", 0);
|
|
|
|
// RANDOTODO implement chest minigame shuffle with keysanity
|
|
cvarSettings[RSK_SHUFFLE_CHEST_MINIGAME] = false;
|
|
|
|
cvarSettings[RSK_LANGUAGE] = CVar_GetS32("gLanguages", 0);
|
|
|
|
cvarSettings[RSK_CUCCO_COUNT] = CVar_GetS32("gRandomizeCuccosToReturn", 7);
|
|
cvarSettings[RSK_BIG_POE_COUNT] = CVar_GetS32("gRandomizeBigPoeTargetCount", 10);
|
|
|
|
// If we skip child zelda, skip child stealth is pointless, so this needs to be reflected in the spoiler log
|
|
cvarSettings[RSK_SKIP_CHILD_STEALTH] =
|
|
!CVar_GetS32("gRandomizeSkipChildZelda", 0) && CVar_GetS32("gRandomizeSkipChildStealth", 0);
|
|
|
|
cvarSettings[RSK_SKIP_EPONA_RACE] = CVar_GetS32("gRandomizeSkipEponaRace", 0);
|
|
cvarSettings[RSK_SKIP_TOWER_ESCAPE] = CVar_GetS32("gRandomizeSkipTowerEscape", 0);
|
|
cvarSettings[RSK_COMPLETE_MASK_QUEST] = CVar_GetS32("gRandomizeCompleteMaskQuest", 0);
|
|
cvarSettings[RSK_SKIP_SCARECROWS_SONG] = CVar_GetS32("gRandomizeSkipScarecrowsSong", 0);
|
|
cvarSettings[RSK_ENABLE_GLITCH_CUTSCENES] = CVar_GetS32("gRandomizeEnableGlitchCutscenes", 0);
|
|
|
|
cvarSettings[RSK_SKULLS_SUNS_SONG] = CVar_GetS32("gRandomizeGsExpectSunsSong", 0);
|
|
// Link's Pocket has to have a dungeon reward if the other rewards are shuffled to end of dungeon.
|
|
cvarSettings[RSK_LINKS_POCKET] = CVar_GetS32("gRandomizeShuffleDungeonReward", RO_DUNGEON_REWARDS_END_OF_DUNGEON) != RO_DUNGEON_REWARDS_END_OF_DUNGEON ?
|
|
CVar_GetS32("gRandomizeLinksPocket", RO_LINKS_POCKET_DUNGEON_REWARD) :
|
|
RO_LINKS_POCKET_DUNGEON_REWARD;
|
|
|
|
if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) {
|
|
// If both OTRs are loaded.
|
|
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = CVar_GetS32("gRandomizeMqDungeons", 0);
|
|
cvarSettings[RSK_MQ_DUNGEON_COUNT] = CVar_GetS32("gRandomizeMqDungeonCount", 12);
|
|
} else if (OTRGlobals::Instance->HasMasterQuest()) {
|
|
// If only Master Quest is loaded.
|
|
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = 1;
|
|
cvarSettings[RSK_MQ_DUNGEON_COUNT] = 12;
|
|
} else {
|
|
// If only Original Quest is loaded.
|
|
cvarSettings[RSK_RANDOM_MQ_DUNGEONS] = 1;
|
|
cvarSettings[RSK_MQ_DUNGEON_COUNT] = 0;
|
|
}
|
|
|
|
// Enable if any of the entrance rando options are enabled.
|
|
cvarSettings[RSK_SHUFFLE_ENTRANCES] = CVar_GetS32("gRandomizeShuffleDungeonsEntrances", 0) ||
|
|
CVar_GetS32("gRandomizeShuffleOverworldEntrances", 0) ||
|
|
CVar_GetS32("gRandomizeShuffleInteriorsEntrances", 0) ||
|
|
CVar_GetS32("gRandomizeShuffleGrottosEntrances", 0);
|
|
|
|
cvarSettings[RSK_SHUFFLE_DUNGEON_ENTRANCES] = CVar_GetS32("gRandomizeShuffleDungeonsEntrances", 0);
|
|
cvarSettings[RSK_SHUFFLE_OVERWORLD_ENTRANCES] = CVar_GetS32("gRandomizeShuffleOverworldEntrances", 0);
|
|
cvarSettings[RSK_SHUFFLE_INTERIOR_ENTRANCES] = CVar_GetS32("gRandomizeShuffleInteriorsEntrances", 0);
|
|
cvarSettings[RSK_SHUFFLE_GROTTO_ENTRANCES] = CVar_GetS32("gRandomizeShuffleGrottosEntrances", 0);
|
|
|
|
// todo: this efficently when we build out cvar array support
|
|
std::set<RandomizerCheck> excludedLocations;
|
|
std::stringstream excludedLocationStringStream(CVar_GetString("gRandomizeExcludedLocations", ""));
|
|
std::string excludedLocationString;
|
|
while (getline(excludedLocationStringStream, excludedLocationString, ',')) {
|
|
excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString));
|
|
}
|
|
|
|
RandoMain::GenerateRando(cvarSettings, excludedLocations);
|
|
|
|
CVar_SetS32("gRandoGenerating", 0);
|
|
CVar_Save();
|
|
CVar_Load();
|
|
|
|
generated = 1;
|
|
}
|
|
|
|
void DrawRandoEditor(bool& open) {
|
|
if (generated) {
|
|
generated = 0;
|
|
randoThread.join();
|
|
}
|
|
|
|
if (!open) {
|
|
CVar_SetS32("gRandomizerSettingsEnabled", 0);
|
|
return;
|
|
}
|
|
|
|
// Randomizer settings
|
|
// Logic Settings
|
|
const char* randoLogicRules[2] = { "Glitchless", "No logic" };
|
|
|
|
// Open Settings
|
|
const char* randoForest[3] = { "Closed", "Closed Deku", "Open" };
|
|
const char* randoKakarikoGate[2] = { "Closed", "Open" };
|
|
const char* randoDoorOfTime[3] = { "Closed", "Song only", "Open" };
|
|
const char* randoZorasFountain[3] = { "Closed", "Closed as child", "Open" };
|
|
const char* randoGerudoFortress[3] = { "Normal", "Fast", "Open" };
|
|
const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions",
|
|
"Dungeon rewards", "Dungeons", "Tokens" };
|
|
const char* randoGanonsTrial[3] = { "Skip", "Set Number", "Random Number" };
|
|
const char* randoMqDungeons[3] = { "None", "Set Number", "Random Number" };
|
|
|
|
// World Settings
|
|
const char* randoStartingAge[3] = { "Child", "Adult", "Random" };
|
|
const char* randoShuffleDungeonsEntrances[3] = { "Off", "On", "On + Ganon" };
|
|
const char* randoShuffleInteriorsEntrances[3] = { "Off", "Simple", "All" };
|
|
const char* randoBombchusInLogic[2] = { "Off", "On" };
|
|
const char* randoAmmoDrops[3] = { "On + Bombchu", "Off", "On" };
|
|
const char* randoHeartDropsAndRefills[4] = { "On", "No Drop", "No Refill", "Off" };
|
|
|
|
// Shuffle Settings
|
|
const char* randoShuffleDungeonRewards[4] = { "End of dungeons", "Any dungeon", "Overworld", "Anywhere" };
|
|
const char* randoLinksPocket[4] = { "Dungeon Reward", "Advancement", "Anything", "Nothing" };
|
|
const char* randoShuffleSongs[3] = { "Song Locations", "Dungeon Rewards", "Anywhere" };
|
|
const char* randoShopsanity[7] = { "Off", "0 Items", "1 Item", "2 Items", "3 Items", "4 Items", "Random" };
|
|
const char* randoTokensanity[4] = { "Off", "Dungeons", "Overworld", "All Tokens" };
|
|
const char* randoShuffleScrubs[4] = { "Off", "Affordable", "Expensive", "Random Prices" };
|
|
const char* randoShuffleMerchants[3] = { "Off", "On (no hints)", "On (with hints)" };
|
|
|
|
// Shuffle Dungeon Items Settings
|
|
const char* randoShuffleMapsAndCompasses[6] = { "Start With", "Vanilla", "Own Dungeon",
|
|
"Any Dungeon", "Overworld", "Anywhere" };
|
|
const char* randoShuffleSmallKeys[6] = { "Start With", "Vanilla", "Own Dungeon",
|
|
"Any Dungeon", "Overworld", "Anywhere" };
|
|
const char* randoShuffleGerudoFortressKeys[4] = { "Vanilla", "Any Dungeon", "Overworld", "Anywhere" };
|
|
const char* randoShuffleBossKeys[6] = { "Start With", "Vanilla", "Own Dungeon",
|
|
"Any Dungeon", "Overworld", "Anywhere" };
|
|
const char* randoShuffleGanonsBossKey[12] = {"Vanilla", "Own dungeon", "Start with",
|
|
"Any Dungeon", "Overworld", "Anywhere",
|
|
"LACS-Vanilla", "LACS-Medallions", "LACS-Stones",
|
|
"LACS-Rewards", "LACS-Dungeons", "LACS-Tokens"};
|
|
const char* randoShuffleKeyRings[4] = { "Off", "Random", "Count", "Selection" };
|
|
|
|
// Misc Settings
|
|
const char* randoGossipStoneHints[4] = { "No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony" };
|
|
const char* randoHintClarity[3] = { "Obscure", "Ambiguous", "Clear" };
|
|
const char* randoHintDistribution[4] = { "Useless", "Balanced", "Strong", "Very Strong" };
|
|
const char* randoStartingTime[2] = { "Day", "Night" };
|
|
const char* randoRandomTrapDamage[3] = { "Basic", "Advanced", "Off" };
|
|
|
|
// Item Pool Settings
|
|
const char* randoItemPool[4] = { "Plentiful", "Balanced", "Scarce", "Minimal" };
|
|
const char* randoIceTraps[5] = { "Off", "Normal", "Extra", "Mayhem", "Onslaught" };
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(920, 600), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("Randomizer Editor", &open, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
|
|
|
|
bool disableEditingRandoSettings = CVar_GetS32("gRandoGenerating", 0) || CVar_GetS32("gOnFileSelectNameEntry", 0);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, disableEditingRandoSettings);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * (disableEditingRandoSettings ? 0.5f : 1.0f));
|
|
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
if (ImGui::Button("Generate Seed")) {
|
|
if (CVar_GetS32("gRandoGenerating", 0) == 0) {
|
|
randoThread = std::thread(&GenerateRandomizerImgui);
|
|
}
|
|
}
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
std::string spoilerfilepath = CVar_GetString("gSpoilerLog", "");
|
|
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
|
|
|
|
// RANDOTODO settings presets
|
|
// std::string presetfilepath = CVar_GetString("gLoadedPreset", "");
|
|
// ImGui::Text("Settings File: %s", presetfilepath.c_str());
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
|
static ImVec2 cellPadding(8.0f, 8.0f);
|
|
|
|
if (ImGui::BeginTabBar("Randomizer Settings", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
|
|
if (ImGui::BeginTabItem("World")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (ImGui::BeginTable("tableRandoWorld", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Area Access", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("World Settings", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Shuffle Entrances", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - Area Access
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildOpenSettings", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Forest
|
|
ImGui::Text(Settings::OpenForest.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Closed - Kokiri sword & shield are required to access "
|
|
"the Deku Tree, and completing the Deku Tree is required to "
|
|
"access the Hyrule Field exit.\n"
|
|
"\n"
|
|
"Closed Deku - Kokiri boy no longer blocks the path to Hyrule "
|
|
"Field but Mido still requires the Kokiri sword and Deku shield "
|
|
"to access the tree.\n"
|
|
"\n"
|
|
"Open - Mido no longer blocks the path to the Deku Tree. Kokiri "
|
|
"boy no longer blocks the path out of the forest."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeForest", randoForest, 3, RO_FOREST_CLOSED);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Kakariko Gate
|
|
ImGui::Text(Settings::OpenKakariko.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Closed - The gate will remain closed until Zelda's letter "
|
|
"is shown to the guard.\n"
|
|
"\n"
|
|
"Open - The gate is always open. The happy mask shop "
|
|
"will open immediately after obtaining Zelda's letter."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeKakarikoGate", randoKakarikoGate, 2, RO_KAK_GATE_CLOSED);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Door of Time
|
|
ImGui::Text(Settings::OpenDoorOfTime.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Closed - The Ocarina of Time, the Song of Time and all "
|
|
"three spiritual stones are required to open the Door of Time.\n"
|
|
"\n"
|
|
"Song only - Play the Song of Time in front of the Door of "
|
|
"Time to open it.\n"
|
|
"\n"
|
|
"Open - The Door of Time is permanently open with no requirements."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeDoorOfTime", randoDoorOfTime, 3, RO_DOOROFTIME_CLOSED);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Zora's Fountain
|
|
ImGui::Text(Settings::ZorasFountain.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Closed - King Zora obstructs the way to Zora's Fountain. "
|
|
"Ruto's letter must be shown as child Link in order to move "
|
|
"him in both time periods.\n"
|
|
"\n"
|
|
"Closed as child - Ruto's Letter is only required to move King Zora "
|
|
"as child Link. Zora's Fountain starts open as adult.\n"
|
|
"\n"
|
|
"Open - King Zora has already mweeped out of the way in both "
|
|
"time periods. Ruto's Letter is removed from the item pool."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeZorasFountain", randoZorasFountain, 3, RO_ZF_CLOSED);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - World Settings
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildMiscWorldSettings", ImVec2(0,-8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
//Starting Age
|
|
//Disabled when Forest is set to Closed or under very specific conditions
|
|
//RANDOTODO: Replace magic number checks with enums
|
|
bool disableRandoStartingAge = (CVar_GetS32("gRandomizeLogicRules", 0) == 0) && // glitchless logic
|
|
((CVar_GetS32("gRandomizeForest", RO_FOREST_CLOSED) == RO_FOREST_CLOSED) ||
|
|
((CVar_GetS32("gRandomizeDoorOfTime", RO_DOOROFTIME_CLOSED) == RO_DOOROFTIME_CLOSED) &&
|
|
(CVar_GetS32("gRandomizeShuffleOcarinas", 0) == 0))); // ocarinas not shuffled
|
|
|
|
const char* disableRandoStartingAgeText = "This option is disabled due to other options making the game unbeatable.";
|
|
ImGui::Text(Settings::StartingAge.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Choose which age Link will start as.\n\n"
|
|
"Starting as adult means you start with the Master Sword in your inventory.\n"
|
|
"The child option is forcefully set if it would conflict with other options."
|
|
);
|
|
if (disableRandoStartingAge) {
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
|
}
|
|
UIWidgets::EnhancementCombobox("gRandomizeStartingAge", randoStartingAge, 3, RO_AGE_CHILD);
|
|
if (disableRandoStartingAge) {
|
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
|
ImGui::SetTooltip("%s", disableRandoStartingAgeText);
|
|
}
|
|
CVar_SetS32("gRandomizeStartingAge", RO_AGE_CHILD);
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::PopItemFlag();
|
|
}
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Gerudo Fortress
|
|
ImGui::Text("Gerudo Fortress Carpenters");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets the amount of carpenters required to repair the bridge "
|
|
"in Gerudo Valley.\n"
|
|
"\n"
|
|
"Normal - All 4 carpenters are required to be saved.\n"
|
|
"\n"
|
|
"Fast - Only the bottom left carpenter requires rescuing.\n"
|
|
"\n"
|
|
"Open - The bridge is repaired from the start."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeGerudoFortress", randoGerudoFortress, 3, RO_GF_NORMAL);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Rainbow Bridge
|
|
ImGui::Text("Rainbow Bridge Requirements");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Alters the requirements to open the bridge to Ganon's Castle.\n"
|
|
"\n"
|
|
"Vanilla - Obtain the Shadow Medallion, Spirit Medallion and Light Arrows.\n"
|
|
"\n"
|
|
"Always open - No requirements.\n"
|
|
"\n"
|
|
"Stones - Obtain the specified amount of spiritual stones.\n"
|
|
"\n"
|
|
"Medallions - Obtain the specified amount of medallions.\n"
|
|
"\n"
|
|
"Dungeon rewards - Obtain the specified total sum of spiritual "
|
|
"stones or medallions.\n"
|
|
"\n"
|
|
"Dungeons - Complete the specified amount of dungeons. Dungeons "
|
|
"are considered complete after stepping in to the blue warp after "
|
|
"the boss.\n"
|
|
"\n"
|
|
"Tokens - Obtain the specified amount of Skulltula tokens."
|
|
);
|
|
|
|
UIWidgets::EnhancementCombobox("gRandomizeRainbowBridge", randoRainbowBridge, 7, RO_BRIDGE_VANILLA);
|
|
ImGui::PopItemWidth();
|
|
switch (CVar_GetS32("gRandomizeRainbowBridge", 0)) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Stone Count: %d", "##RandoStoneCount",
|
|
"gRandomizeStoneCount", 1, 3, "", 3, true);
|
|
break;
|
|
case 3:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Medallion Count: %d", "##RandoMedallionCount",
|
|
"gRandomizeMedallionCount", 1, 6, "", 6, true);
|
|
break;
|
|
case 4:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Reward Count: %d", "##RandoRewardCount",
|
|
"gRandomizeRewardCount", 1, 9, "", 9, true);
|
|
break;
|
|
case 5:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Dungeon Count: %d", "##RandoDungeonCount",
|
|
"gRandomizeDungeonCount", 1, 8, "", 8, true);
|
|
break;
|
|
case 6:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Token Count: %d", "##RandoTokenCount",
|
|
"gRandomizeTokenCount", 1, 100, "", 100, true);
|
|
break;
|
|
}
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Ganon's Trials
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
ImGui::Text("Ganon's Trials");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"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."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 3, RO_GANONS_TRIALS_SET_NUMBER);
|
|
ImGui::PopItemWidth();
|
|
if (CVar_GetS32("gRandomizeGanonTrial", RO_GANONS_TRIALS_SET_NUMBER) == RO_GANONS_TRIALS_SET_NUMBER) {
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Ganon's Trial Count: %d", "##RandoTrialCount",
|
|
"gRandomizeGanonTrialCount", 1, 6, "", 6, true);
|
|
UIWidgets::InsertHelpHoverText("Set the number of trials required to enter Ganon's Tower.");
|
|
}
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
if (OTRGlobals::Instance->HasMasterQuest() && OTRGlobals::Instance->HasOriginal()) {
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
ImGui::Text("Master Quest Dungeons");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets the number of Master Quest Dungeons that are shuffled into the pool.\n"
|
|
"\n"
|
|
"None - All Dungeons will be their Vanilla versions.\n"
|
|
"\n"
|
|
"Set Number - Select a number of dungeons that will be their Master Quest versions "
|
|
"using the slider below. Which dungeons are set to be the Master Quest variety will be random.\n"
|
|
"\n"
|
|
"Random Number - A Random number and set of dungeons will be their Master Quest varieties."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeMqDungeons", randoMqDungeons, 3, CVar_GetS32("gRandomizeMqDungeons", 0));
|
|
ImGui::PopItemWidth();
|
|
if (CVar_GetS32("gRandomizeMqDungeons", 0) == 1) {
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Master Quest Dungeon Count: %d", "##RandoMqDungeonCount",
|
|
"gRandomizeMqDungeonCount", 1, 12, "", CVar_GetS32("gRandomizeMqDungeonCount", 12), true);
|
|
}
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 3 - Shuffle Entrances
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildShuffleEntrances", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Shuffle Dungeon Entrances
|
|
ImGui::Text("Shuffle Dungeon Entrances");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffle the pool of dungeon entrances, including Bottom of the Well, Ice Cavern and Gerudo Training Grounds.\n"
|
|
"\n"
|
|
"Shuffling Ganon's Castle can be enabled separately.\n"
|
|
"\n"
|
|
"Additionally, the entrances of Deku Tree, Fire Temple, Bottom of the Well and Gerudo Training Ground are opened for both child and adult.\n"
|
|
"\n"
|
|
"- Deku Tree will be open for adult after Mido has seen child Link with a sword and shield.\n"
|
|
"- Bottom of the Well will be open for adult after playing Song of Storms to the Windmill guy as child.\n"
|
|
"- Gerudo Training Ground will be open for child after adult has paid to open the gate once."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleDungeonsEntrances", randoShuffleDungeonsEntrances, 3, RO_DUNGEON_ENTRANCE_SHUFFLE_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Overworld Entrances
|
|
UIWidgets::EnhancementCheckbox("Shuffle Overworld Entrances", "gRandomizeShuffleOverworldEntrances");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffle the pool of Overworld entrances, which corresponds to almost all loading zones between overworld areas.\n"
|
|
"\n"
|
|
"Some entrances are unshuffled to avoid issues:\n"
|
|
"- Hyrule Castle Courtyard and Garden entrance\n"
|
|
"- Both Market Back Alley entrances\n"
|
|
"- Gerudo Valley to Lake Hylia (unless entrances are decoupled)"
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Interior Entrances
|
|
ImGui::Text("Shuffle Interior Entrances");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffle the pool of interior entrances which contains most Houses and all Great Fairies.\n"
|
|
"\n"
|
|
"All - An extended version of 'Simple' with some extra places:\n"
|
|
"- Windmill\n"
|
|
"- Link's House\n"
|
|
"- Temple of Time\n"
|
|
"- Kakariko Potion Shop"
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleInteriorsEntrances", randoShuffleInteriorsEntrances, 3, RO_INTERIOR_ENTRANCE_SHUFFLE_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Grotto Entrances
|
|
UIWidgets::EnhancementCheckbox("Shuffle Grotto Entrances", "gRandomizeShuffleGrottosEntrances");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffle the pool of grotto entrances, including all graves, small Fairy fountains and the Deku Theatre."
|
|
);
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Items")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (ImGui::BeginTable("tableRandoStartingInventory", 3,
|
|
ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Shuffle Items", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Shuffle NPCs & Merchants", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Shuffle Dungeon Items", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - Shuffle Items
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildShuffleItems", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Shuffle Songs
|
|
ImGui::Text(Settings::ShuffleSongs.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Song locations - Songs will only appear at locations that normally teach songs.\n"
|
|
"\n"
|
|
"Dungeon rewards - Songs appear after beating a major dungeon boss.\n"
|
|
"The 4 remaining songs are located at:\n"
|
|
" - Zelda's lullaby location\n"
|
|
" - Ice Cavern's Serenade of Water location\n"
|
|
" - Bottom of the Well Lens of Truth location\n"
|
|
" - Gerudo Training Ground's Ice Arrows location\n"
|
|
"\n"
|
|
"Anywhere - Songs can appear at any location."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleSongs", randoShuffleSongs, 3, RO_SONG_SHUFFLE_SONG_LOCATIONS);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Tokens
|
|
ImGui::Text(Settings::Tokensanity.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles Golden Skulltula Tokens into the item pool. This means "
|
|
"Golden Skulltulas can contain other items as well.\n"
|
|
"\n"
|
|
"Off - GS tokens will not be shuffled.\n"
|
|
"\n"
|
|
"Dungeons - Only shuffle GS tokens that are within dungeons.\n"
|
|
"\n"
|
|
"Overworld - Only shuffle GS tokens that are outside of dungeons.\n"
|
|
"\n"
|
|
"All Tokens - Shuffle all 100 GS tokens."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleTokens", randoTokensanity, 4, RO_TOKENSANITY_OFF);
|
|
|
|
ImGui::Dummy(ImVec2(0,0));
|
|
|
|
UIWidgets::EnhancementCheckbox("Nighttime GS expect Sun's Song", "gRandomizeGsExpectSunsSong");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"All Golden Skulltulas that require nighttime to appear will only be "
|
|
"expected to be collected after getting Sun's Song."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Kokiri Sword
|
|
// Disabled when Start with Kokiri Sword is active
|
|
bool disableShuffleKokiriSword = CVar_GetS32("gRandomizeStartingKokiriSword", 0);
|
|
const char* disableShuffleKokiriSwordText = "This option is disabled because \"Start with Kokiri Sword\" is enabled.";
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleKokiriSword.GetName().c_str(), "gRandomizeShuffleKokiriSword",
|
|
disableShuffleKokiriSword, disableShuffleKokiriSwordText);
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles the Kokiri Sword into the item pool.\n"
|
|
"\n"
|
|
"This will require the use of sticks until the Kokiri Sword is found."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Ocarinas
|
|
// Disabled when Start with Ocarina is active
|
|
bool disableShuffleOcarinas = CVar_GetS32("gRandomizeStartingOcarina", 0);
|
|
const char* disableShuffleOcarinasText = "This option is disabled because \"Start with Fairy Ocarina\" is enabled.";
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleOcarinas.GetName().c_str(), "gRandomizeShuffleOcarinas",
|
|
disableShuffleOcarinas, disableShuffleOcarinasText);
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Enabling this shuffles the Fairy Ocarina and the Ocarina of Time into the item pool.\n"
|
|
"\n"
|
|
"This will require finding an Ocarina before being able to play songs."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Weird Egg
|
|
// Disabled when Skip Child Zelda is active
|
|
bool disableShuffleWeirdEgg = CVar_GetS32("gRandomizeSkipChildZelda", 0);
|
|
const char* disableShuffleWeirdEggText = "This option is disabled because \"Skip Child Zelda\" is enabled.";
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleWeirdEgg.GetName().c_str(), "gRandomizeShuffleWeirdEgg",
|
|
disableShuffleWeirdEgg, disableShuffleWeirdEggText);
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles the Weird Egg from Malon in to the item pool. Enabling "
|
|
"\"Skip Child Zelda\" disables this feature.\n"
|
|
"\n"
|
|
"The Weird Egg is required to unlock several events:\n"
|
|
" - Zelda's Lullaby from Impa\n"
|
|
" - Saria's song in Sacred Forest Meadow\n"
|
|
" - Epona's song and chicken minigame at Lon Lon Ranch\n"
|
|
" - Zelda's letter for Kakariko gate (if set to closed)\n"
|
|
" - Happy Mask Shop sidequest\n"
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Gerudo Membership Card
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleGerudoToken.GetName().c_str(),
|
|
"gRandomizeShuffleGerudoToken");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles the Gerudo Membership Card into the item pool.\n"
|
|
"\n"
|
|
"The Gerudo Card is required to enter the Gerudo Training Grounds, opening "
|
|
"the gate to Haunted Wasteland and the Horseback Archery minigame."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - Shuffle NPCs & Merchants
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildShuffleNpcs", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Shopsanity
|
|
ImGui::Text(Settings::Shopsanity.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Off - All shop items will be the same as vanilla.\n"
|
|
"\n"
|
|
"0 Items - Vanilla shop items will be shuffled among different shops.\n"
|
|
"\n"
|
|
"1-4 Items - Vanilla shop items will be shuffled among different shops, and each shop will contain 1-4 non-vanilla shop items.\n"
|
|
"\n"
|
|
"Random - Vanilla shop items will be shuffled among different shops, and each shop will contain a random number(1-4) of non-vanilla shop items.\n"
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShopsanity", randoShopsanity, 7, RO_SHOPSANITY_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Scrubs
|
|
ImGui::Text(Settings::Scrubsanity.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Off - Scrubs will not be shuffled. The 3 Scrubs that give one-time items in the vanilla game "
|
|
"(PoH, Deku Nut capacity, and Deku Stick capacity) will have random items.\n"
|
|
"\n"
|
|
"Affordable - Scrubs will be shuffled and their item will cost 10 rupees.\n"
|
|
"\n"
|
|
"Expensive - Scrubs will be shuffled and their item will cost the vanilla price.\n"
|
|
"\n"
|
|
"Random - Scrubs will be shuffled and their item will cost will be between 0-95 rupees.\n");
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleScrubs", randoShuffleScrubs, 4, RO_SCRUBS_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Cows
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleCows.GetName().c_str(), "gRandomizeShuffleCows");
|
|
UIWidgets::InsertHelpHoverText("Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleMagicBeans.GetName().c_str(), "gRandomizeShuffleBeans");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Enabling this adds a pack of 10 beans to the item pool and changes the Magic Bean "
|
|
"Salesman to sell a random item at a price of 60 rupees."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Merchants
|
|
ImGui::Text(Settings::ShuffleMerchants.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Enabling this adds a Giant's Knife and a pack of Bombchus to the item pool "
|
|
"and changes both Medigoron and the Haunted Wasteland Carpet Salesman to sell "
|
|
"a random item once at the price of 200 rupees.\n\n"
|
|
"On (no hints) - Salesmen will be included but won't tell you what you'll get.\n"
|
|
"On (with hints) - Salesmen will be included and you'll know what you're buying."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleMerchants", randoShuffleMerchants, 3, RO_SHUFFLE_MERCHANTS_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Frog Song Rupees
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleFrogSongRupees.GetName().c_str(), "gRandomizeShuffleFrogSongRupees");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles 5 Purple Rupees into to the item pool, and allows\n"
|
|
"you to earn items by playing songs at the Frog Choir.\n"
|
|
"\n"
|
|
"This setting does not effect the item earned from playing\n"
|
|
"the Song of Storms and the frog song minigame."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Shuffle Adult Trade Quest
|
|
UIWidgets::EnhancementCheckbox(Settings::ShuffleAdultTradeQuest.GetName().c_str(),
|
|
"gRandomizeShuffleAdultTrade");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Adds all of the adult trade quest items into the pool, each of which "
|
|
"can be traded for a unique reward.\n"
|
|
"\n"
|
|
"You will be able to choose which of your owned adult trade items is visible "
|
|
"in the inventory by selecting the item with A and using the control stick or "
|
|
"D-pad.\n"
|
|
"\n"
|
|
"If disabled, only the Claim Check will be found in the pool."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 3 - Shuffle Dungeon Items
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildShuffleDungeonItems", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Shuffle Dungeon Rewards
|
|
ImGui::Text(Settings::ShuffleRewards.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Shuffles the location of spiritual stones and medallions.\n"
|
|
"\n"
|
|
"End of dungeons - Spiritual stones and medallions will be given as rewards "
|
|
"for beating major dungeons. Link will always start with one stone or medallion.\n"
|
|
"\n"
|
|
"Any dungeon - Spiritual stones and medallions can be found inside any dungeon.\n"
|
|
"\n"
|
|
"Overworld - Spiritual stones and medallions can only be found outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Spiritual stones and medallions can appear anywhere."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleDungeonReward", randoShuffleDungeonRewards, 4, RO_DUNGEON_REWARDS_END_OF_DUNGEON);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Maps & Compasses
|
|
ImGui::Text(Settings::MapsAndCompasses.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Start with - You will start with Maps & Compasses from all dungeons.\n"
|
|
"\n"
|
|
"Vanilla - Maps & Compasses will appear in their vanilla locations.\n"
|
|
"\n"
|
|
"Own dungeon - Maps & Compasses can only appear in their respective dungeon.\n"
|
|
"\n"
|
|
"Any dungeon - Maps & Compasses can only appear inside of any dungon.\n"
|
|
"\n"
|
|
"Overworld - Maps & Compasses can only appear outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Maps & Compasses can appear anywhere in the world."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeStartingMapsCompasses", randoShuffleMapsAndCompasses, 6, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Keysanity
|
|
ImGui::Text(Settings::Keysanity.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Start with - You will start with all Small Keys from all dungeons.\n"
|
|
"\n"
|
|
"Vanilla - Small Keys will appear in their vanilla locations.\n"
|
|
"\n"
|
|
"Own dungeon - Small Keys can only appear in their respective dungeon.\n"
|
|
"\n"
|
|
"Any dungeon - Small Keys can only appear inside of any dungon.\n"
|
|
"\n"
|
|
"Overworld - Small Keys can only appear outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Small Keys can appear anywhere in the world."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeKeysanity", randoShuffleSmallKeys, 6, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Key Rings
|
|
ImGui::Text(Settings::KeyRings.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Keyrings will replace all small keys from a particular dungeon with a single keyring that awards all keys for it's associated dungeon\n"
|
|
"\n"
|
|
"Off - No dungeons will have their keys replaced with keyrings.\n"
|
|
"\n"
|
|
"Random - A random amount of dungeons(0-8) will have their keys replaced with keyrings.\n"
|
|
"\n"
|
|
"Count - A specified amount of randomly selected dungeons will have their keys replaced with keyrings.\n"
|
|
"\n"
|
|
"Selection - Hand select which dungeons will have their keys replaced with keyrings."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleKeyRings", randoShuffleKeyRings, 4, RO_KEYRINGS_OFF);
|
|
ImGui::PopItemWidth();
|
|
switch (CVar_GetS32("gRandomizeShuffleKeyRings", RO_KEYRINGS_OFF)) {
|
|
case RO_KEYRINGS_COUNT:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Key Ring Count: %d", "##RandomizeShuffleKeyRingsRandomCount",
|
|
"gRandomizeShuffleKeyRingsRandomCount", 1, 8, "", 8, true);
|
|
break;
|
|
case RO_KEYRINGS_SELECTION:
|
|
UIWidgets::EnhancementCheckbox("Forest Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsForestTemple");
|
|
UIWidgets::EnhancementCheckbox("Fire Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsFireTemple");
|
|
UIWidgets::EnhancementCheckbox("Water Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsWaterTemple");
|
|
UIWidgets::EnhancementCheckbox("Spirit Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsSpiritTemple");
|
|
UIWidgets::EnhancementCheckbox("Shadow Temple##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsShadowTemple");
|
|
UIWidgets::EnhancementCheckbox("Bottom of the Well##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsBottomOfTheWell");
|
|
UIWidgets::EnhancementCheckbox("GTG##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsGTG");
|
|
UIWidgets::EnhancementCheckbox("Ganon's Castle##RandomizeShuffleKeyRings", "gRandomizeShuffleKeyRingsGanonsCastle");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Gerudo Keys
|
|
ImGui::Text(Settings::GerudoKeys.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Vanilla - Thieve's Hideout Keys will appear in their vanilla locations.\n"
|
|
"\n"
|
|
"Any dungeon - Thieve's Hideout Keys can only appear inside of any dungon.\n"
|
|
"\n"
|
|
"Overworld - Thieve's Hideout Keys can only appear outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Thieve's Hideout Keys can appear anywhere in the world."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeGerudoKeys", randoShuffleGerudoFortressKeys, 4, RO_GERUDO_KEYS_VANILLA);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Boss Keysanity
|
|
ImGui::Text(Settings::BossKeysanity.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Start with - You will start with Boss keys from all dungeons.\n"
|
|
"\n"
|
|
"Vanilla - Boss Keys will appear in their vanilla locations.\n"
|
|
"\n"
|
|
"Own dungeon - Boss Keys can only appear in their respective dungeon.\n"
|
|
"\n"
|
|
"Any dungeon - Boss Keys can only appear inside of any dungon.\n"
|
|
"\n"
|
|
"Overworld - Boss Keys can only appear outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Boss Keys can appear anywhere in the world."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeBossKeysanity", randoShuffleBossKeys, 6, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Ganon's Boss Key
|
|
ImGui::Text(Settings::GanonsBossKey.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Vanilla - Ganon's Boss Key will appear in the vanilla location.\n"
|
|
"\n"
|
|
"Own dungeon - Ganon's Boss Key can appear anywhere inside Ganon's Castle.\n"
|
|
"\n"
|
|
"Start with - Places Ganon's Boss Key in your starting inventory."
|
|
"\n"
|
|
"Any dungeon - Ganon's Boss Key Key can only appear inside of any dungon.\n"
|
|
"\n"
|
|
"Overworld - Ganon's Boss Key Key can only appear outside of dungeons.\n"
|
|
"\n"
|
|
"Anywhere - Ganon's Boss Key Key can appear anywhere in the world.\n"
|
|
"\n"
|
|
"LACS - These settings put the boss key on the Light Arrow Cutscene location, from Zelda in Temple of Time as adult, with differing requirements:\n"
|
|
"- Vanilla: Obtain the Shadow Medallion and Spirit Medallion\n"
|
|
"- Medallions: Obtain the specified amount of medallions.\n"
|
|
"- Stones: Obtain the specified amount of spiritual stones.\n"
|
|
"- Dungeon rewards: Obtain the specified total sum of spiritual stones or medallions.\n"
|
|
"- Dungeons: Complete the specified amount of dungeons. Dungeons are considered complete after stepping in to the blue warp after the boss.\n"
|
|
"- Tokens: Obtain the specified amount of Skulltula tokens."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeShuffleGanonBossKey", randoShuffleGanonsBossKey, 12, RO_GANON_BOSS_KEY_VANILLA);
|
|
ImGui::PopItemWidth();
|
|
switch (CVar_GetS32("gRandomizeShuffleGanonBossKey", RO_GANON_BOSS_KEY_VANILLA)) {
|
|
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Medallion Count: %d", "##RandoLacsMedallionCount",
|
|
"gRandomizeLacsMedallionCount", 1, 6, "", 6, true);
|
|
break;
|
|
case RO_GANON_BOSS_KEY_LACS_STONES:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Stone Count: %d", "##RandoLacsStoneCount",
|
|
"gRandomizeLacsStoneCount", 1, 3, "", 3, true);
|
|
break;
|
|
case RO_GANON_BOSS_KEY_LACS_REWARDS:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Reward Count: %d", "##RandoLacsRewardCount",
|
|
"gRandomizeLacsRewardCount", 1, 9, "", 9, true);
|
|
break;
|
|
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Dungeon Count: %d", "##RandoLacsDungeonCount",
|
|
"gRandomizeLacsDungeonCount", 1, 8, "", 8, true);
|
|
break;
|
|
case RO_GANON_BOSS_KEY_LACS_TOKENS:
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
UIWidgets::EnhancementSliderInt("Token Count: %d", "##RandoLacsTokenCount",
|
|
"gRandomizeLacsTokenCount", 1, 100, "", 100, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Gameplay")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (ImGui::BeginTable("tableRandoGameplay", 3, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Timesavers", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Item Pool & Hints", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Additional Features", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - TIME SAVERS
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildTimeSavers", ImVec2(0, -8));
|
|
|
|
// Cuccos to return
|
|
UIWidgets::EnhancementSliderInt("Cuccos to return: %d", "##RandoCuccosToReturn",
|
|
"gRandomizeCuccosToReturn", 0, 7, "", 7, true);
|
|
UIWidgets::InsertHelpHoverText("The amount of cuccos needed to claim the reward from Anju the cucco lady");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Big Poe Target Count
|
|
UIWidgets::EnhancementSliderInt("Big Poe Target Count: %d", "##RandoBigPoeTargetCount",
|
|
"gRandomizeBigPoeTargetCount", 1, 10, "", 10, true);
|
|
UIWidgets::InsertHelpHoverText("The Poe collector will give a reward for turning in this many Big Poes.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Skip child stealth
|
|
// Disabled when Skip Child Zelda is active
|
|
bool disableChildStealth = CVar_GetS32("gRandomizeSkipChildZelda", 0);
|
|
const char* disableChildStealthText = "This option is disabled because \"Skip Child Zelda\" is enabled";
|
|
UIWidgets::EnhancementCheckbox(Settings::SkipChildStealth.GetName().c_str(), "gRandomizeSkipChildStealth", disableChildStealth, disableChildStealthText);
|
|
UIWidgets::InsertHelpHoverText("The crawlspace into Hyrule Castle goes straight to Zelda, skipping the guards.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Skip child zelda
|
|
UIWidgets::EnhancementCheckbox("Skip Child Zelda", "gRandomizeSkipChildZelda");
|
|
UIWidgets::InsertHelpHoverText("Start with Zelda's Letter and the item Impa would normally give you and skip the sequence up "
|
|
"until after meeting Zelda. Disables the ability to shuffle Weird Egg.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Skip Epona race
|
|
UIWidgets::EnhancementCheckbox(Settings::SkipEponaRace.GetName().c_str(), "gRandomizeSkipEponaRace");
|
|
UIWidgets::InsertHelpHoverText("Epona can be summoned with Epona's Song without needing to race Ingo.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Skip tower escape
|
|
UIWidgets::EnhancementCheckbox(Settings::SkipTowerEscape.GetName().c_str(), "gRandomizeSkipTowerEscape");
|
|
UIWidgets::InsertHelpHoverText("The tower escape sequence between Ganondorf and Ganon will be skipped.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Complete mask quest
|
|
UIWidgets::EnhancementCheckbox(Settings::CompleteMaskQuest.GetName().c_str(),
|
|
"gRandomizeCompleteMaskQuest");
|
|
UIWidgets::InsertHelpHoverText("Once the happy mask shop is opened, all masks will be available to be borrowed.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Skip Scarecrow Song
|
|
UIWidgets::EnhancementCheckbox(Settings::FreeScarecrow.GetName().c_str(),
|
|
"gRandomizeSkipScarecrowsSong");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Start with the ability to summon Pierre the scarecrow. Pulling out an ocarina in the usual locations will automatically summon him."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - Item Pool & Hint Settings
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildItemPoolHintSettings", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
// Item Pool Settings
|
|
ImGui::Text(Settings::ItemPoolValue.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets how many major items appear in the item pool.\n"
|
|
"\n"
|
|
"Plentiful - Extra major items are added to the pool.\n"
|
|
"\n"
|
|
"Balanced - Original item pool.\n"
|
|
"\n"
|
|
"Scarce - Some excess items are removed, including health upgrades.\n"
|
|
"\n"
|
|
"Minimal - Most excess items are removed."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeItemPool", randoItemPool, 4, RO_ITEM_POOL_BALANCED);
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Ice Traps
|
|
ImGui::Text(Settings::IceTrapValue.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets how many items are replaced by ice traps.\n"
|
|
"\n"
|
|
"Off - No ice traps.\n"
|
|
"\n"
|
|
"Normal - Only Ice Traps from the base item pool are shuffled in.\n"
|
|
"\n"
|
|
"Extra - Chance to replace added junk items with additional ice traps.\n"
|
|
"\n"
|
|
"Mayhem - All added junk items will be Ice Traps.\n"
|
|
"\n"
|
|
"Onslaught - All junk items will be replaced by Ice Traps, even those "
|
|
"in the base pool."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeIceTraps", randoIceTraps, 5, RO_ICE_TRAPS_NORMAL);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Gossip Stone Hints
|
|
ImGui::Text(Settings::GossipStoneHints.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Allows Gossip Stones to provide hints on item locations. Hints mentioning "
|
|
"\"Way of the Hero\" indicate a location that holds an item required to beat "
|
|
"the seed.\n"
|
|
"\n"
|
|
"No hints - No hints will be given at all.\n"
|
|
"\n"
|
|
"Need Nothing - Hints are always available from Gossip Stones.\n"
|
|
"\n"
|
|
"Need Stone of Agony - Hints are only available after obtaining the Stone of Agony.\n"
|
|
"\n"
|
|
"Need Mask of Truth - Hints are only available whilst wearing the Mask of Truth.\n");
|
|
UIWidgets::EnhancementCombobox("gRandomizeGossipStoneHints", randoGossipStoneHints, 4, RO_GOSSIP_STONES_NEED_NOTHING);
|
|
if (CVar_GetS32("gRandomizeGossipStoneHints", 1) != 0) {
|
|
// Hint Clarity
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
ImGui::Indent();
|
|
ImGui::Text(Settings::ClearerHints.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets the difficulty of hints.\n"
|
|
"\n"
|
|
"Obscure - Hints are unique for each item, but the writing may be cryptic.\n"
|
|
"Ex: Kokiri Sword > a butter knife\n"
|
|
"\n"
|
|
"Ambiguous - Hints are clearly written, but may refer to more than one item.\n"
|
|
"Ex: Kokiri Sword > a sword\n"
|
|
"\n"
|
|
"Clear - Hints are clearly written and are unique for each item.\n"
|
|
"Ex: Kokiri Sword > the Kokiri Sword"
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeHintClarity", randoHintClarity, 3, RO_HINT_CLARITY_CLEAR);
|
|
|
|
// Hint Distribution
|
|
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
|
ImGui::Text(Settings::HintDistribution.GetName().c_str());
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Sets how many hints will be useful.\n"
|
|
"\n"
|
|
"Useless - Only junk hints.\n"
|
|
"\n"
|
|
"Balanced - Recommended hint spread.\n"
|
|
"\n"
|
|
"Strong - More useful hints.\n"
|
|
"\n"
|
|
"Very Strong - Many powerful hints."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeHintDistribution", randoHintDistribution, 4, RO_HINT_DIST_BALANCED);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 3 - Additional Features
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildAdditionalFeatures", ImVec2(0, -8));
|
|
ImGui::PushItemWidth(-FLT_MIN);
|
|
|
|
UIWidgets::EnhancementCheckbox("Full Wallets", "gRandomizeFullWallets");
|
|
UIWidgets::InsertHelpHoverText("Start with a full wallet. All wallet upgrades come filled with rupees.");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Bombchus in Logic
|
|
UIWidgets::EnhancementCheckbox(Settings::BombchusInLogic.GetName().c_str(), "gRandomizeBombchusInLogic");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Bombchus are properly considered in logic.\n"
|
|
"\n"
|
|
"The first Bombchu pack will always be 20, and subsequent packs will be "
|
|
"5 or 10 based on how many you have.\n"
|
|
"Once found, they can be replenished at the Bombchu shop.\n"
|
|
"\n"
|
|
"Bombchu Bowling is opened by obtaining Bombchus."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Enable Bombchu Drops
|
|
UIWidgets::EnhancementCheckbox("Enable Bombchu Drops", "gRandomizeEnableBombchuDrops");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Once you obtain bombchus for the first time, refills can be found in bushes and other places where bomb drops can normally spawn."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
UIWidgets::EnhancementCheckbox("Blue Fire Arrows", "gRandomizeBlueFireArrows");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Ice Arrows act like Blue Fire, making them able to melt red ice. "
|
|
"Item placement logic will respect this option, so it might be required to use this to progress."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
UIWidgets::EnhancementCheckbox("Sunlight Arrows", "gRandomizeSunlightArrows");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Light Arrows can be used to light up the sun switches instead of using the Mirror Shield. "
|
|
"Item placement logic will respect this option, so it might be required to use this to progress."
|
|
);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndChild();
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
static bool locationsTabOpen = false;
|
|
if (ImGui::BeginTabItem("Locations")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (!locationsTabOpen) {
|
|
locationsTabOpen = true;
|
|
RandomizerCheckObjects::UpdateImGuiVisibility();
|
|
// todo: this efficently when we build out cvar array support
|
|
std::stringstream excludedLocationStringStream(CVar_GetString("gRandomizeExcludedLocations", ""));
|
|
std::string excludedLocationString;
|
|
excludedLocations.clear();
|
|
while (getline(excludedLocationStringStream, excludedLocationString, ',')) {
|
|
excludedLocations.insert((RandomizerCheck)std::stoi(excludedLocationString));
|
|
}
|
|
}
|
|
|
|
if (ImGui::BeginTable("tableRandoLocations", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Included", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Excluded", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - INCLUDED LOCATIONS
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
|
|
static ImGuiTextFilter locationSearch;
|
|
locationSearch.Draw();
|
|
|
|
ImGui::BeginChild("ChildIncludedLocations", ImVec2(0, -8));
|
|
for (auto [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) {
|
|
bool hasItems = false;
|
|
for (auto [randomizerCheck, rcObject] : rcObjects) {
|
|
if (rcObject.visibleInImgui && !excludedLocations.count(rcObject.rc) &&
|
|
locationSearch.PassFilter(rcObject.rcSpoilerName.c_str())) {
|
|
|
|
hasItems = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasItems) {
|
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
|
if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) {
|
|
for (auto [randomizerCheck, rcObject] : rcObjects) {
|
|
if (rcObject.visibleInImgui && !excludedLocations.count(rcObject.rc) &&
|
|
locationSearch.PassFilter(rcObject.rcSpoilerName.c_str())) {
|
|
|
|
if (ImGui::ArrowButton(std::to_string(rcObject.rc).c_str(), ImGuiDir_Right)) {
|
|
excludedLocations.insert(rcObject.rc);
|
|
// todo: this efficently when we build out cvar array support
|
|
std::string excludedLocationString = "";
|
|
for (auto excludedLocationIt : excludedLocations) {
|
|
excludedLocationString += std::to_string(excludedLocationIt);
|
|
excludedLocationString += ",";
|
|
}
|
|
CVar_SetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
|
SohImGui::RequestCvarSaveOnNextTick();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text(rcObject.rcShortName.c_str());
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - EXCLUDED LOCATIONS
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
|
|
ImGui::BeginChild("ChildExcludedLocations", ImVec2(0, -8));
|
|
for (auto [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) {
|
|
bool hasItems = false;
|
|
for (auto [randomizerCheck, rcObject] : rcObjects) {
|
|
if (rcObject.visibleInImgui && excludedLocations.count(rcObject.rc)) {
|
|
hasItems = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasItems) {
|
|
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
|
if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) {
|
|
for (auto [randomizerCheck, rcObject] : rcObjects) {
|
|
auto elfound = excludedLocations.find(rcObject.rc);
|
|
if (rcObject.visibleInImgui && elfound != excludedLocations.end()) {
|
|
if (ImGui::ArrowButton(std::to_string(rcObject.rc).c_str(), ImGuiDir_Left)) {
|
|
excludedLocations.erase(elfound);
|
|
// todo: this efficently when we build out cvar array support
|
|
std::string excludedLocationString = "";
|
|
for (auto excludedLocationIt : excludedLocations) {
|
|
excludedLocationString += std::to_string(excludedLocationIt);
|
|
excludedLocationString += ",";
|
|
}
|
|
CVar_SetString("gRandomizeExcludedLocations", excludedLocationString.c_str());
|
|
SohImGui::RequestCvarSaveOnNextTick();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Text(rcObject.rcShortName.c_str());
|
|
}
|
|
}
|
|
ImGui::TreePop();
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
} else {
|
|
locationsTabOpen = false;
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Tricks/Glitches")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (ImGui::BeginTable("tableRandoLogic", 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushItemWidth(170.0);
|
|
ImGui::Text("Logic Rules");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"Glitchless - No glitches are required, but may require some minor tricks.\n"
|
|
"\n"
|
|
"No logic - Item placement is completely random. MAY BE IMPOSSIBLE TO BEAT."
|
|
);
|
|
UIWidgets::EnhancementCombobox("gRandomizeLogicRules", randoLogicRules, 2, RO_GENERIC_OFF);
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
// Enable Glitch-Useful Cutscenes
|
|
UIWidgets::EnhancementCheckbox(Settings::EnableGlitchCutscenes.GetName().c_str(),
|
|
"gRandomizeEnableGlitchCutscenes");
|
|
UIWidgets::InsertHelpHoverText(
|
|
"The cutscenes of the Poes in Forest Temple and Darunia in Fire Temple will not be skipped. "
|
|
"These cutscenes are only useful for glitched gameplay and can be safely skipped otherwise.");
|
|
|
|
ImGui::PopItemWidth();
|
|
ImGui::EndTable();
|
|
}
|
|
if (ImGui::BeginTable("tableRandoTricksGlitches", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Disabled", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Enabled", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - DISABLED TRICKS AND GLITCHES
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildTricksGlitchesDisabled", ImVec2(0, -8));
|
|
|
|
ImGui::Text("Coming soon");
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - ENABLED TRICKS AND GLITCHES
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildTricksGlitchesEnabled", ImVec2(0, -8));
|
|
|
|
ImGui::Text("Coming soon");
|
|
|
|
ImGui::EndChild();
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Starting Inventory")) {
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
|
|
if (ImGui::BeginTable("tableRandoStartingInventory", 3,
|
|
ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
|
|
ImGui::TableSetupColumn("Starting Equipment", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Starting Items", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::TableSetupColumn("Starting Songs", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
ImGui::TableHeadersRow();
|
|
ImGui::PopItemFlag();
|
|
ImGui::TableNextRow();
|
|
|
|
// COLUMN 1 - STARTING EQUIPMENT
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildStartingEquipment", ImVec2(0, -8));
|
|
// Don't display this option if Dungeon Rewards are Shuffled to End of Dungeon.
|
|
// TODO: Show this but disabled when we have options for disabled Comboboxes.
|
|
if (CVar_GetS32("gRandomizeShuffleDungeonReward", RO_DUNGEON_REWARDS_END_OF_DUNGEON) != RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
|
|
ImGui::Text(Settings::LinksPocketItem.GetName().c_str());
|
|
UIWidgets::EnhancementCombobox("gRandomizeLinksPocket", randoLinksPocket, 4, RO_LINKS_POCKET_DUNGEON_REWARD);
|
|
UIWidgets::PaddedSeparator();
|
|
}
|
|
|
|
UIWidgets::EnhancementCheckbox(Settings::StartingKokiriSword.GetName().c_str(),
|
|
"gRandomizeStartingKokiriSword");
|
|
UIWidgets::PaddedSeparator();
|
|
UIWidgets::EnhancementCheckbox(Settings::StartingDekuShield.GetName().c_str(),
|
|
"gRandomizeStartingDekuShield");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 2 - STARTING ITEMS
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildStartingItems", ImVec2(0, -8));
|
|
|
|
UIWidgets::EnhancementCheckbox(Settings::StartingOcarina.GetName().c_str(), "gRandomizeStartingOcarina");
|
|
UIWidgets::PaddedSeparator();
|
|
UIWidgets::EnhancementCheckbox(Settings::StartingConsumables.GetName().c_str(),
|
|
"gRandomizeStartingConsumables");
|
|
UIWidgets::PaddedSeparator();
|
|
UIWidgets::EnhancementSliderInt("Gold Skulltula Tokens: %d", "##RandoStartingSkulltulaToken", "gRandomizeStartingSkulltulaToken", 0, 100, "", 0, true);
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::EndChild();
|
|
|
|
// COLUMN 3 - STARTING SONGS
|
|
ImGui::TableNextColumn();
|
|
window->DC.CurrLineTextBaseOffset = 0.0f;
|
|
ImGui::BeginChild("ChildStartingSongs", ImVec2(0, -8));
|
|
|
|
ImGui::Text("Coming soon");
|
|
|
|
UIWidgets::PaddedSeparator();
|
|
|
|
ImGui::EndChild();
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::PopStyleVar(1);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
ImGui::PopItemFlag();
|
|
ImGui::PopStyleVar();
|
|
ImGui::End();
|
|
}
|
|
|
|
CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u16 textId, bool mysterious) {
|
|
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, textId);
|
|
RandomizerCheck rc = GetCheckFromRandomizerInf(randomizerInf);
|
|
RandomizerGet shopItemGet = this->itemLocations[rc].rgID;
|
|
std::vector<std::string> shopItemName;
|
|
if (mysterious) {
|
|
shopItemName = {
|
|
"mysterious item",
|
|
"mysteriösen Gegenstand",
|
|
"objet mystérieux"
|
|
};
|
|
// TODO: This should eventually be replaced with a full fledged trick model & trick name system
|
|
} else if (shopItemGet == RG_ICE_TRAP) {
|
|
shopItemGet = this->itemLocations[rc].fakeRgID;
|
|
shopItemName = {
|
|
std::string(this->itemLocations[rc].trickName),
|
|
std::string(this->itemLocations[rc].trickName),
|
|
std::string(this->itemLocations[rc].trickName)
|
|
};
|
|
} else {
|
|
shopItemName = EnumToSpoilerfileGetName[shopItemGet];
|
|
}
|
|
u16 shopItemPrice = merchantPrices[rc];
|
|
|
|
if (textId == TEXT_SCRUB_RANDOM && shopItemPrice == 0) {
|
|
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE);
|
|
}
|
|
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{item}}", shopItemName[0], shopItemName[1], shopItemName[2]);
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{price}}", std::to_string(shopItemPrice));
|
|
return messageEntry;
|
|
}
|
|
|
|
std::vector<std::vector<const char*>> mapGetItemHints = {
|
|
{ " It's ordinary.", " It's masterful!" },
|
|
{ " It's ordinary.", " It's masterful!" }, // TODO: German Translation (when map items are also translated)
|
|
{ "&Elle vous semble %rordinaire%w.", "&Étrange... les mots %r\"Master&Quest\"%w sont gravés dessus." },
|
|
};
|
|
|
|
CustomMessageEntry Randomizer::GetMapGetItemMessageWithHint(GetItemEntry itemEntry) {
|
|
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, itemEntry.getItemId);
|
|
int sceneNum;
|
|
switch (itemEntry.getItemId) {
|
|
case RG_DEKU_TREE_MAP:
|
|
sceneNum = SCENE_YDAN;
|
|
break;
|
|
case RG_DODONGOS_CAVERN_MAP:
|
|
sceneNum = SCENE_DDAN;
|
|
break;
|
|
case RG_JABU_JABUS_BELLY_MAP:
|
|
sceneNum = SCENE_BDAN;
|
|
break;
|
|
case RG_FOREST_TEMPLE_MAP:
|
|
sceneNum = SCENE_BMORI1;
|
|
break;
|
|
case RG_FIRE_TEMPLE_MAP:
|
|
sceneNum = SCENE_HIDAN;
|
|
break;
|
|
case RG_WATER_TEMPLE_MAP:
|
|
sceneNum = SCENE_MIZUSIN;
|
|
break;
|
|
case RG_SPIRIT_TEMPLE_MAP:
|
|
sceneNum = SCENE_JYASINZOU;
|
|
break;
|
|
case RG_SHADOW_TEMPLE_MAP:
|
|
sceneNum = SCENE_HAKADAN;
|
|
break;
|
|
case RG_BOTTOM_OF_THE_WELL_MAP:
|
|
sceneNum = SCENE_HAKADANCH;
|
|
break;
|
|
case RG_ICE_CAVERN_MAP:
|
|
sceneNum = SCENE_ICE_DOUKUTO;
|
|
break;
|
|
}
|
|
|
|
if (this->masterQuestDungeons.empty() || this->masterQuestDungeons.size() >= 12) {
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", "");
|
|
} else if (ResourceMgr_IsSceneMasterQuest(sceneNum)) {
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", mapGetItemHints[0][1], mapGetItemHints[1][1], mapGetItemHints[2][1]);
|
|
} else {
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{typeHint}}", mapGetItemHints[0][0], mapGetItemHints[1][0], mapGetItemHints[2][0]);
|
|
}
|
|
|
|
return messageEntry;
|
|
}
|
|
|
|
void CreateGetItemMessages(std::vector<GetItemMessage> 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 CreateRupeeMessages() {
|
|
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
|
|
customMessageManager->AddCustomMessageTable(Randomizer::rupeeMessageTableID);
|
|
const std::vector<u8> rupees = { TEXT_BLUE_RUPEE, TEXT_RED_RUPEE, TEXT_PURPLE_RUPEE, TEXT_HUGE_RUPEE };
|
|
std::string rupeeText = " ";
|
|
for (u8 rupee : rupees) {
|
|
switch (rupee) {
|
|
case TEXT_BLUE_RUPEE:
|
|
rupeeText = "\x05\x03 5 {{rupee}}\x05\x00";
|
|
break;
|
|
case TEXT_RED_RUPEE:
|
|
rupeeText = "\x05\x01 20 {{rupee}}\x05\x00";
|
|
break;
|
|
case TEXT_PURPLE_RUPEE:
|
|
rupeeText = "\x05\x05 50 {{rupee}}\x05\x00";
|
|
break;
|
|
case TEXT_HUGE_RUPEE:
|
|
rupeeText = "\x05\x06 200 {{rupee}}\x05\x00";
|
|
break;
|
|
}
|
|
customMessageManager->CreateMessage(Randomizer::rupeeMessageTableID, rupee,
|
|
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
|
|
"You found" + rupeeText + " !",
|
|
"Du hast" + rupeeText + " gefunden!",
|
|
"Vous obtenez" + rupeeText + " !"
|
|
});
|
|
}
|
|
}
|
|
|
|
CustomMessageEntry Randomizer::GetRupeeMessage(u16 rupeeTextId) {
|
|
CustomMessageEntry messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::rupeeMessageTableID, rupeeTextId);
|
|
std::string englishName = RandomElement(englishRupeeNames);
|
|
std::string germanName = RandomElement(germanRupeeNames);
|
|
std::string frenchName = RandomElement(frenchRupeeNames);
|
|
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{rupee}}", englishName, germanName, frenchName);
|
|
return messageEntry;
|
|
}
|
|
|
|
CustomMessageMinimal NaviMessages[NUM_NAVI_MESSAGES] = {
|
|
|
|
{ "%cMissing a small key in a dungeon?&Maybe the %rboss %chas it!",
|
|
"%cFehlt dir ein kleiner Schlüssel in &einem Labyrinth? Vielleicht hat ihn&ja der %rEndgegner%c!",
|
|
"%cIl te manque une %wPetite Clé %cdans&un donjon? C'est peut-être le %rboss&%cqui l'a!" },
|
|
|
|
{ "%cSometimes you can use the %rMegaton&Hammer %cinstead of bombs!",
|
|
"%cManchmal kannst du den %rStahlhammer&%cstatt Bomben verwenden!",
|
|
"%cParfois, tu peux utiliser la %rMasse&des Titans %cau lieu de tes bombes!" },
|
|
|
|
{ "%cThere are three %gbusiness scrubs %cin &Hyrule who sell %wmysterious items%c. Do&you know where they are?",
|
|
"%cEs gibt drei %gDeku-Händler %cin Hyrule&die mysteriöse Gegenstände&verkaufen. Weißt du wo Sie sind?",
|
|
"%cIl y a trois %gPestes Marchandes%c en&Hyrule qui vendent des %wobjets&mystérieux%c. Tu sais où elles sont?" },
|
|
|
|
{ "%cStuck on this seed? You could &throw in the towel and check the&%wspoiler log%c...",
|
|
"%cHängst du bei diesem Seed fest?&Du könntest die Flinte ins Korn&werfen und ins %wSpoiler Log %cschauen...",
|
|
"%cSi tu es coincé sur cette seed,&tu peux toujours jeter l'éponge&et regader le %wSpoiler log%c..." },
|
|
|
|
{ "%cDid you know that the %yHover&Boots %ccan be used to cross&%wquicksand%c?",
|
|
"%cWußtest du, daß du mit den&%yGleitstiefeln %cTreibsand %wüberqueren&kannst%c?",
|
|
"%cEst-ce que tu savais que les %rBottes&des airs %cpouvaient être utilisées&pour traverser les %wsables mouvants%c?" },
|
|
|
|
{ "%cYou can reach the back of %wImpa's&House %cby jumping from the&unfinished house with a %rcucco%c!",
|
|
"%cDu kannst den Balkon von %wImpas&Haus %cerreichen indem du von&der Baustelle aus mit einem %rHuhn&%cspringst!",
|
|
"%cTu peux atteindre l'arrière de la&%wMaison d'Impa %cen sautant depuis la&maison en construction avec une&%rcocotte%c!" },
|
|
|
|
{ "%cThe %ySpirit Trial %cin %pGanon's Castle&%chas a %whidden chest %csomewhere.&Did you already know that?",
|
|
"%cDie %yGeister-Prüfung %cin %pGanons&Schloß %chat irgendwo eine&%wversteckte Kiste%c. Weißt du schon&wo?",
|
|
"%cL'%yÉpreuve de l'Esprit%c dans le %pChâteau&de Ganon %ca un coffre caché quelque&part. Je suppose que tu le savais&déjà?" },
|
|
|
|
{ "%cYou know the %wBombchu Bowling&Alley%c? I heard %wonly two of the &prizes %care worthwhile. The rest &is junk!",
|
|
"%cKennst du die %wMinenbowlingbahn%c?&Ich habe gehört daß sich nur &%wzwei der Preise%c lohnen. Der Rest&ist Krimskrams!",
|
|
"%cEst-ce que tu savais qu'au %wBowling&Teigneux%c, il n'y a que les %wdeux&premiers prix %cqui sont intéréssant?" },
|
|
|
|
{ "%cHave you been using %wDeku Nuts&%cenough? I've seen them blow up&a %rBombchu%c!",
|
|
"%cBenutzt du auch genügend %wDeku&Nüsse%c? Ich habe mal gesehen daß&man damit %rKrabbelminen %cdetonieren&kann!",
|
|
"%cTu es sûr d'utiliser tes %wNoix Mojo &%ccorrectement? J'en ai déjà vu&exploser des %rChoux-Péteurs%c!" },
|
|
|
|
{ "%cYou might end up with an %wextra&key %cfor the %bWater Temple %cor the&%rFire Temple%c. It's for your safety!",
|
|
"%cVielleicht verbleibt dir im&%bWassertempel %coder %rFeuertempel %cein&%wzusätzlicher Schlüssel%c. Dies&ist zu deiner Sicherheit!",
|
|
"%cIl se peut que tu aies une %wPetite&Clé %cen trop dans le %bTemple de l'Eau&%cou le %rTemple du Feu%c. C'est pour ta&propre sécurité!" },
|
|
|
|
{ "%cIf you can't pick up a %rbomb&flower %cwith your hands, you can&still detonate it with %rfire %cor&with %warrows%c!",
|
|
"%cNur weil du eine %rDonnerblume&%cnicht hochheben kannst, so kannst&du sie immernoch mit %rFeuer %coder&%wPfeilen %cdetonieren!",
|
|
"%cSi tu ne peux pas ramasser&un %rChoux-Péteur %cavec tes mains, tu&peux toujours le faire exploser&avec du %rFeu %cou avec des %wflèches%c!" },
|
|
|
|
{ "%cEven an adult can't push large&blocks without some %wextra&strength%c!",
|
|
"%cSelbst ein Erwachsener kann ohne&etwas %wzusätzliche Kraft %ckeine&großen Blöcke verschieben!",
|
|
"%cMême un adulte ne peut pas pousser&de grands blocs sans un petit %wgain&de force%c!" },
|
|
|
|
{ "%cI've heard that %rFlare Dancer&%cis weak to the %wMaster Sword%c!&Have you tried it?",
|
|
"%cIch habe gehört daß der&%rFlammenderwisch %ceine Schwäche für&das %wMasterschwert %caufweist. Hast du&es schonmal versucht einzusetzen?",
|
|
"%cJ'ai entendu dire que les %rDanse-&Flammes %csont faîbles face à l'%wÉpée de&Légende%c! Est-ce que tu as essayé?" },
|
|
|
|
{ "%cDon't have a weapon to kill a&%rspider%c? Try throwing a %wpot&%cat it!",
|
|
"%cFehlt dir die Waffe um gegen&eine %rSkulltula %czu kämpfen? Versuch&Sie mit einem %wKrug %cabzuwerfen!",
|
|
"%cSi tu n'as pas d'arme pour tuer&une %raraignée%c, pourquoi n'essayerais&-tu pas de lui jetter une %wjarre&%cà la figure?" },
|
|
|
|
{ "%cI hear the patch of %wsoft soil&%cin %bZora's River %cis the only one&that isn't home to a %rspider%c!",
|
|
"%cIch habe gehört daß die Stelle&%wfeuchten Bodens %cim %bZora-Fluß %cals&einzige keine %rSkulltula %cbeherbergt.",
|
|
"%cJ'ai entendu dire que la %wterre meuble&%cqui se trouve à la %bRivière Zora %cest&la seule qui ne contienne pas&d'%raraignée%c." },
|
|
|
|
{ "%cThe people of Hyrule sometimes&have %witems %cfor you, but they won't&like it if you're %wwearing a mask%c!",
|
|
"%cDie Bewohner Hyrules haben manchmal&%wGegenstände %cfür dich, aber Sie mögen&es nicht wenn du %wMasken trägst%c!",
|
|
"%cIl se peut que les habitants d'Hyrule&aient des %wobjets %cpour toi. Par contre,&ils risquent de ne pas trop apprécier&le fait que tu %wportes un masque%c!" },
|
|
|
|
{ "%cIf you get trapped somewhere, you&might have to %wsave your game %cand&%wreset%c!",
|
|
"%cSolltest du irgendwo eingeschloßen&sein, mußt du vielleicht dein %wSpiel&speichern %cund %wneustarten%c!",
|
|
"%cSi tu es coincé quelque part, tu&devrais %wsauvegarder ta partie %cet&faire un %wreset%c!" },
|
|
|
|
{ "%cSheik will meet you in a %rburning&village %conce you have %gForest%c,&%rFire%c, and %bWater %cMedallions!",
|
|
"%cSheik wird dich in einem %rbrennenden&Dorf %ctreffen sobald du das Amulett&des %gWaldes%c, %rFeuers %cund %bWassers&%cbesitzt.",
|
|
"%cSheik t'attendra dans un %rvillage&en feu %clorsque tu auras récupéré&les médaillons de la %gForêt%c, du %rFeu&%cet de l'%bEau%c!" },
|
|
|
|
{ "%cIf you don't have a %wsword %cas a&child, try buying %wDeku Sticks%c!&They're effective against your foes!",
|
|
"%cSolltest du als Kind kein %wSchwert&%cbesitzen, empfehle ich %wDeku Stäbe&%czu kaufen! Diese sind effektiv gegen&Widersacher!",
|
|
"%cSi tu n'as pas d'%wépée %cen tant&qu'enfant, pourquoi n'irais-tu pas&acheter quelques %wBâtons Mojo%c? Ils&sont efficaces contre tes ennemis!" }
|
|
};
|
|
|
|
void CreateNaviRandoMessages() {
|
|
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
|
|
customMessageManager->AddCustomMessageTable(Randomizer::NaviRandoMessageTableID);
|
|
for (u8 i = 0; i <= (NUM_NAVI_MESSAGES - 1); i++) {
|
|
customMessageManager->CreateMessage(Randomizer::NaviRandoMessageTableID, i,
|
|
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, NaviMessages[i].english,
|
|
NaviMessages[i].german, NaviMessages[i].french });
|
|
}
|
|
}
|
|
|
|
CustomMessageMinimal IceTrapMessages[NUM_ICE_TRAP_MESSAGES] = {
|
|
{ "You are a %bFOOL%w!",
|
|
"Du bist ein %bDUMMKOPF%w!",
|
|
"%bPauvre fou%w..." },
|
|
|
|
{ "You are a %bFOWL%w!",
|
|
"Du bist eine %bFrostbeule%w!",
|
|
"Tu es un %bglaçon%w, Harry!" },
|
|
|
|
{ "%bFOOL%w!",
|
|
"%bDUMMKOPF%w!",
|
|
"%bSot%w que tu es." },
|
|
|
|
{ "You just got %bPUNKED%w!",
|
|
"Du wurdest %beiskalt%w erwischt!",
|
|
"Ça me %bglace%w le sang!" },
|
|
|
|
{ "Stay %bfrosty%w, @.",
|
|
"Es läuft dir %beiskalt%w den Rücken&hinunter, @.",
|
|
"%bReste au frais%w, @." },
|
|
|
|
{ "Take a %bchill pill%w, @.",
|
|
"Bleib %bcool%w, @.",
|
|
"Et c'est la douche %bfroide%w!" },
|
|
|
|
{ "%bWinter%w is coming.",
|
|
"Der %bWinter%w naht.",
|
|
"L'%bhiver%w vient." },
|
|
|
|
{ "%bICE%w to see you, @.",
|
|
"Alles %bcool%w im Pool?",
|
|
"%bGlacier%w!" },
|
|
|
|
{ "Feeling a little %rhot%w under the collar?&%bLet's fix that%w.",
|
|
"%bAbkühlung gefällig%w?",
|
|
"%Ça en jette un %bfroid%w." },
|
|
|
|
{ "It's a %bcold day%w in the Evil Realm.",
|
|
"Es ist ein %kalter%w Tag im Herzen&von Hyrule.",
|
|
"Est-ce que tu as déjà eu des sueurs&%bfroides%w?" },
|
|
|
|
{ "Getting %bcold feet%w?",
|
|
"Bekommst du etwa %bkalte%w Füße?",
|
|
"La vengeance est un plat qui se mange&%bfroid%w!" },
|
|
|
|
{ "Say hello to the %bZoras%w for me!",
|
|
"Sag den %bZoras%w viele Grüße von mir!",
|
|
"Dit bonjour aux %bZoras%w pour moi!" },
|
|
|
|
{ "Can you keep a %bcool head%w?",
|
|
"Bewahre einen %bkühlen%w! Kopf.",
|
|
"Il faut parfois savoir garder la tête&%bfroide%w!" },
|
|
|
|
{ "Ganondorf used %bIce Trap%w!&It's super effective!",
|
|
"Ganondorf setzt %bEisstrahl%w ein.&Das ist sehr effektiv!",
|
|
"Ganondorf utilise %bPiège de Glace%w!&C'est super efficace!" },
|
|
|
|
{ "Allow me to break the %bice%w!",
|
|
"Ein Lächeln ist der beste Weg,&um das %bEis%w zu brechen!",
|
|
"Laisse moi briser la %bglace%w!" },
|
|
|
|
{ "%bCold pun%w.",
|
|
"%bEiskalt%w lässt du meine Seele&erfrier'n.",
|
|
"Balance man...,&Cadence man...,&Trace la %bglace%w...,&c'est le Cooooolllll Rasta!" },
|
|
|
|
{ "The %bTitanic%w would be scared of you,&@.",
|
|
"Die %bTitanic%w hätte Angst vor dir,&@.",
|
|
"Le %bTitanic%w aurait peur de toi,&@." },
|
|
|
|
{ "Oh no!",
|
|
"Oh nein!",
|
|
"Oh non!" },
|
|
|
|
{ "What killed the dinosaurs?&The %bICE%w age!",
|
|
"Was die Dinosaurier getötet hat?&Die %bEiszeit%w!",
|
|
"Qu'est-ce qui a tué les dinosaures?&L'ère %bglacière%w!" },
|
|
|
|
{ "Knock knock. Who's there? Ice. Ice&who? Ice see that you're a %bFOOL%w.",
|
|
"Nachts ist es %bkälter%w als draußen.",
|
|
"L'imbécile réfléchit uniquement quand il&s'observe dans la %bglace%w." },
|
|
|
|
{ "Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
|
|
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w.",
|
|
"Never gonna %bgive you up%w. Never&gonna %blet you down%w. Never gonna&run around and %bhurt you%w." },
|
|
|
|
{ "Thank you %b@%w!&But your item is in another castle!",
|
|
"Danke %b@%w!&Aber der Gegenstand ist in&einem anderem Schloss!",
|
|
"Merci %b@%w!&Mais ton objet est dans un autre&château!" },
|
|
|
|
{ "%bFREEZE%w! Don't move!",
|
|
" Kalt. Kalt. Kälter. %bEISKALT%w!",
|
|
"J'espère que ça ne te fait ni chaud, ni&%bfroid%w." },
|
|
|
|
};
|
|
|
|
void CreateIceTrapRandoMessages() {
|
|
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
|
|
customMessageManager->AddCustomMessageTable(Randomizer::IceTrapRandoMessageTableID);
|
|
for (u8 i = 0; i <= (NUM_ICE_TRAP_MESSAGES - 1); i++) {
|
|
customMessageManager->CreateMessage(Randomizer::IceTrapRandoMessageTableID, i,
|
|
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, IceTrapMessages[i].english,
|
|
IceTrapMessages[i].german, IceTrapMessages[i].french });
|
|
}
|
|
}
|
|
|
|
void Randomizer::CreateCustomMessages() {
|
|
// RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED
|
|
// with GIMESSAGE(getItemID, itemID, english, german, french).
|
|
const std::vector<GetItemMessage> getItemMessages = {
|
|
GIMESSAGE_NO_GERMAN(
|
|
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!"),
|
|
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!",
|
|
"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,
|
|
"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!"),
|
|
GIMESSAGE_NO_GERMAN(
|
|
RG_BOTTLE_WITH_FISH, ITEM_FISH,
|
|
"You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!",
|
|
"Vous obtenez une %rBouteille avec&un Poisson%w! Il a l'air délicieux!&Il paraîtrait que %bJabu-Jabu "
|
|
"%wen&serait friand!"),
|
|
GIMESSAGE_NO_GERMAN(
|
|
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&des Insectes%w! Ils adorent creuser&dans la terre meuble!"),
|
|
GIMESSAGE_NO_GERMAN(RG_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!",
|
|
"Vous obtenez une %rBouteille avec&une Fée%w! Faites-en bon usage!"),
|
|
GIMESSAGE_NO_GERMAN(
|
|
RG_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
|
|
"You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%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_GREEN_POTION, ITEM_POTION_GREEN,
|
|
"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_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!"),
|
|
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_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!"),
|
|
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 %pPuits%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 %rChâteau de Ganon%w!"),
|
|
|
|
GIMESSAGE_NO_GERMAN(RG_GERUDO_FORTRESS_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %yThieves Hideout &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %yRepaire des Voleurs%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %gForest Temple &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %gTemple de la Forêt%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %rFire Temple &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %rTemple du Feu%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %bWater Temple &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %bTemple de l'Eau%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %ySpirit Temple &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %yTemple de l'Esprit%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %pShadow Temple &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %pTemple de l'Ombre%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %pBottom of the &Well %wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %pPuits%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_GERUDO_TRAINING_GROUNDS_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %yGerudo Training &Grounds %wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %yGymnase Gerudo%w!"),
|
|
GIMESSAGE_NO_GERMAN(RG_GANONS_CASTLE_KEY_RING, ITEM_KEY_SMALL,
|
|
"You found a %rGanon's Castle &%wKeyring!",
|
|
"Vous obtenez un trousseau de&clés du %rChâ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_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!"),
|
|
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_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!"),
|
|
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_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!"),
|
|
|
|
GIMESSAGE_NO_GERMAN(RG_DEKU_TREE_MAP, ITEM_DUNGEON_MAP, "You found the %gDeku Tree &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wde&l'%gArbre Mojo%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_DODONGOS_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %rDodongo's Cavern &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wde la&%rCaverne Dodongo%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_JABU_JABUS_BELLY_MAP, ITEM_DUNGEON_MAP, "You found the %bJabu Jabu's Belly &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%bVentre de Jabu-Jabu%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_FOREST_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %gForest Temple &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%gTemple de la Forêt%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_FIRE_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %rFire Temple &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%rTemple du Feu%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_WATER_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %bWater Temple &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%bTemple de l'Eau%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_SPIRIT_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %ySpirit Temple &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%yTemple de l'Esprit%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_SHADOW_TEMPLE_MAP, ITEM_DUNGEON_MAP, "You found the %pShadow Temple &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%pTemple de l'Ombre%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_BOTTOM_OF_THE_WELL_MAP, ITEM_DUNGEON_MAP, "You found the %pBottom of the &Well %wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wdu &%pPuits%w!{{typeHint}}"),
|
|
GIMESSAGE_NO_GERMAN(RG_ICE_CAVERN_MAP, ITEM_DUNGEON_MAP, "You found the %cIce Cavern &%wMap!{{typeHint}}",
|
|
"Vous obtenez la %rCarte %wde &la %cCaverne Polaire%w!{{typeHint}}"),
|
|
|
|
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 obtenez un %rPaquet de&Haricots Magiques%w! Trouvez&un endroit approprié pour un&jardin et plantez-les.^Attendez ensuite que quelque&chose d'amusant se produise!"),
|
|
GIMESSAGE_NO_GERMAN(RG_TYCOON_WALLET, ITEM_WALLET_GIANT,
|
|
"You got a %rTycoon's Wallet%w!&It's gigantic! Now you can carry&up to %y999 rupees%w!",
|
|
"Vous obtenez la %rBourse de Magnat%w!&Elle peut contenir jusqu'à %y999 rubis%w!&C'est gigantesque!")
|
|
};
|
|
CreateGetItemMessages(getItemMessages);
|
|
CreateRupeeMessages();
|
|
CreateNaviRandoMessages();
|
|
CreateIceTrapRandoMessages();
|
|
}
|
|
|
|
class ExtendedVanillaTableInvalidItemIdException: public std::exception {
|
|
private:
|
|
s16 itemID;
|
|
|
|
public:
|
|
ExtendedVanillaTableInvalidItemIdException(s16 itemID): itemID(itemID) {}
|
|
std::string what() {
|
|
return itemID + " is not a valid ItemID for the extendedVanillaGetItemTable. If you are adding a new"
|
|
"item, try adding it to randoGetItemTable instead.";
|
|
}
|
|
};
|
|
|
|
void InitRandoItemTable() {
|
|
// These entries have ItemIDs from vanilla, but not GetItemIDs or entries in the old sGetItemTable
|
|
GetItemEntry extendedVanillaGetItemTable[] = {
|
|
GET_ITEM(ITEM_MEDALLION_LIGHT, OBJECT_GI_MEDAL, GID_MEDALLION_LIGHT, 0x40, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_LIGHT_MEDALLION),
|
|
GET_ITEM(ITEM_MEDALLION_FOREST, OBJECT_GI_MEDAL, GID_MEDALLION_FOREST, 0x3E, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_FOREST_MEDALLION),
|
|
GET_ITEM(ITEM_MEDALLION_FIRE, OBJECT_GI_MEDAL, GID_MEDALLION_FIRE, 0x3C, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_FIRE_MEDALLION),
|
|
GET_ITEM(ITEM_MEDALLION_WATER, OBJECT_GI_MEDAL, GID_MEDALLION_WATER, 0x3D, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_WATER_MEDALLION),
|
|
GET_ITEM(ITEM_MEDALLION_SHADOW, OBJECT_GI_MEDAL, GID_MEDALLION_SHADOW, 0x41, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SHADOW_MEDALLION),
|
|
GET_ITEM(ITEM_MEDALLION_SPIRIT, OBJECT_GI_MEDAL, GID_MEDALLION_SPIRIT, 0x3F, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SPIRIT_MEDALLION),
|
|
|
|
GET_ITEM(ITEM_KOKIRI_EMERALD, OBJECT_GI_JEWEL, GID_KOKIRI_EMERALD, 0x80, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_KOKIRI_EMERALD),
|
|
GET_ITEM(ITEM_GORON_RUBY, OBJECT_GI_JEWEL, GID_GORON_RUBY, 0x81, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_GORON_RUBY),
|
|
GET_ITEM(ITEM_ZORA_SAPPHIRE, OBJECT_GI_JEWEL, GID_ZORA_SAPPHIRE, 0x82, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_ZORA_SAPPHIRE),
|
|
|
|
GET_ITEM(ITEM_SONG_LULLABY, OBJECT_GI_MELODY, GID_SONG_ZELDA, 0xD4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_ZELDAS_LULLABY),
|
|
GET_ITEM(ITEM_SONG_SUN, OBJECT_GI_MELODY, GID_SONG_SUN, 0xD3, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SUNS_SONG),
|
|
GET_ITEM(ITEM_SONG_EPONA, OBJECT_GI_MELODY, GID_SONG_EPONA, 0xD2, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_EPONAS_SONG),
|
|
GET_ITEM(ITEM_SONG_STORMS, OBJECT_GI_MELODY, GID_SONG_STORM, 0xD6, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SONG_OF_STORMS),
|
|
GET_ITEM(ITEM_SONG_TIME, OBJECT_GI_MELODY, GID_SONG_TIME, 0xD5, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SONG_OF_TIME),
|
|
GET_ITEM(ITEM_SONG_SARIA, OBJECT_GI_MELODY, GID_SONG_SARIA, 0xD1, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SARIAS_SONG),
|
|
|
|
GET_ITEM(ITEM_SONG_MINUET, OBJECT_GI_MELODY, GID_SONG_MINUET, 0x73, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_MINUET_OF_FOREST),
|
|
GET_ITEM(ITEM_SONG_BOLERO, OBJECT_GI_MELODY, GID_SONG_BOLERO, 0x74, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_BOLERO_OF_FIRE),
|
|
GET_ITEM(ITEM_SONG_SERENADE, OBJECT_GI_MELODY, GID_SONG_SERENADE, 0x75, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_SERENADE_OF_WATER),
|
|
GET_ITEM(ITEM_SONG_NOCTURNE, OBJECT_GI_MELODY, GID_SONG_NOCTURNE, 0x77, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_NOCTURNE_OF_SHADOW),
|
|
GET_ITEM(ITEM_SONG_REQUIEM, OBJECT_GI_MELODY, GID_SONG_REQUIEM, 0x76, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_REQUIEM_OF_SPIRIT),
|
|
GET_ITEM(ITEM_SONG_PRELUDE, OBJECT_GI_MELODY, GID_SONG_PRELUDE, 0x78, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_NONE, RG_PRELUDE_OF_LIGHT),
|
|
};
|
|
|
|
// These do not have ItemIDs or GetItemIDs from vanilla, so I'm using their
|
|
// RandomizerGet enum values for both.
|
|
GetItemEntry randoGetItemTable[] = {
|
|
GET_ITEM(RG_ICE_TRAP, OBJECT_GI_RUPY, GID_RUPEE_GOLD, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_ICE_TRAP),
|
|
GET_ITEM(RG_MAGIC_SINGLE, OBJECT_GI_MAGICPOT, GID_MAGIC_SMALL, 0xE4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_MAGIC_SINGLE),
|
|
GET_ITEM(RG_MAGIC_DOUBLE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_MAGIC_DOUBLE),
|
|
GET_ITEM(RG_DOUBLE_DEFENSE, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xE9, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_DOUBLE_DEFENSE),
|
|
GET_ITEM(RG_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_RED_POTION),
|
|
GET_ITEM(RG_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_GREEN_POTION),
|
|
GET_ITEM(RG_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_BLUE_POTION),
|
|
GET_ITEM(RG_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_FAIRY),
|
|
GET_ITEM(RG_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_FISH),
|
|
GET_ITEM(RG_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_BLUE_FIRE),
|
|
GET_ITEM(RG_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_BUGS),
|
|
GET_ITEM(RG_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_POE),
|
|
GET_ITEM(RG_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_BOTTLE_WITH_BIG_POE),
|
|
GET_ITEM(RG_GERUDO_FORTRESS_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GERUDO_FORTRESS_SMALL_KEY),
|
|
GET_ITEM(RG_FOREST_TEMPLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_FOREST_TEMPLE_SMALL_KEY),
|
|
GET_ITEM(RG_FIRE_TEMPLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_FIRE_TEMPLE_SMALL_KEY),
|
|
GET_ITEM(RG_WATER_TEMPLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_WATER_TEMPLE_SMALL_KEY),
|
|
GET_ITEM(RG_SPIRIT_TEMPLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_SPIRIT_TEMPLE_SMALL_KEY),
|
|
GET_ITEM(RG_SHADOW_TEMPLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_SMALL_KEY),
|
|
GET_ITEM(RG_BOTTOM_OF_THE_WELL_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_SMALL_KEY),
|
|
GET_ITEM(RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GERUDO_TRAINING_GROUNDS_SMALL_KEY),
|
|
GET_ITEM(RG_GANONS_CASTLE_SMALL_KEY, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GANONS_CASTLE_SMALL_KEY),
|
|
GET_ITEM(RG_GERUDO_FORTRESS_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GERUDO_FORTRESS_KEY_RING),
|
|
GET_ITEM(RG_FOREST_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_FOREST_TEMPLE_KEY_RING),
|
|
GET_ITEM(RG_FIRE_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_FIRE_TEMPLE_KEY_RING),
|
|
GET_ITEM(RG_WATER_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_WATER_TEMPLE_KEY_RING),
|
|
GET_ITEM(RG_SPIRIT_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_SPIRIT_TEMPLE_KEY_RING),
|
|
GET_ITEM(RG_SHADOW_TEMPLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_KEY_RING),
|
|
GET_ITEM(RG_BOTTOM_OF_THE_WELL_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_KEY_RING),
|
|
GET_ITEM(RG_GERUDO_TRAINING_GROUNDS_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GERUDO_TRAINING_GROUNDS_KEY_RING),
|
|
GET_ITEM(RG_GANONS_CASTLE_KEY_RING, OBJECT_GI_KEY, GID_KEY_SMALL, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_RANDOMIZER, RG_GANONS_CASTLE_KEY_RING),
|
|
GET_ITEM(RG_FOREST_TEMPLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_FOREST_TEMPLE_BOSS_KEY),
|
|
GET_ITEM(RG_FIRE_TEMPLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_FIRE_TEMPLE_BOSS_KEY),
|
|
GET_ITEM(RG_WATER_TEMPLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_WATER_TEMPLE_BOSS_KEY),
|
|
GET_ITEM(RG_SPIRIT_TEMPLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_SPIRIT_TEMPLE_BOSS_KEY),
|
|
GET_ITEM(RG_SHADOW_TEMPLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_BOSS_KEY),
|
|
GET_ITEM(RG_GANONS_CASTLE_BOSS_KEY, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_BOSS_KEY, MOD_RANDOMIZER, RG_GANONS_CASTLE_BOSS_KEY),
|
|
GET_ITEM(RG_DEKU_TREE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_DEKU_TREE_MAP),
|
|
GET_ITEM(RG_DODONGOS_CAVERN_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_DODONGOS_CAVERN_MAP),
|
|
GET_ITEM(RG_JABU_JABUS_BELLY_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_JABU_JABUS_BELLY_MAP),
|
|
GET_ITEM(RG_FOREST_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_FOREST_TEMPLE_MAP),
|
|
GET_ITEM(RG_FIRE_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_FIRE_TEMPLE_MAP),
|
|
GET_ITEM(RG_WATER_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_WATER_TEMPLE_MAP),
|
|
GET_ITEM(RG_SPIRIT_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_SPIRIT_TEMPLE_MAP),
|
|
GET_ITEM(RG_SHADOW_TEMPLE_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_SHADOW_TEMPLE_MAP),
|
|
GET_ITEM(RG_BOTTOM_OF_THE_WELL_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_BOTTOM_OF_THE_WELL_MAP),
|
|
GET_ITEM(RG_ICE_CAVERN_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_ICE_CAVERN_MAP),
|
|
GET_ITEM(RG_DEKU_TREE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_DEKU_TREE_COMPASS),
|
|
GET_ITEM(RG_DODONGOS_CAVERN_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_DODONGOS_CAVERN_COMPASS),
|
|
GET_ITEM(RG_JABU_JABUS_BELLY_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_JABU_JABUS_BELLY_COMPASS),
|
|
GET_ITEM(RG_FOREST_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_FOREST_TEMPLE_COMPASS),
|
|
GET_ITEM(RG_FIRE_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_FIRE_TEMPLE_COMPASS),
|
|
GET_ITEM(RG_WATER_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_WATER_TEMPLE_COMPASS),
|
|
GET_ITEM(RG_SPIRIT_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_SPIRIT_TEMPLE_COMPASS),
|
|
GET_ITEM(RG_SHADOW_TEMPLE_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, 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, ITEM_CATEGORY_LESSER, 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, ITEM_CATEGORY_LESSER, 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, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER, RG_MAGIC_BEAN_PACK),
|
|
GET_ITEM(RG_TYCOON_WALLET, OBJECT_GI_PURSE, GID_WALLET_GIANT, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER, RG_TYCOON_WALLET),
|
|
};
|
|
ItemTableManager::Instance->AddItemTable(MOD_RANDOMIZER);
|
|
for (int i = 0; i < ARRAY_COUNT(extendedVanillaGetItemTable); i++) {
|
|
ItemTableManager::Instance->AddItemEntry(MOD_RANDOMIZER, extendedVanillaGetItemTable[i].getItemId, extendedVanillaGetItemTable[i]);
|
|
}
|
|
for (int i = 0; i < ARRAY_COUNT(randoGetItemTable); i++) {
|
|
if (randoGetItemTable[i].itemId >= RG_FOREST_TEMPLE_SMALL_KEY && randoGetItemTable[i].itemId <= RG_GANONS_CASTLE_SMALL_KEY
|
|
&& randoGetItemTable[i].itemId != RG_GERUDO_FORTRESS_SMALL_KEY) {
|
|
randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawSmallKey;
|
|
} else if (randoGetItemTable[i].itemId >= RG_FOREST_TEMPLE_KEY_RING && randoGetItemTable[i].itemId <= RG_GANONS_CASTLE_KEY_RING) {
|
|
randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawKeyRing;
|
|
} else if (randoGetItemTable[i].itemId >= RG_FOREST_TEMPLE_BOSS_KEY && randoGetItemTable[i].itemId <= RG_GANONS_CASTLE_BOSS_KEY) {
|
|
randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawBossKey;
|
|
} else if (randoGetItemTable[i].itemId == RG_DOUBLE_DEFENSE) {
|
|
randoGetItemTable[i].drawFunc = (CustomDrawFunc)Randomizer_DrawDoubleDefense;
|
|
}
|
|
ItemTableManager::Instance->AddItemEntry(MOD_RANDOMIZER, randoGetItemTable[i].itemId, randoGetItemTable[i]);
|
|
}
|
|
}
|
|
|
|
|
|
void InitRando() {
|
|
SohImGui::AddWindow("Randomizer", "Randomizer Settings", DrawRandoEditor);
|
|
Randomizer::CreateCustomMessages();
|
|
InitRandoItemTable();
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
void Rando_Init(void) {
|
|
InitRando();
|
|
}
|
|
|
|
}
|
|
|