Merge pull request #1331 from leggettc18/ganon-trials

Reimplements selecting different amounts of Ganon's Trials.
This commit is contained in:
briaguya 2022-08-28 11:48:34 -04:00 committed by GitHub
commit 6d4288c2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 164 additions and 75 deletions

View File

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

View File

@ -2517,18 +2517,16 @@ namespace Settings {
BridgeRewardCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_REWARD_COUNT]);
BridgeDungeonCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_DUNGEON_COUNT]);
BridgeTokenCount.SetSelectedIndex(cvarSettings[RSK_RAINBOW_BRIDGE_TOKEN_COUNT]);
RandomGanonsTrials.SetSelectedIndex(cvarSettings[RSK_RANDOM_TRIALS]);
// RANDTODO: Switch this back once Ganon's Trials Count is properly implemented.
//GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
switch (cvarSettings[RSK_TRIAL_COUNT]) {
case 0:
GanonsTrialsCount.SetSelectedIndex(6);
break;
case 1:
GanonsTrialsCount.SetSelectedIndex(0);
break;
if (cvarSettings[RSK_RANDOM_TRIALS] == 2) {
RandomGanonsTrials.SetSelectedIndex(1);
} else {
RandomGanonsTrials.SetSelectedIndex(0);
}
if (cvarSettings[RSK_RANDOM_TRIALS] == 0) {
GanonsTrialsCount.SetSelectedIndex(0);
} else {
GanonsTrialsCount.SetSelectedIndex(cvarSettings[RSK_TRIAL_COUNT]);
}
ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]);
ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]);
Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]);

View File

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

View File

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

View File

