Race Integrity QoL (#3445)

* Add gDisableChangingSettings

* Add support for dropping a config file to overwrite CVars
This commit is contained in:
Garrett Cox 2023-11-28 22:42:37 -06:00 committed by GitHub
parent c4f34624ba
commit fdcd9a7508
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 221 additions and 34 deletions

View File

@ -312,6 +312,7 @@ namespace GameControlEditor {
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
"certain items.");
UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false);
DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above");
if (CVarGetInteger("gEnableWalkModify", 0)) {
@ -323,6 +324,7 @@ namespace GameControlEditor {
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
window->EndGroupPanelPublic(0);
}
ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");

View File

@ -1485,6 +1485,7 @@ void Draw_Placements(){
}
void DrawSillyTab() {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (CVarGetInteger("gLetItSnow", 0)) {
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
@ -1569,6 +1570,7 @@ void DrawSillyTab() {
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
ImGui::EndDisabled();
}
// Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this

View File

@ -1,3 +1,6 @@
#ifndef _ENHANCEMENT_TYPES_H_
#define _ENHANCEMENT_TYPES_H_
typedef enum {
WARP_MODE_OVERRIDE_OFF,
WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
@ -74,3 +77,5 @@ typedef enum {
DEKU_STICK_UNBREAKABLE,
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
} DekuStickType;
#endif

View File

@ -217,6 +217,31 @@ const std::vector<const char*> enhancementsCvars = {
"gFixTexturesOOB",
"gIvanCoopModeEnabled",
"gEnemySpawnsOverWaterboxes",
"gTreeStickDrops",
"gShadowTag",
"gRandomizedEnemySizes",
"gRandomizedEnemies",
"gMirroredWorldMode",
"gMirroredWorld",
"gHyperEnemies",
"gHookshotableReticle",
"gHideBunnyHood",
"gFixVineFall",
"gFileSelectMoreInfo",
"gEnemyHealthBar",
"gBushDropFix",
"gAllDogsRichard",
"gAddTraps.enabled",
"gAddTraps.Ammo",
"gAddTraps.Bomb",
"gAddTraps.Burn",
"gAddTraps.Ice",
"gAddTraps.Kill",
"gAddTraps.Knock",
"gAddTraps.Shock",
"gAddTraps.Speed",
"gAddTraps.Tele",
"gAddTraps.Void",
};
const std::vector<const char*> cheatCvars = {
@ -269,7 +294,23 @@ const std::vector<const char*> cheatCvars = {
"gNoRedeadFreeze",
"gBombTimerMultiplier",
"gNoFishDespawn",
"gNoBugsDespawn"
"gNoBugsDespawn",
"gWalkModifierDoesntChangeJump",
"gStatsEnabled",
"gSaveStatesEnabled",
"gSaveStatePromise",
"gRegEditEnabled",
"gPreset0",
"gPreset1",
"gDekuStickCheat",
"gDebugWarpScreenTranslation",
"gDebugSaveFileMode",
"gCosmetics.Link_BodyScale.Changed",
"gCosmetics.Link_BodyScale.Value",
"gCosmetics.Link_HeadScale.Changed",
"gCosmetics.Link_HeadScale.Value",
"gCosmetics.Link_SwordScale.Changed",
"gCosmetics.Link_SwordScale.Value",
};
const std::vector<const char*> randomizerCvars = {
@ -399,6 +440,15 @@ const std::vector<const char*> randomizerCvars = {
"gRandomizeGregHint",
"gRandoManualSeedEntry",
"gRandomizerSettingsEnabled",
"gRandomizeTriforceHuntTotalPieces",
"gRandomizeTriforceHuntRequiredPieces",
"gRandomizeTriforceHunt",
"gRandomizeShuffleMasterSword",
"gRandomizeSariaHint",
"gRandomizeRupeeNames",
"gRandomizeFrogsHint",
"gRandoRelevantNavi",
"gRandoQuestItemFanfares",
};
const std::vector<PresetEntry> vanillaPlusPresetEntries = {

View File

@ -1,5 +1,6 @@
#include "playthrough.hpp"
#include <libultraship/libultraship.h>
#include <boost_custom/container_hash/hash_32.hpp>
#include "custom_messages.hpp"
#include "fill.hpp"
@ -8,6 +9,7 @@
#include "random.hpp"
#include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "variables.h"
namespace Playthrough {
@ -39,6 +41,10 @@ int Playthrough_Init(uint32_t seed, std::unordered_map<RandomizerSettingKey, uin
}
}
if (CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
settingsStr += (char*)gBuildVersion;
}
uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr);
Random_Init(finalHash);
Settings::hash = std::to_string(finalHash);

View File

@ -360,37 +360,35 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE },
};
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) {
try {
if (strcmp(spoilerFileName, "") != 0) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return false;
} else {
return true;
}
json spoilerFileJson;
spoilerFileStream >> spoilerFileJson;
if (!spoilerFileJson.contains("version") || !spoilerFileJson.contains("finalSeed")) {
return false;
}
return true;
}
return false;
} catch (std::exception& e) {
SPDLOG_ERROR("Error checking if spoiler file exists: {}", e.what());
return false;
} catch (...) {
SPDLOG_ERROR("Error checking if spoiler file exists");
return false;
}
}
#pragma GCC pop_options
#pragma optimize("", on)
@ -659,7 +657,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
}
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@ -1293,7 +1291,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
}
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@ -1394,7 +1392,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
}
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@ -1415,7 +1413,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
}
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@ -1495,7 +1493,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
}
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream)
return;
@ -1558,7 +1556,7 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
}
void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) {
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
if (!spoilerFileStream) {
return;
}
@ -3144,7 +3142,9 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
}
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
ImGui::EndDisabled();
UIWidgets::Spacer(0);
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
@ -3167,13 +3167,17 @@ void RandomizerSettingsWindow::DrawElement() {
}
UIWidgets::Spacer(0);
ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
if (ImGui::Button("Generate Randomizer")) {
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
}
ImGui::EndDisabled();
UIWidgets::Spacer(0);
if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
}
// RANDOTODO settings presets
// std::string presetfilepath = CVarGetString("gLoadedPreset", "");
@ -3181,6 +3185,8 @@ void RandomizerSettingsWindow::DrawElement() {
UIWidgets::PaddedSeparator();
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
ImGuiWindow* window = ImGui::GetCurrentWindow();
static ImVec2 cellPadding(8.0f, 8.0f);
@ -5226,6 +5232,8 @@ void RandomizerSettingsWindow::DrawElement() {
ImGui::EndTabBar();
}
ImGui::EndDisabled();
if (disableEditingRandoSettings) {
UIWidgets::ReEnableComponent("");
}

View File

@ -49,6 +49,9 @@
#include "Fonts.h"
#include <Utils/StringHelper.h>
#include "Enhancements/custom-message/CustomMessageManager.h"
#include "Enhancements/presets.h"
#include "util.h"
#include <boost_custom/container_hash/hash_32.hpp>
#if not defined (__SWITCH__) && not defined(__WIIU__)
#include "Extractor/Extract.h"
@ -2573,3 +2576,71 @@ extern "C" void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex) {
extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement) {
gfx_register_blended_texture(name, mask, replacement);
}
// #region SOH [TODO] Ideally this should move to being event based, it's currently run every frame on the file select screen
extern "C" void SoH_ProcessDroppedFiles() {
const char* droppedFile = CVarGetString("gDroppedFile", "");
if (CVarGetInteger("gNewFileDropped", 0) && strcmp(droppedFile, "") != 0) {
try {
std::ifstream configStream(SohUtils::Sanitize(droppedFile));
if (!configStream) {
return;
}
nlohmann::json configJson;
configStream >> configJson;
if (!configJson.contains("CVars")) {
return;
}
clearCvars(enhancementsCvars);
clearCvars(cheatCvars);
clearCvars(randomizerCvars);
// Flatten everything under CVars into a single array
auto cvars = configJson["CVars"].flatten();
for (auto& [key, value] : cvars.items()) {
// Replace slashes with dots in key, and remove leading dot
std::string path = key;
std::replace(path.begin(), path.end(), '/', '.');
if (path[0] == '.') {
path.erase(0, 1);
}
if (value.is_string()) {
CVarSetString(path.c_str(), value.get<std::string>().c_str());
} else if (value.is_number_integer()) {
CVarSetInteger(path.c_str(), value.get<int>());
} else if (value.is_number_float()) {
CVarSetFloat(path.c_str(), value.get<float>());
}
}
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGuiWindow("Console")->Hide();
gui->GetGuiWindow("Actor Viewer")->Hide();
gui->GetGuiWindow("Collision Viewer")->Hide();
gui->GetGuiWindow("Save Editor")->Hide();
gui->GetGuiWindow("Display List Viewer")->Hide();
gui->GetGuiWindow("Stats")->Hide();
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
gui->SaveConsoleVariablesOnNextTick();
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
} catch (std::exception& e) {
SPDLOG_ERROR("Failed to load config file: {}", e.what());
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
} catch (...) {
SPDLOG_ERROR("Failed to load config file");
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
return;
}
}
}
// #endregion

