Merge branch 'develop' into aManchipelago

This commit is contained in:
aMannus
2025-10-20 20:48:41 +02:00
8 changed files with 253 additions and 119 deletions

View File

@@ -1,17 +1,32 @@
#include "mod_menu.h"
#include <ship/utils/StringHelper.h>
#include <libultraship/classes.h>
#include "soh/SohGui/SohGui.hpp"
#include "soh/OTRGlobals.h"
#include "soh/resource/type/Skeleton.h"
#include <map>
#include <ranges>
#include <vector>
#include <libultraship/classes.h>
#include <ship/utils/StringHelper.h>
#include "mod_menu.h"
#include "soh/OTRGlobals.h"
#include "soh/resource/type/Skeleton.h"
#include "soh/SohGui/MenuTypes.h"
#include "soh/SohGui/SohMenu.h"
#include "soh/SohGui/SohGui.hpp"
std::vector<std::string> enabledModFiles;
std::vector<std::string> disabledModFiles;
std::vector<std::string> unsupportedFiles;
std::map<std::string, std::filesystem::path> filePaths;
static int dragSourceIndex = -1;
static int dragTargetIndex = -1;
#define CVAR_ENABLED_MODS_NAME CVAR_GENERAL("EnabledMods")
namespace SohGui {
extern std::shared_ptr<SohMenu> mSohMenu;
}
static WidgetInfo enableModsWidget;
static WidgetInfo tabHotkeyWidget;
#define CVAR_ENABLED_MODS_NAME CVAR_SETTING("EnabledMods")
#define CVAR_ENABLED_MODS_DEFAULT ""
#define CVAR_ENABLED_MODS_VALUE CVarGetString(CVAR_ENABLED_MODS_NAME, CVAR_ENABLED_MODS_DEFAULT)
@@ -42,6 +57,42 @@ void SetEnabledModsCVarValue() {
CVarSetString(CVAR_ENABLED_MODS_NAME, s.c_str());
}
void AfterModChange() {
// disabled mods are always sorted
std::sort(disabledModFiles.begin(), disabledModFiles.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); });
});
}
void ModsPostDragAndDrop() {
if (dragTargetIndex != -1) {
std::string file = enabledModFiles[dragSourceIndex];
enabledModFiles.erase(enabledModFiles.begin() + dragSourceIndex);
enabledModFiles.insert(enabledModFiles.begin() + dragTargetIndex, file);
dragTargetIndex = dragSourceIndex = -1;
AfterModChange();
}
}
void ModsHandleDragAndDrop(std::vector<std::string>& objectList, int targetIndex, const std::string& itemName,
ImGuiDragDropFlags flags = ImGuiDragDropFlags_SourceAllowNullID) {
if (ImGui::BeginDragDropSource(flags)) {
ImGui::SetDragDropPayload("DragMove", &targetIndex, sizeof(uint32_t));
ImGui::Text("Move %s", itemName.c_str());
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DragMove")) {
IM_ASSERT(payload->DataSize == sizeof(uint32_t));
dragSourceIndex = *(const int*)payload->Data;
dragTargetIndex = targetIndex;
}
ImGui::EndDragDropTarget();
}
}
std::vector<std::string> GetEnabledModsFromCVar() {
std::string enabledModsCVarValue = CVAR_ENABLED_MODS_VALUE;
return StringHelper::Split(enabledModsCVarValue, SEPARATOR);
@@ -55,58 +106,71 @@ std::shared_ptr<Ship::ArchiveManager> GetArchiveManager() {
return Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager();
}
void UpdateModFiles(bool init = false) {
if (init) {
bool IsValidExtension(std::string extension) {
if (
#ifdef INCLUDE_MPQ_SUPPORT
// .mpq doesn't make sense to support because all tools to make such mods output OTR
StringHelper::IEquals(extension, ".otr") /*|| StringHelper::IEquals(extension, ".mpq")*/ ||
#endif
// .zip needs to be excluded because mods are most often distributed in zip archives
// and thus could contain .otr/o2r files
StringHelper::IEquals(extension, ".o2r") /*|| StringHelper::IEquals(extension, ".zip")*/) {
return true;
}
return false;
}
void UpdateModFiles(bool init = false, bool reset = false) {
if (init || reset) {
enabledModFiles.clear();
enabledModFiles = GetEnabledModsFromCVar();
}
disabledModFiles.clear();
std::vector<std::string> enabledMods = GetEnabledModsFromCVar();
unsupportedFiles.clear();
filePaths.clear();
std::string modsPath = Ship::Context::LocateFileAcrossAppDirs("mods", appShortName);
bool changed = false;
if (modsPath.length() > 0 && std::filesystem::exists(modsPath)) {
std::vector<std::filesystem::path> enabledFiles;
if (std::filesystem::is_directory(modsPath)) {
for (const std::filesystem::directory_entry& p : std::filesystem::recursive_directory_iterator(
modsPath, std::filesystem::directory_options::follow_directory_symlink)) {
std::string extension = p.path().extension().string();
if (
#ifndef EXCLUDE_MPQ_SUPPORT
StringHelper::IEquals(extension, ".otr") || StringHelper::IEquals(extension, ".mpq") ||
#endif
StringHelper::IEquals(extension, ".o2r") || StringHelper::IEquals(extension, ".zip")) {
std::string path = p.path().generic_string();
bool shouldBeEnabled = std::find(enabledMods.begin(), enabledMods.end(), path) != enabledMods.end();
if (shouldBeEnabled) {
if (init) {
enabledModFiles.push_back(path);
GetArchiveManager()->AddArchive(path);
}
if (p.is_directory()) {
continue;
}
std::string filename =
p.path().filename().generic_string().substr(0, p.path().filename().generic_string().rfind("."));
std::string extension = p.path().extension().generic_string();
if (!IsValidExtension(extension)) {
continue;
}
bool enabled =
std::find(enabledModFiles.begin(), enabledModFiles.end(), filename) != enabledModFiles.end();
if (!enabled) {
enabledModFiles.push_back(filename);
changed = true;
}
filePaths.emplace(filename, p.path());
}
if (init) {
std::vector<std::string> enabledTemp(enabledModFiles);
for (std::string mod : enabledTemp) {
if (filePaths.contains(mod)) {
GetArchiveManager()->AddArchive(filePaths.at(mod).generic_string());
} else {
disabledModFiles.push_back(path);
enabledModFiles.erase(std::find(enabledModFiles.begin(), enabledModFiles.end(), mod));
}
}
}
}
}
if (changed) {
SetEnabledModsCVarValue();
}
}
extern "C" void gfx_texture_cache_clear();
void AfterModChange() {
SetEnabledModsCVarValue();
// TODO: runtime changes
/*
gfx_texture_cache_clear();
SOH::SkeletonPatcher::ClearSkeletons();
*/
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
// disabled mods are always sorted
std::sort(disabledModFiles.begin(), disabledModFiles.end(), [](const std::string& a, const std::string& b) {
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); });
});
}
void EnableMod(std::string file) {
disabledModFiles.erase(std::find(disabledModFiles.begin(), disabledModFiles.end(), file));
enabledModFiles.insert(enabledModFiles.begin(), file);
@@ -139,39 +203,30 @@ void DrawMods(bool enabled) {
bool madeAnyChange = false;
int switchFromIndex = -1;
int switchToIndex = -1;
uint32_t index = 0;
for (int i = 0; i < selectedModFiles.size(); i += 1) {
for (int i = selectedModFiles.size() - 1; i >= 0; i--) {
std::string file = selectedModFiles[i];
if (UIWidgets::StateButton((file + "_left_right").c_str(), enabled ? ICON_FA_ARROW_LEFT : ICON_FA_ARROW_RIGHT,
ImVec2(25, 25), UIWidgets::ButtonOptions().Color(THEME_COLOR))) {
if (enabled) {
DisableMod(file);
} else {
EnableMod(file);
}
if (enabled) {
ImGui::BeginGroup();
}
// if (UIWidgets::StateButton((file + "_left_right").c_str(), enabled ? ICON_FA_ARROW_RIGHT :
// ICON_FA_ARROW_LEFT,
// ImVec2(25, 25), UIWidgets::ButtonOptions().Color(THEME_COLOR))) {
// if (enabled) {
// DisableMod(file);
// } else {
// EnableMod(file);
// }
// }
// it's not relevant to reorder disabled mods
if (enabled) {
ImGui::SameLine();
if (i == 0) {
ImGui::BeginDisabled();
}
if (UIWidgets::StateButton((file + "_up").c_str(), ICON_FA_ARROW_UP, ImVec2(25, 25),
UIWidgets::ButtonOptions().Color(THEME_COLOR))) {
madeAnyChange = true;
switchFromIndex = i;
switchToIndex = i - 1;
}
if (i == 0) {
ImGui::EndDisabled();
}
ImGui::SameLine();
// ImGui::SameLine();
if (i == selectedModFiles.size() - 1) {
ImGui::BeginDisabled();
}
if (UIWidgets::StateButton((file + "_down").c_str(), ICON_FA_ARROW_DOWN, ImVec2(25, 25),
if (UIWidgets::StateButton((file + "_up").c_str(), ICON_FA_ARROW_UP, ImVec2(25, 25),
UIWidgets::ButtonOptions().Color(THEME_COLOR))) {
madeAnyChange = true;
switchFromIndex = i;
@@ -180,9 +235,31 @@ void DrawMods(bool enabled) {
if (i == selectedModFiles.size() - 1) {
ImGui::EndDisabled();
}
ImGui::SameLine();
if (i == 0) {
ImGui::BeginDisabled();
}
if (UIWidgets::StateButton((file + "_down").c_str(), ICON_FA_ARROW_DOWN, ImVec2(25, 25),
UIWidgets::ButtonOptions().Color(THEME_COLOR))) {
madeAnyChange = true;
switchFromIndex = i;
switchToIndex = i - 1;
}
if (i == 0) {
ImGui::EndDisabled();
}
}
DrawModInfo(file);
DrawModInfo(filePaths.at(file).filename().generic_string());
if (enabled) {
ImGui::EndGroup();
ModsHandleDragAndDrop(selectedModFiles, i, file);
}
}
if (enabled) {
ModsPostDragAndDrop();
}
if (madeAnyChange) {
@@ -191,22 +268,61 @@ void DrawMods(bool enabled) {
}
}
void ModMenuWindow::DrawElement() {
ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0));
bool editing = false;
const ImVec4 yellow = ImVec4(1, 1, 0, 1);
void ModMenuWindow::DrawElement() {
SohGui::mSohMenu->MenuDrawItem(enableModsWidget, 200, THEME_COLOR);
ImGui::SameLine();
SohGui::mSohMenu->MenuDrawItem(tabHotkeyWidget, 200, THEME_COLOR);
ImGui::TextColored(
yellow, "Mods are currently not reloaded at runtime.\nClose and re-open Ship for the changes to take effect.");
UIWidgets::ColorValues.at(UIWidgets::Colors::Yellow),
"Mods are currently not reloaded at runtime. Close and re-open Ship for the changes to take effect.\n"
"Drag ordering for the enabled list is available.\nMod priority is top to bottom. They override mods listed "
"below them.");
if (UIWidgets::Button("Update", UIWidgets::ButtonOptions().Size(ImVec2(250.0f, 0.0f)).Color(THEME_COLOR))) {
UpdateModFiles();
// if (UIWidgets::Button(
// "Update", UIWidgets::ButtonOptions({ { .disabled = editing, .disabledTooltip = "Currently editing..." }
// })
// .Size(UIWidgets::Sizes::Inline)
// .Color(THEME_COLOR))) {
// UpdateModFiles();
// }
// ImGui::SameLine();
if (UIWidgets::Button("Edit",
UIWidgets::ButtonOptions({ { .disabled = editing, .disabledTooltip = "Already editing..." } })
.Size(UIWidgets::Sizes::Inline)
.Color(THEME_COLOR))) {
editing = true;
}
UIWidgets::Tooltip("Re-check the mods folder for new files");
if (editing) {
ImGui::SameLine();
if (UIWidgets::Button("Cancel", UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline))) {
editing = false;
UpdateModFiles(false, true);
}
ImGui::SameLine();
if (UIWidgets::Button("Apply & Close",
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
SohGui::RegisterPopup("Apply & Close",
"Application currently requires a restart. Save the mod info and close SoH?", "Close",
"Cancel", [&]() {
// TODO: runtime changes
SetEnabledModsCVarValue();
// TODO: runtime changes
/*
gfx_texture_cache_clear();
SOH::SkeletonPatcher::ClearSkeletons();
*/
Ship::Context::GetInstance()->GetConsoleVariables()->Save();
Ship::Context::GetInstance()->GetWindow()->Close();
});
}
}
ImGui::BeginDisabled(!editing);
if (ImGui::BeginTable("tableMods", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
ImGui::TableSetupColumn("Disabled Mods", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableSetupColumn("Enabled Mods", ImGuiTableColumnFlags_WidthStretch, 200.0f);
// ImGui::TableSetupColumn("Disabled Mods", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::TableHeadersRow();
ImGui::PopItemFlag();
@@ -214,26 +330,52 @@ void ModMenuWindow::DrawElement() {
ImGui::TableNextColumn();
if (ImGui::BeginChild("Disabled Mods", ImVec2(0, -8))) {
DrawMods(false);
ImGui::EndChild();
}
ImGui::TableNextColumn();
if (ImGui::BeginChild("Enabled Mods", ImVec2(0, -8))) {
DrawMods(true);
ImGui::EndChild();
}
/*ImGui::TableNextColumn();
if (ImGui::BeginChild("Disabled Mods", ImVec2(0, -8))) {
DrawMods(false);
ImGui::EndChild();
}*/
ImGui::EndTable();
}
ImGui::EndDisabled();
}
void ModMenuWindow::InitElement() {
UpdateModFiles(true);
}
}
void RegisterModMenuWidgets() {
enableModsWidget = { .name = "Enable Mods", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
enableModsWidget.CVar(CVAR_SETTING("AltAssets"))
.RaceDisable(false)
.Options(UIWidgets::CheckboxOptions({ { .disabledTooltip = "Temporarily disabled while editing mods list." } })
.Color(THEME_COLOR)
.Tooltip("Toggle mods. For graphics mods, this means toggling between default and mod graphics.")
.DefaultValue(true))
.PreFunc([&](WidgetInfo& info) {
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(info.options);
options->disabled = editing;
});
SohGui::mSohMenu->AddSearchWidget({ enableModsWidget, "Settings", "Mod Menu", "Top", "alternat assets" });
tabHotkeyWidget = { .name = "Mods Tab Hotkey", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
tabHotkeyWidget.CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey"))
.RaceDisable(false)
.Options(UIWidgets::CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Allows pressing the Tab key to toggle mods")
.DefaultValue(true));
SohGui::mSohMenu->AddSearchWidget(
{ enableModsWidget, "Settings", "Mod Menu", "Top", "alternat assets tab hotkey" });
}
static RegisterMenuInitFunc menuInitFunc(RegisterModMenuWidgets);

View File

@@ -227,9 +227,10 @@ uint8_t StonesRequiredBySettings() {
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
stones = std::max<uint8_t>({ stones, ctx->GetOption(RSK_LACS_STONE_COUNT).Get() });
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_STONES)) {
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Get() - 6) });
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS)) {
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
stones = std::max<uint8_t>({ stones, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get() - 6) });
}
return stones;
@@ -247,12 +248,12 @@ uint8_t MedallionsRequiredBySettings() {
medallions = ctx->GetOption(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT).Get() - 3;
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_MEDALLIONS)) {
medallions = std::max({ medallions, ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Get() });
medallions = std::max(medallions, ctx->GetOption(RSK_LACS_MEDALLION_COUNT).Get());
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_REWARDS)) {
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Get() - 3) });
medallions = std::max(medallions, (uint8_t)(ctx->GetOption(RSK_LACS_REWARD_COUNT).Get() - 3));
} else if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_DUNGEONS) &&
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
medallions = std::max({ medallions, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get() - 3) });
medallions = std::max(medallions, (uint8_t)(ctx->GetOption(RSK_LACS_DUNGEON_COUNT).Get() - 3));
}
return medallions;
}
@@ -264,7 +265,7 @@ uint8_t TokensRequiredBySettings() {
tokens = ctx->GetOption(RSK_RAINBOW_BRIDGE_TOKEN_COUNT).Get();
}
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_LACS_TOKENS)) {
tokens = std::max<uint8_t>({ tokens, ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get() });
tokens = std::max<uint8_t>(tokens, ctx->GetOption(RSK_LACS_TOKEN_COUNT).Get());
}
return tokens;
}