@ -107,6 +107,21 @@ Randomizer::~Randomizer() {
this->randomizerMerchantPrices.clear();
}
std::unordered_map<std::string, RandomizerInf> spoilerFileTrialToEnum = {
{ "the Forest Trial", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "l'épreuve de la Forêt", RAND_INF_TRIALS_DONE_FOREST_TRIAL },
{ "the Fire Trial", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "l'épreuve du Feu", RAND_INF_TRIALS_DONE_FIRE_TRIAL },
{ "the Water Trial", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "l'épreuve de l'Eau", RAND_INF_TRIALS_DONE_WATER_TRIAL },
{ "the Spirit Trial", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "l'épreuve de l'Esprit", RAND_INF_TRIALS_DONE_SPIRIT_TRIAL },
{ "the Shadow Trial", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "l'épreuve de l'Ombre", RAND_INF_TRIALS_DONE_SHADOW_TRIAL },
{ "the Light Trial", RAND_INF_TRIALS_DONE_LIGHT_TRIAL },
{ "l'épreuve de la Lumière", RAND_INF_TRIALS_DONE_LIGHT_TRIAL }
};
std::unordered_map<s16, s16> getItemIdToItemId = {
{ GI_BOW, ITEM_BOW },
{ GI_ARROW_FIRE, ITEM_ARROW_FIRE },
@ -673,6 +688,12 @@ void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
itemLocations[RC_UNKNOWN_CHECK] = RG_NONE;
}
void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
if (strcmp(spoilerFileName, "") != 0) {
ParseRequiredTrialsFile(spoilerFileName);
}
}
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream)
@ -1091,6 +1112,25 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
}
}
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
try {
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
json trialsJson = spoilerFileJson["requiredTrials"];
for (auto it = trialsJson.begin(); it != trialsJson.end(); it++) {
this->trialsRequired[spoilerFileTrialToEnum[it.value()]] = true;
}
} catch (const std::exception& e) {
return;
}
}
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
if (!spoilerFileStream)
@ -1141,6 +1181,10 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
}
}
bool Randomizer::IsTrialRequired(RandomizerInf trial) {
return this->trialsRequired.contains(trial);
}
s16 Randomizer::GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) {
s16 itemId = GetItemFromActor(actorId, actorParams, sceneNum, ogId);
return itemId;
@ -2981,7 +3025,7 @@ void DrawRandoEditor(bool& open) {
const char* randoGerudoFortress[3] = { "Normal", "Fast", "Open" };
const char* randoRainbowBridge[7] = { "Vanilla", "Always open", "Stones", "Medallions",
"Dungeon rewards", "Dungeons", "Tokens" };
const char* randoGanonsTrial[2] = { "Off", "On" };
const char* randoGanonsTrial[3] = { "Skip", "Set Number", "Random Number" };
// World Settings
const char* randoStartingAge[3] = { "Child", "Adult", "Random" };
@ -3355,21 +3399,18 @@ void DrawRandoEditor(bool& open) {
PaddedSeparator();
// Random Ganon's Trials
/*
ImGui::Text("Random Ganon's Trials");
InsertHelpHoverText("Sets a random number or required trials to enter\nGanon's Tower.");
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 2, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 0) {
ImGui::PopItemWidth();
ImGui::Text("Ganon's Trials");
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.");
SohImGui::EnhancementCombobox("gRandomizeGanonTrial", randoGanonsTrial, 3, 0);
if (CVar_GetS32("gRandomizeGanonTrial", 0) == 1) {
SohImGui::EnhancementSliderInt("Ganon's Trial Count: %d", "##RandoTrialCount",
"gRandomizeGanonTrialCount", 0, 6, "", 6);
"gRandomizeGanonTrialCount", 1, 6, "", 6);
InsertHelpHoverText("Set the number of trials required to enter Ganon's Tower.");
RANDTODO: Switch back to slider when pre-completing some of Ganon's Trials is properly implemnted.
}
*/
SohImGui::EnhancementCheckbox("Skip Ganon's Trials", "gRandomizeGanonTrialCount");
InsertHelpHoverText(
"Sets whether or not Ganon's Castle Trials are required to enter Ganon's Tower.");
}
// COLUMN 2 - Shuffle Settings

View File

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

View File

@ -1562,10 +1562,18 @@ extern "C" void Randomizer_LoadHintLocations(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadHintLocations(spoilerFileName);
}
extern "C" void Randomizer_LoadRequiredTrials(const char* spoilerFileName) {
OTRGlobals::Instance->gRandomizer->LoadRequiredTrials(spoilerFileName);
}
extern "C" void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent) {
OTRGlobals::Instance->gRandomizer->LoadItemLocations(spoilerFileName, silent);
}
extern "C" bool Randomizer_IsTrialRequired(RandomizerInf trial) {
return OTRGlobals::Instance->gRandomizer->IsTrialRequired(trial);
}
extern "C" bool SpoilerFileExists(const char* spoilerFileName) {
return OTRGlobals::Instance->gRandomizer->SpoilerFileExists(spoilerFileName);
}

View File

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

View File

@ -7,7 +7,6 @@
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
#define NUM_DUNGEONS 8
#define NUM_TRIALS 6
#define NUM_COWS 10
#define NUM_SCRUBS 35
@ -762,6 +761,15 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.randomizerInf[i] = 0;
}
// Set all trials to cleared if trial count is random or anything other than 6
if (Randomizer_GetSettingValue(RSK_RANDOM_TRIALS) || (Randomizer_GetSettingValue(RSK_TRIAL_COUNT) != 6)) {
for (u16 i = RAND_INF_TRIALS_DONE_LIGHT_TRIAL; i <= RAND_INF_TRIALS_DONE_SHADOW_TRIAL; i++) {
if (!Randomizer_IsTrialRequired(i)) {
Flags_SetRandomizerInf(i);
}
}
}
// Set Cutscene flags to skip them
gSaveContext.eventChkInf[0xC] |= 0x10; // returned to tot with medallions
gSaveContext.eventChkInf[0xC] |= 0x20; //sheik at tot pedestal

View File

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

View File

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

View File

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