View File

@ -175,6 +175,7 @@ void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void SaveManager_ThreadPoolWait();
void CheckTracker_OnMessageClose();
void SoH_ProcessDroppedFiles();
int32_t GetGIID(uint32_t itemID);
#endif

View File

@ -492,6 +492,8 @@ extern std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
void DrawEnhancementsMenu() {
if (ImGui::BeginMenu("Enhancements"))
{
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS);
UIWidgets::PaddedSeparator();
@ -1199,6 +1201,8 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f);
ImGui::EndDisabled();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@ -1240,6 +1244,8 @@ void DrawEnhancementsMenu() {
void DrawCheatsMenu() {
if (ImGui::BeginMenu("Cheats"))
{
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
if (ImGui::BeginMenu("Infinite...")) {
UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney");
UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false);
@ -1394,6 +1400,8 @@ void DrawCheatsMenu() {
}
UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps.");
ImGui::EndDisabled();
ImGui::EndMenu();
}
}
@ -1407,6 +1415,8 @@ extern std::shared_ptr<DLViewerWindow> mDLViewerWindow;
void DrawDeveloperToolsMenu() {
if (ImGui::BeginMenu("Developer Tools")) {
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled");
UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen");
if (CVarGetInteger("gDebugEnabled", 0)) {
@ -1479,6 +1489,8 @@ void DrawDeveloperToolsMenu() {
ImGui::PopStyleVar(3);
ImGui::PopStyleColor(1);
ImGui::EndDisabled();
ImGui::EndMenu();
}
}

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <vector>
#include <algorithm>
std::vector<std::string> sceneNames = {
"Inside the Deku Tree",
@ -318,3 +319,20 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
strncpy(destination, source.c_str(), size - 1);
destination[size - 1] = '\0';
}
std::string SohUtils::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;
}

View File

@ -12,4 +12,6 @@ namespace SohUtils {
// Copies a string and ensures the destination is null terminated if the source string is larger than size
// Only up to size-1 characters are copied from the source string
void CopyStringToCharArray(char* destination, std::string source, size_t size);
std::string Sanitize(std::string stringValue);
} // namespace SohUtils

View File

@ -1,6 +1,7 @@
#include "file_choose.h"
#include <string.h>
#include <stdio.h>
#include "textures/title_static/title_static.h"
#include "textures/parameter_static/parameter_static.h"
@ -1024,7 +1025,7 @@ void FileChoose_UpdateRandomizer() {
return;
}
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) {
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && !CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
CVarSetString("gSpoilerLog", "");
fileSelectSpoilerFileLoaded = false;
}
@ -1051,6 +1052,10 @@ void FileChoose_UpdateRandomizer() {
Randomizer_LoadMasterQuestDungeons(fileLoc);
Randomizer_LoadEntranceOverrides(fileLoc, silent);
fileSelectSpoilerFileLoaded = true;
if (SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
remove(fileLoc);
}
}
}
@ -1071,6 +1076,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
Input* input = &this->state.input[0];
bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) {
@ -1261,6 +1267,7 @@ void FileChoose_UpdateQuestMenu(GameState* thisx) {
s8 i = 0;
bool dpad = CVarGetInteger("gDpadText", 0);
SoH_ProcessDroppedFiles();
FileChoose_UpdateRandomizer();
if (ABS(this->stickRelX) > 30 || (dpad && CHECK_BTN_ANY(input->press.button, BTN_DLEFT | BTN_DRIGHT))) {
@ -3686,4 +3693,7 @@ void FileChoose_Init(GameState* thisx) {
Font_LoadOrderedFont(&this->font);
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1);
// Originally this was only set when transitioning from the title screen, but gSkipLogoTitle skips that process so we're ensuring it's set here
gSaveContext.gameMode = GAMEMODE_FILE_SELECT;
}