View File

@@ -255,11 +255,14 @@ void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) {
return;
if (flagType == FLAG_GS_TOKEN &&
Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_TOKENS).Is(RO_TOKENSANITY_OFF))
Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_TOKENS).Is(RO_TOKENSANITY_OFF)) {
Rando::Context::GetInstance()->GetItemLocation(rc)->SetCheckStatus(RCSHOW_COLLECTED);
return;
}
auto loc = Rando::Context::GetInstance()->GetItemLocation(rc);
if (rc != RC_HF_OCARINA_OF_TIME_ITEM) {
if (loc == nullptr || loc->HasObtained() || loc->GetPlacedRandomizerGet() == RG_NONE)
Rando::Context::GetInstance()->GetItemLocation(rc)->SetCheckStatus(RCSHOW_COLLECTED);
return;
}
SPDLOG_INFO("Queuing RC: {}", static_cast<uint32_t>(rc));

View File

@@ -2116,9 +2116,8 @@ void RegisterItemTrackerWidgets() {
hookshotIdentWidget.CVar(CVAR_SETTING("FreeLook.Enabled"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Shows an 'H' or an 'L' to more easiely distinguish between Hookshot and Longshot."));
SohGui::mSohMenu->AddSearchWidget(
{ hookshotIdentWidget, "Settings", "Controls", "Camera Controls", "longshot icon" });
.Tooltip("Shows an 'H' or an 'L' to more easily distinguish between Hookshot and Longshot."));
SohGui::mSohMenu->AddSearchWidget({ hookshotIdentWidget, "Randomizer", "Item Tracker", "General Settings" });
}
static RegisterMenuInitFunc menuInitFunc(RegisterItemTrackerWidgets);

View File

@@ -300,7 +300,7 @@ void OTRGlobals::Initialize() {
// tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
context->InitResourceManager(OTRFiles, {}, 3);
prevAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0);
prevAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 1);
context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets);
auto controlDeck = std::make_shared<LUS::ControlDeck>(std::vector<CONTROLLERBUTTONS_T>({
@@ -1483,7 +1483,7 @@ extern "C" void Graph_StartFrame() {
#endif
case KbScancode::LUS_KB_TAB: {
if (CVarGetInteger(CVAR_SETTING("Mods.AlternateAssetsHotkey"), 1)) {
CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 0));
CVarSetInteger(CVAR_SETTING("AltAssets"), !CVarGetInteger(CVAR_SETTING("AltAssets"), 1));
}
break;
}
@@ -1571,7 +1571,7 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
}
}
bool curAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 0);
bool curAltAssets = CVarGetInteger(CVAR_SETTING("AltAssets"), 1);
if (prevAltAssets != curAltAssets) {
prevAltAssets = curAltAssets;
Ship::Context::GetInstance()->GetResourceManager()->SetAltAssetsEnabled(curAltAssets);

View File

@@ -537,12 +537,6 @@ void SohMenu::AddMenuEnhancements() {
path.column = SECTION_COLUMN_1;
AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Use Alternate Assets", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_SETTING("AltAssets"))
.RaceDisable(false)
.Options(CheckboxOptions().Tooltip(
"Toggle between standard assets and alternate assets. Usually mods will indicate if "
"this setting has to be used or not."));
AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("DisableBombBillboarding"))
.RaceDisable(false)
@@ -1908,15 +1902,6 @@ void SohMenu::AddMenuEnhancements() {
.CVar(timer.timeEnable)
.Callback([](WidgetInfo& info) { TimeDisplayUpdateDisplayOptions(); });
}
// Mod Menu
path.sidebarName = "Mod Menu";
AddSidebarEntry("Enhancements", path.sidebarName, 1);
AddWidget(path, "Popout Mod Menu Window", WIDGET_WINDOW_BUTTON)
.CVar(CVAR_WINDOW("ModMenu"))
.WindowName("Mod Menu")
.HideInSearch(true)
.Options(WindowButtonOptions().Tooltip("Enables the separate Mod Menu Window."));
}
} // namespace SohGui

View File

@@ -178,11 +178,6 @@ void SohMenu::AddMenuSettings() {
.RaceDisable(false)
.Options(CheckboxOptions().Tooltip(
"Search input box gets autofocus when visible. Does not affect using other widgets."));
AddWidget(path, "Alt Assets Tab hotkey", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_SETTING("Mods.AlternateAssetsHotkey"))
.RaceDisable(false)
.Options(
CheckboxOptions().Tooltip("Allows pressing the Tab key to toggle alternate assets").DefaultValue(true));
AddWidget(path, "Open App Files Folder", WIDGET_BUTTON)
.RaceDisable(false)
.Callback([](WidgetInfo& info) {
@@ -505,6 +500,15 @@ void SohMenu::AddMenuSettings() {
});
})
.Options(ButtonOptions().Tooltip("Displays a test notification."));
// Mod Menu
path.sidebarName = "Mod Menu";
AddSidebarEntry("Settings", path.sidebarName, 1);
AddWidget(path, "Popout Mod Menu Window", WIDGET_WINDOW_BUTTON)
.CVar(CVAR_WINDOW("ModMenu"))
.WindowName("Mod Menu")
.HideInSearch(true)
.Options(WindowButtonOptions().Tooltip("Enables the separate Mod Menu Window."));
}
} // namespace SohGui

View File

@@ -1503,7 +1503,7 @@ std::vector<Migration> version3Migrations = {
{ MigrationAction::Rename, "gOpenMenuBar", "gSettings.OpenMenuBar" },
{ MigrationAction::Rename, "gRandomizeSkipChildStealth", "gRandoSettings.SkipChildStealth" },
{ MigrationAction::Rename, "gRandomizeExcludedLocations", "gRandoSettings.ExcludedLocations" },
{ MigrationAction::Rename, "gAltAssets", "gEnhancements.AltAssets" },
{ MigrationAction::Rename, "gAltAssets", "gSettings.AltAssets" },
{ MigrationAction::Rename, "gMSAAValue", "gSettings.MSAAValue" },
{ MigrationAction::Rename, "gInternalResolution", "gSettings.InternalResolution" },
{ MigrationAction::Rename, "gTextureFilter", "gSettings.TextureFilter" },