mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
* Fix crashing with modern menu and UIWidgets when null is passed for tooltips * move to shiputils
1986 lines
104 KiB
C++
1986 lines
104 KiB
C++
#include "2s2h/Enhancements/Enhancements.h"
|
|
#include "2s2h/DeveloperTools/DeveloperTools.h"
|
|
#include "2s2h/Enhancements/GfxPatcher/AuthenticGfxPatches.h"
|
|
#include "UIWidgets.hpp"
|
|
#include "BenMenuBar.h"
|
|
#include "Notification.h"
|
|
#include "macros.h"
|
|
#include "variables.h"
|
|
#include <variant>
|
|
#include <tuple>
|
|
|
|
extern "C" {
|
|
#include "functions.h"
|
|
extern PlayState* gPlayState;
|
|
}
|
|
|
|
typedef enum {
|
|
COLOR_WHITE,
|
|
COLOR_GRAY,
|
|
COLOR_DARK_GRAY,
|
|
COLOR_INDIGO,
|
|
COLOR_RED,
|
|
COLOR_DARK_RED,
|
|
COLOR_LIGHT_GREEN,
|
|
COLOR_GREEN,
|
|
COLOR_DARK_GREEN,
|
|
COLOR_YELLOW,
|
|
} ColorOption;
|
|
|
|
typedef enum {
|
|
DISABLE_FOR_CAMERAS_OFF,
|
|
DISABLE_FOR_DEBUG_CAM_ON,
|
|
DISABLE_FOR_DEBUG_CAM_OFF,
|
|
DISABLE_FOR_FREE_LOOK_ON,
|
|
DISABLE_FOR_FREE_LOOK_OFF,
|
|
DISABLE_FOR_GYRO_OFF,
|
|
DISABLE_FOR_GYRO_ON,
|
|
DISABLE_FOR_RIGHT_STICK_OFF,
|
|
DISABLE_FOR_AUTO_SAVE_OFF,
|
|
DISABLE_FOR_NULL_PLAY_STATE,
|
|
DISABLE_FOR_DEBUG_MODE_OFF,
|
|
DISABLE_FOR_NO_VSYNC,
|
|
DISABLE_FOR_NO_WINDOWED_FULLSCREEN,
|
|
DISABLE_FOR_NO_MULTI_VIEWPORT,
|
|
DISABLE_FOR_NOT_DIRECTX,
|
|
DISABLE_FOR_DIRECTX,
|
|
DISABLE_FOR_MATCH_REFRESH_RATE_ON,
|
|
DISABLE_FOR_MOTION_BLUR_MODE,
|
|
DISABLE_FOR_MOTION_BLUR_OFF,
|
|
DISABLE_FOR_FRAME_ADVANCE_OFF,
|
|
DISABLE_FOR_WARP_POINT_NOT_SET,
|
|
DISABLE_FOR_INTRO_SKIP_OFF,
|
|
} DisableOption;
|
|
|
|
struct widgetInfo;
|
|
struct disabledInfo;
|
|
using VoidFunc = void (*)();
|
|
using DisableInfoFunc = bool (*)(disabledInfo&);
|
|
using DisableVec = std::vector<DisableOption>;
|
|
using WidgetFunc = void (*)(widgetInfo&);
|
|
std::string disabledTempTooltip;
|
|
const char* disabledTooltip;
|
|
bool disabledValue = false;
|
|
ColorOption menuThemeIndex = COLOR_INDIGO;
|
|
const ImVec4 COLOR_NONE = { 0, 0, 0, 0 };
|
|
|
|
typedef enum {
|
|
WIDGET_CHECKBOX,
|
|
WIDGET_COMBOBOX,
|
|
WIDGET_SLIDER_INT,
|
|
WIDGET_SLIDER_FLOAT,
|
|
WIDGET_CVAR_CHECKBOX,
|
|
WIDGET_CVAR_COMBOBOX,
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
WIDGET_BUTTON,
|
|
WIDGET_COLOR_24, // color picker without alpha
|
|
WIDGET_COLOR_32, // color picker with alpha
|
|
WIDGET_SEARCH,
|
|
WIDGET_SEPARATOR,
|
|
WIDGET_SEPARATOR_TEXT,
|
|
WIDGET_TEXT,
|
|
WIDGET_WINDOW_BUTTON,
|
|
WIDGET_AUDIO_BACKEND, // needed because of special operations that can't be handled easily with the normal combobox
|
|
// widget
|
|
WIDGET_VIDEO_BACKEND // same as above
|
|
} WidgetType;
|
|
|
|
typedef enum {
|
|
MOTION_BLUR_DYNAMIC,
|
|
MOTION_BLUR_ALWAYS_OFF,
|
|
MOTION_BLUR_ALWAYS_ON,
|
|
} MotionBlurOption;
|
|
|
|
typedef enum {
|
|
DEBUG_LOG_TRACE,
|
|
DEBUG_LOG_DEBUG,
|
|
DEBUG_LOG_INFO,
|
|
DEBUG_LOG_WARN,
|
|
DEBUG_LOG_ERROR,
|
|
DEBUG_LOG_CRITICAL,
|
|
DEBUG_LOG_OFF,
|
|
} DebugLogOption;
|
|
|
|
// holds the widget values for a widget, contains all CVar types available from LUS. int32_t is used for boolean
|
|
// evaluation
|
|
using CVarVariant = std::variant<int32_t, const char*, float, Color_RGBA8, Color_RGB8>;
|
|
|
|
// contains various information used to display specific types of widgets
|
|
// `min` and `max` are usually only used by sliders
|
|
// `defaultVariant` can be used by all widgetTypes, but defaults to 0, "", 0.0f, and white respectively, so only needed
|
|
// when you want to change those defaults
|
|
// `comboBoxOptions` is the list of dropdown options to be added to a dropdown.
|
|
// this needs to be a map of int32_t to const char*, but can also be enum values
|
|
// `valuePointer` is used for non-CVar sliders to track current widgetValue
|
|
// `size` applies only to buttons, determines whether it stretches to fill its current container or only fits its text
|
|
// `color` can be used in any way, depending on the widget. color pickers (COLOR_24 and COLOR_32) will eventually
|
|
// use this for storing its current value; WIDGET_SEPARATOR_TEXT uses this to change the text color `windowName`
|
|
// is used when using WIDGET_WINDOW_BUTTON, which optionally draws windows, like Stats, in the menu with a button
|
|
// to pop the window out of the main menu overlay
|
|
// `windowName` is what is displayed and searched for `windowButton` type and window interactions
|
|
// `labelPosition` applies to checkbox, combobox and button types. specifies label orientation compared to widget body
|
|
// `sameLine` allows widgets to be displayed on the same line as the previously registered widget
|
|
// the next three only apply to sliders. `showButtons` shows or hides the +/- buttons on either side of a slider
|
|
// `format` specifies how info is displayed within the slider
|
|
// `isPercentage` toggles the float slider's value being multiplied by 100 to show percentages instead of direct floats
|
|
struct WidgetOptions {
|
|
CVarVariant min;
|
|
CVarVariant max;
|
|
CVarVariant defaultVariant;
|
|
std::unordered_map<int32_t, const char*> comboBoxOptions;
|
|
std::variant<bool*, int32_t*, float*> valuePointer;
|
|
ImVec2 size = UIWidgets::Sizes::Fill;
|
|
ImVec4 color = COLOR_NONE;
|
|
const char* windowName = "";
|
|
UIWidgets::LabelPosition labelPosition = UIWidgets::LabelPosition::None;
|
|
bool sameLine = false;
|
|
bool showButtons = true;
|
|
const char* format = "%f";
|
|
bool isPercentage = false;
|
|
};
|
|
|
|
bool operator==(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
|
return l.r == r.r && l.g == r.g && l.b == r.b;
|
|
}
|
|
|
|
bool operator==(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
|
return l.r == r.r && l.g == r.g && l.b == r.b && l.a == r.a;
|
|
}
|
|
|
|
bool operator<(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
|
return (l.r < r.r && l.g <= r.g && l.b <= r.b) || (l.r <= r.r && l.g < r.g && l.b <= r.b) ||
|
|
(l.r <= r.r && l.g <= r.g && l.b < r.b);
|
|
}
|
|
|
|
bool operator<(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
|
return (l.r < r.r && l.g <= r.g && l.b <= r.b && l.a <= r.a) ||
|
|
(l.r <= r.r && l.g < r.g && l.b <= r.b && l.a <= r.a) ||
|
|
(l.r <= r.r && l.g <= r.g && l.b < r.b && l.a <= r.a) ||
|
|
(l.r <= r.r && l.g <= r.g && l.b <= r.b && l.a < r.a);
|
|
}
|
|
|
|
bool operator>(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
|
|
return (l.r > r.r && l.g >= r.g && l.b >= r.b) || (l.r >= r.r && l.g > r.g && l.b >= r.b) ||
|
|
(l.r >= r.r && l.g >= r.g && l.b > r.b);
|
|
}
|
|
|
|
bool operator>(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
|
|
return (l.r > r.r && l.g >= r.g && l.b >= r.b && l.a >= r.a) ||
|
|
(l.r >= r.r && l.g > r.g && l.b >= r.b && l.a >= r.a) ||
|
|
(l.r >= r.r && l.g >= r.g && l.b > r.b && l.a >= r.a) ||
|
|
(l.r >= r.r && l.g >= r.g && l.b >= r.b && l.a > r.a);
|
|
}
|
|
|
|
std::unordered_map<ColorOption, ImVec4> menuTheme = { { COLOR_WHITE, UIWidgets::Colors::White },
|
|
{ COLOR_GRAY, UIWidgets::Colors::Gray },
|
|
{ COLOR_DARK_GRAY, UIWidgets::Colors::DarkGray },
|
|
{ COLOR_INDIGO, UIWidgets::Colors::Indigo },
|
|
{ COLOR_RED, UIWidgets::Colors::Red },
|
|
{ COLOR_DARK_RED, UIWidgets::Colors::DarkRed },
|
|
{ COLOR_LIGHT_GREEN, UIWidgets::Colors::LightGreen },
|
|
{ COLOR_GREEN, UIWidgets::Colors::Green },
|
|
{ COLOR_DARK_GREEN, UIWidgets::Colors::DarkGreen },
|
|
{ COLOR_YELLOW, UIWidgets::Colors::Yellow } };
|
|
|
|
// All the info needed for display and search of all widgets in the menu. WidgetName is the label displayed,
|
|
// `widgetCVar` is the string representation of the CVar used to store the widget value
|
|
// `widgetTooltip` is what is displayed when hovering (except when disabled, more on that later)
|
|
// `widgetType` is the WidgetType for the widget, which is what determines how the information is used in the draw
|
|
// function. all of the preceding are required parts for every widget except for the special widgets (backend dropdowns,
|
|
// separators, etc) various parts of widgetOptions are required depending on what widget type you're using
|
|
// `widgetCallback` is a lambda used for running code on widget change
|
|
// `preFunc` is a lambda called before drawing code starts. It can be used to determine a widget's status,
|
|
// whether disabled or hidden, as well as update pointers for non-CVar widget types.
|
|
// `postFunc` is a lambda called after all drawing code is finished, for reacting to states other than
|
|
// widgets having been changed, like holding buttons.
|
|
// All three lambdas accept a `widgetInfo` pointer in case it needs information on the widget for these operations
|
|
// `activeDisables` is a vector of DisableOptions for specifying what reasons a widget is disabled, which are displayed
|
|
// in the disabledTooltip for the widget. Can display multiple reasons. Handling the reasons is done in `modifierFunc`.
|
|
// It is recommended to utilize `disabledInfo`/`DisableReason` to list out all reasons for disabling and isHidden so
|
|
// the info can be shown.
|
|
// `isHidden` just prevents the widget from being drawn under whatever circumstances you specify in the `modifierFunc`
|
|
struct widgetInfo {
|
|
std::string widgetName; // Used by all widgets
|
|
const char* widgetCVar; // Used by all widgets except
|
|
const char* widgetTooltip;
|
|
WidgetType widgetType;
|
|
WidgetOptions widgetOptions;
|
|
WidgetFunc widgetCallback = nullptr;
|
|
WidgetFunc preFunc = nullptr;
|
|
WidgetFunc postFunc = nullptr;
|
|
DisableVec activeDisables = {};
|
|
bool isHidden = false;
|
|
};
|
|
|
|
// `disabledInfo` holds information on reasons for hiding or disabling a widget, as well as an evaluation lambda that
|
|
// is run once per frame to update its status (this is done to prevent dozens of redundant CVarGets in each frame loop)
|
|
// `evaluation` returns a bool which can be determined by whatever code you want that changes its status
|
|
// `reason` is the text displayed in the disabledTooltip when a widget is disabled by a particular DisableReason
|
|
// `active` is what's referenced when determining disabled status for a widget that uses this This can also be used to
|
|
// hold reasons to hide widgets so that their evaluations are also only run once per frame
|
|
struct disabledInfo {
|
|
DisableInfoFunc evaluation;
|
|
const char* reason;
|
|
bool active = false;
|
|
int32_t value = 0;
|
|
};
|
|
|
|
// Contains the name displayed in the sidebar (label), the number of columns to use in drawing (columnCount; for visual
|
|
// separation, 1-3), and nested vectors of the widgets, grouped by column (columnWidgets). The number of widget vectors
|
|
// added to the column groups does not need to match the specified columnCount, e.g. you can have one vector added to
|
|
// the sidebar, but still separate the window into 3 columns and display only in the first column
|
|
struct SidebarEntry {
|
|
std::string label;
|
|
uint32_t columnCount;
|
|
std::vector<std::vector<widgetInfo>> columnWidgets;
|
|
};
|
|
|
|
// Contains entries for what's listed in the header at the top, including the name displayed on the top bar (label),
|
|
// a vector of the SidebarEntries for that header entry, and the name of the cvar used to track what sidebar entry is
|
|
// the last viewed for that header.
|
|
struct MainMenuEntry {
|
|
std::string label;
|
|
std::vector<SidebarEntry> sidebarEntries;
|
|
const char* sidebarCvar;
|
|
};
|
|
extern std::unordered_map<s16, const char*> warpPointSceneList;
|
|
extern void Warp();
|
|
|
|
namespace BenGui {
|
|
extern std::shared_ptr<std::vector<Ship::WindowBackend>> availableWindowBackends;
|
|
extern std::unordered_map<Ship::WindowBackend, const char*> availableWindowBackendsMap;
|
|
extern Ship::WindowBackend configWindowBackend;
|
|
extern void UpdateWindowBackendObjects();
|
|
|
|
std::vector<MainMenuEntry> menuEntries;
|
|
static ImGuiTextFilter menuSearch;
|
|
std::vector<SidebarEntry> settingsSidebar;
|
|
std::vector<SidebarEntry> enhancementsSidebar;
|
|
std::vector<SidebarEntry> devToolsSidebar;
|
|
uint8_t searchSidebarIndex = 0;
|
|
SidebarEntry searchSidebarEntry = {
|
|
"Search",
|
|
1,
|
|
{ { { "Menu Theme", "", "Searches all menus for the given text, including tooltips.", WIDGET_SEARCH } } }
|
|
};
|
|
|
|
static std::map<DisableOption, disabledInfo> disabledMap = {
|
|
{ DISABLE_FOR_CAMERAS_OFF,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return !CVarGetInteger("gEnhancements.Camera.DebugCam.Enable", 0) &&
|
|
!CVarGetInteger("gEnhancements.Camera.FreeLook.Enable", 0);
|
|
},
|
|
"Both Debug Camera and Free Look are Disabled" } },
|
|
{ DISABLE_FOR_DEBUG_CAM_ON,
|
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gEnhancements.Camera.DebugCam.Enable", 0); },
|
|
"Debug Camera is Enabled" } },
|
|
{ DISABLE_FOR_DEBUG_CAM_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.DebugCam.Enable", 0); },
|
|
"Debug Camera is Disabled" } },
|
|
{ DISABLE_FOR_FREE_LOOK_ON,
|
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gEnhancements.Camera.FreeLook.Enable", 0); },
|
|
"Free Look is Enabled" } },
|
|
{ DISABLE_FOR_FREE_LOOK_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.FreeLook.Enable", 0); },
|
|
"Free Look is Disabled" } },
|
|
{ DISABLE_FOR_GYRO_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Camera.FirstPerson.GyroEnabled", 0); },
|
|
"Gyro Aiming is Disabled" } },
|
|
{ DISABLE_FOR_GYRO_ON,
|
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gEnhancements.Camera.FirstPerson.GyroEnabled", 0); },
|
|
"Gyro Aiming is Enabled" } },
|
|
{ DISABLE_FOR_RIGHT_STICK_OFF,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return !CVarGetInteger("gEnhancements.Camera.FirstPerson.RightStickEnabled", 0);
|
|
},
|
|
"Right Stick Aiming is Disabled" } },
|
|
{ DISABLE_FOR_AUTO_SAVE_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Saving.Autosave", 0); },
|
|
"AutoSave is Disabled" } },
|
|
{ DISABLE_FOR_NULL_PLAY_STATE,
|
|
{ [](disabledInfo& info) -> bool { return gPlayState == NULL; }, "Save Not Loaded" } },
|
|
{ DISABLE_FOR_DEBUG_MODE_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gDeveloperTools.DebugEnabled", 0); },
|
|
"Debug Mode is Disabled" } },
|
|
{ DISABLE_FOR_NO_VSYNC,
|
|
{ [](disabledInfo& info) -> bool { return !Ship::Context::GetInstance()->GetWindow()->CanDisableVerticalSync(); },
|
|
"Disabling VSync not supported" } },
|
|
{ DISABLE_FOR_NO_WINDOWED_FULLSCREEN,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return !Ship::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen();
|
|
},
|
|
"Windowed Fullscreen not supported" } },
|
|
{ DISABLE_FOR_NO_MULTI_VIEWPORT,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return !Ship::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports();
|
|
},
|
|
"Multi-viewports not supported" } },
|
|
{ DISABLE_FOR_NOT_DIRECTX,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() !=
|
|
Ship::WindowBackend::FAST3D_DXGI_DX11;
|
|
},
|
|
"Available Only on DirectX" } },
|
|
{ DISABLE_FOR_DIRECTX,
|
|
{ [](disabledInfo& info) -> bool {
|
|
return Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() ==
|
|
Ship::WindowBackend::FAST3D_DXGI_DX11;
|
|
},
|
|
"Not Available on DirectX" } },
|
|
{ DISABLE_FOR_MATCH_REFRESH_RATE_ON,
|
|
{ [](disabledInfo& info) -> bool { return CVarGetInteger("gMatchRefreshRate", 0); },
|
|
"Match Refresh Rate is Enabled" } },
|
|
{ DISABLE_FOR_MOTION_BLUR_MODE,
|
|
{ [](disabledInfo& info) -> bool {
|
|
info.value = CVarGetInteger("gEnhancements.Graphics.MotionBlur.Mode", 0);
|
|
return !info.value;
|
|
},
|
|
"Motion Blur Mode mismatch" } },
|
|
{ DISABLE_FOR_MOTION_BLUR_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !R_MOTION_BLUR_ENABLED; }, "Motion Blur is Disabled" } },
|
|
{ DISABLE_FOR_FRAME_ADVANCE_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !(gPlayState != nullptr && gPlayState->frameAdvCtx.enabled); },
|
|
"Frame Advance is Disabled" } },
|
|
{ DISABLE_FOR_WARP_POINT_NOT_SET,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger(WARP_POINT_CVAR "Saved", 0); },
|
|
"Warp Point Not Saved" } },
|
|
{ DISABLE_FOR_INTRO_SKIP_OFF,
|
|
{ [](disabledInfo& info) -> bool { return !CVarGetInteger("gEnhancements.Cutscenes.SkipIntroSequence", 0); },
|
|
"Intro Skip Not Selected" } }
|
|
};
|
|
|
|
std::unordered_map<int32_t, const char*> menuThemeOptions = {
|
|
{ COLOR_WHITE, "White" },
|
|
{ COLOR_GRAY, "Gray" },
|
|
{ COLOR_DARK_GRAY, "Dark Gray" },
|
|
{ COLOR_INDIGO, "Indigo" },
|
|
{ COLOR_RED, "Red" },
|
|
{ COLOR_DARK_RED, "Dark Red" },
|
|
{ COLOR_LIGHT_GREEN, "Light Green" },
|
|
{ COLOR_GREEN, "Green" },
|
|
{ COLOR_DARK_GREEN, "Dark Green" },
|
|
{ COLOR_YELLOW, "Yellow" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> alwaysWinDoggyraceOptions = {
|
|
{ ALWAYS_WIN_DOGGY_RACE_OFF, "Off" },
|
|
{ ALWAYS_WIN_DOGGY_RACE_MASKOFTRUTH, "When owning Mask of Truth" },
|
|
{ ALWAYS_WIN_DOGGY_RACE_ALWAYS, "Always" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> cremiaRewardOptions = {
|
|
{ CREMIA_REWARD_RANDOM, "Vanilla" },
|
|
{ CREMIA_REWARD_ALWAYS_HUG, "Hug" },
|
|
{ CREMIA_REWARD_ALWAYS_RUPEE, "Rupee" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> clockTypeOptions = {
|
|
{ CLOCK_TYPE_ORIGINAL, "Original" },
|
|
{ CLOCK_TYPE_3DS, "MM3D style" },
|
|
{ CLOCK_TYPE_TEXT_BASED, "Text only" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> textureFilteringMap = {
|
|
{ FILTER_THREE_POINT, "Three-Point" },
|
|
{ FILTER_LINEAR, "Linear" },
|
|
{ FILTER_NONE, "None" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> motionBlurOptions = {
|
|
{ MOTION_BLUR_DYNAMIC, "Dynamic (default)" },
|
|
{ MOTION_BLUR_ALWAYS_OFF, "Always Off" },
|
|
{ MOTION_BLUR_ALWAYS_ON, "Always On" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> debugSaveOptions = {
|
|
{ DEBUG_SAVE_INFO_COMPLETE, "100\% save" },
|
|
{ DEBUG_SAVE_INFO_VANILLA_DEBUG, "Vanilla debug save" },
|
|
{ DEBUG_SAVE_INFO_NONE, "Empty save" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> logLevels = {
|
|
{ DEBUG_LOG_TRACE, "Trace" }, { DEBUG_LOG_DEBUG, "Debug" }, { DEBUG_LOG_INFO, "Info" },
|
|
{ DEBUG_LOG_WARN, "Warn" }, { DEBUG_LOG_ERROR, "Error" }, { DEBUG_LOG_CRITICAL, "Critical" },
|
|
{ DEBUG_LOG_OFF, "Off" },
|
|
};
|
|
|
|
static const std::unordered_map<Ship::AudioBackend, const char*> audioBackendsMap = {
|
|
{ Ship::AudioBackend::WASAPI, "Windows Audio Session API" },
|
|
{ Ship::AudioBackend::SDL, "SDL" },
|
|
};
|
|
|
|
static std::unordered_map<Ship::WindowBackend, const char*> windowBackendsMap = {
|
|
{ Ship::WindowBackend::FAST3D_DXGI_DX11, "DirectX" },
|
|
{ Ship::WindowBackend::FAST3D_SDL_OPENGL, "OpenGL" },
|
|
{ Ship::WindowBackend::FAST3D_SDL_METAL, "Metal" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> timeStopOptions = {
|
|
{ TIME_STOP_OFF, "Off" },
|
|
{ TIME_STOP_TEMPLES, "Temples" },
|
|
{ TIME_STOP_TEMPLES_DUNGEONS, "Temples + Mini Dungeons" },
|
|
};
|
|
|
|
static const std::unordered_map<int32_t, const char*> notificationPosition = {
|
|
{ 0, "Top Left" }, { 1, "Top Right" }, { 2, "Bottom Left" }, { 3, "Bottom Right" }, { 4, "Hidden" },
|
|
};
|
|
|
|
void FreeLookPitchMinMax() {
|
|
f32 maxY = CVarGetFloat("gEnhancements.Camera.FreeLook.MaxPitch", 72.0f);
|
|
f32 minY = CVarGetFloat("gEnhancements.Camera.FreeLook.MinPitch", -49.0f);
|
|
CVarSetFloat("gEnhancements.Camera.FreeLook.MaxPitch", std::max(maxY, minY));
|
|
CVarSetFloat("gEnhancements.Camera.FreeLook.MinPitch", std::min(maxY, minY));
|
|
}
|
|
|
|
void InsertSidebarSearch() {
|
|
menuEntries[0].sidebarEntries.insert(menuEntries[0].sidebarEntries.begin() + searchSidebarIndex,
|
|
searchSidebarEntry);
|
|
CVarSetInteger(menuEntries[0].sidebarCvar, CVarGetInteger(menuEntries[0].sidebarCvar, 0) + 1);
|
|
}
|
|
|
|
void AddSettings() {
|
|
// General Settings
|
|
settingsSidebar.push_back(
|
|
{ "General",
|
|
3,
|
|
{ {
|
|
{ "Menu Theme",
|
|
"gSettings.MenuTheme",
|
|
"Changes the Theme of the Menu Widgets.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .defaultVariant = COLOR_INDIGO, .comboBoxOptions = menuThemeOptions } },
|
|
#if not defined(__SWITCH__) and not defined(__WIIU__)
|
|
{ "Menu Controller Navigation", CVAR_IMGUI_CONTROLLER_NAV,
|
|
"Allows controller navigation of the 2Ship menu (Settings, Enhancements,...)\nCAUTION: "
|
|
"This will disable game inputs while the menu is visible.\n\nD-pad to move between "
|
|
"items, A to select, B to move up in scope.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Cursor Always Visible",
|
|
"gSettings.CursorVisibility",
|
|
"Makes the cursor always visible, even in full screen.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
Ship::Context::GetInstance()->GetWindow()->SetForceCursorVisibility(
|
|
CVarGetInteger("gSettings.CursorVisibility", 0));
|
|
} },
|
|
#endif
|
|
{ "Search In Sidebar",
|
|
"gSettings.SidebarSearch",
|
|
"Displays the Search menu as a sidebar entry in Settings instead of in the header.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
if (CVarGetInteger("gSettings.SidebarSearch", 0)) {
|
|
InsertSidebarSearch();
|
|
} else {
|
|
std::erase_if(menuEntries[0].sidebarEntries, [](SidebarEntry entry) {
|
|
return strncmp(entry.label.c_str(), searchSidebarEntry.label.c_str(), 5) == 0;
|
|
});
|
|
int32_t sidebarVal = CVarGetInteger(menuEntries[0].sidebarCvar, 0);
|
|
if (sidebarVal < menuEntries.size()) {
|
|
sidebarVal -= 1;
|
|
} else {
|
|
sidebarVal = menuEntries.size() - 1;
|
|
}
|
|
CVarSetInteger(menuEntries[0].sidebarCvar, sidebarVal);
|
|
}
|
|
} },
|
|
{ "Search Input Autofocus", "gSettings.SearchAutofocus",
|
|
"Search input box gets autofocus when visible. Does not affect using other widgets.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ .widgetName = "Alt Assets Tab hotkey",
|
|
.widgetCVar = "gEnhancements.Mods.AlternateAssetsHotkey",
|
|
.widgetTooltip = "Allows pressing the Tab key to toggle alternate assets",
|
|
.widgetType = WIDGET_CVAR_CHECKBOX,
|
|
.widgetOptions = { .defaultVariant = true } },
|
|
{ "Open App Files Folder",
|
|
"",
|
|
"Opens the folder that contains the save and mods folders, etc.",
|
|
WIDGET_BUTTON,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
std::string filesPath = Ship::Context::GetInstance()->GetAppDirectoryPath();
|
|
SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str());
|
|
} },
|
|
} } });
|
|
// Audio Settings
|
|
settingsSidebar.push_back(
|
|
{ "Audio",
|
|
3,
|
|
{ { { "Master Volume: %.0f%%",
|
|
"gSettings.Audio.MasterVolume",
|
|
"Adjust overall sound volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true } },
|
|
{ "Main Music Volume: %.0f%%",
|
|
"gSettings.Audio.MainMusicVolume",
|
|
"Adjust the Background Music volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
AudioSeq_SetPortVolumeScale(SEQ_PLAYER_BGM_MAIN,
|
|
CVarGetFloat("gSettings.Audio.MainMusicVolume", 1.0f));
|
|
} },
|
|
{ "Sub Music Volume: %.0f%%",
|
|
"gSettings.Audio.SubMusicVolume",
|
|
"Adjust the Sub Music volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
AudioSeq_SetPortVolumeScale(SEQ_PLAYER_BGM_SUB,
|
|
CVarGetFloat("gSettings.Audio.SubMusicVolume", 1.0f));
|
|
} },
|
|
{ "Sound Effects Volume: %.0f%%",
|
|
"gSettings.Audio.SoundEffectsVolume",
|
|
"Adjust the Sound Effects volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
AudioSeq_SetPortVolumeScale(SEQ_PLAYER_SFX,
|
|
CVarGetFloat("gSettings.Audio.SoundEffectsVolume", 1.0f));
|
|
} },
|
|
{ "Fanfare Volume: %.0f%%",
|
|
"gSettings.Audio.FanfareVolume",
|
|
"Adjust the Fanfare volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
AudioSeq_SetPortVolumeScale(SEQ_PLAYER_FANFARE,
|
|
CVarGetFloat("gSettings.Audio.FanfareVolume", 1.0f));
|
|
} },
|
|
{ "Ambience Volume: %.0f%%",
|
|
"gSettings.Audio.AmbienceVolume",
|
|
"Adjust the Ambient Sound volume.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f,
|
|
.max = 100.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
AudioSeq_SetPortVolumeScale(SEQ_PLAYER_AMBIENCE,
|
|
CVarGetFloat("gSettings.Audio.AmbienceVolume", 1.0f));
|
|
} },
|
|
{ "Audio API", NULL, "Sets the audio API used by the game. Requires a relaunch to take effect.",
|
|
WIDGET_AUDIO_BACKEND } } } });
|
|
// Graphics Settings
|
|
static int32_t maxFps;
|
|
const char* tooltip = "";
|
|
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
|
|
maxFps = 360;
|
|
tooltip = "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is "
|
|
"purely visual and does not impact game logic, execution of glitches etc.\n\nA higher target "
|
|
"FPS than your monitor's refresh rate will waste resources, and might give a worse result.";
|
|
} else {
|
|
maxFps = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
|
tooltip = "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is "
|
|
"purely visual and does not impact game logic, execution of glitches etc.";
|
|
}
|
|
settingsSidebar.push_back(
|
|
{ "Graphics",
|
|
3,
|
|
{ { { "Toggle Fullscreen",
|
|
"gSettings.Fullscreen",
|
|
"Toggles Fullscreen On/Off.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { Ship::Context::GetInstance()->GetWindow()->ToggleFullscreen(); } },
|
|
#ifndef __APPLE__
|
|
{ "Internal Resolution: %.0f%%",
|
|
CVAR_INTERNAL_RESOLUTION,
|
|
"Multiplies your output resolution by the value inputted, as a more intensive but effective "
|
|
"form of anti-aliasing.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 50.0f,
|
|
.max = 200.0f,
|
|
.defaultVariant = 100.0f,
|
|
.showButtons = false,
|
|
.format = "",
|
|
.isPercentage = true },
|
|
[](widgetInfo& info) {
|
|
Ship::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(
|
|
CVarGetFloat(CVAR_INTERNAL_RESOLUTION, 1));
|
|
} },
|
|
#endif
|
|
#ifndef __WIIU__
|
|
{ "Anti-aliasing (MSAA): %d",
|
|
CVAR_MSAA_VALUE,
|
|
"Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of rendered "
|
|
"geometry.\n"
|
|
"Higher sample count will result in smoother edges on models, but may reduce performance.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 8, 1 },
|
|
[](widgetInfo& info) {
|
|
Ship::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger(CVAR_MSAA_VALUE, 1));
|
|
} },
|
|
#endif
|
|
|
|
{ "Current FPS: %d",
|
|
"gInterpolationFPS",
|
|
tooltip,
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 20, maxFps, 20 },
|
|
[](widgetInfo& info) {
|
|
int32_t defaultVariant = std::get<int32_t>(info.widgetOptions.defaultVariant);
|
|
if (CVarGetInteger(info.widgetCVar, defaultVariant) == defaultVariant) {
|
|
info.widgetName = "Current FPS: Original (%d)";
|
|
} else {
|
|
info.widgetName = "Current FPS: %d";
|
|
}
|
|
},
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_MATCH_REFRESH_RATE_ON).active)
|
|
info.activeDisables.push_back(DISABLE_FOR_MATCH_REFRESH_RATE_ON);
|
|
} },
|
|
{ "Match Refresh Rate",
|
|
"",
|
|
"Matches interpolation value to the current game's window refresh rate.",
|
|
WIDGET_BUTTON,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
int hz = Ship::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
|
|
if (hz >= 20 && hz <= 360) {
|
|
CVarSetInteger("gInterpolationFPS", hz);
|
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
}
|
|
},
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NOT_DIRECTX).active; } },
|
|
{ "Match Refresh Rate",
|
|
"gMatchRefreshRate",
|
|
"Matches interpolation value to the current game's window refresh rate.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DIRECTX).active; } },
|
|
{ "Jitter fix : >= % d FPS",
|
|
"gExtraLatencyThreshold",
|
|
"When Interpolation FPS setting is at least this threshold, add one frame of input "
|
|
"lag (e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the "
|
|
"CPU to work on one frame while GPU works on the previous frame.\nThis setting "
|
|
"should be used when your computer is too slow to do CPU + GPU work in time.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 0, 360, 80 },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NOT_DIRECTX).active; } },
|
|
{ "Renderer API (Needs reload)", NULL, "Sets the renderer API used by the game.", WIDGET_VIDEO_BACKEND },
|
|
{ "Enable Vsync",
|
|
CVAR_VSYNC_ENABLED,
|
|
"Enables Vsync.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NO_VSYNC).active; } },
|
|
{ "Windowed Fullscreen",
|
|
CVAR_SDL_WINDOWED_FULLSCREEN,
|
|
"Enables Windowed Fullscreen Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NO_WINDOWED_FULLSCREEN).active; } },
|
|
{ "Allow multi-windows",
|
|
CVAR_ENABLE_MULTI_VIEWPORTS,
|
|
"Allows multiple windows to be opened at once. Requires a reload to take effect.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NO_MULTI_VIEWPORT).active; } },
|
|
{ "Texture Filter (Needs reload)",
|
|
CVAR_TEXTURE_FILTER,
|
|
"Sets the applied Texture Filtering.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = textureFilteringMap } } } } });
|
|
// Input Editor
|
|
settingsSidebar.push_back({ "Input Editor",
|
|
1,
|
|
{ { { "Popout Input Editor",
|
|
"gWindows.BenInputEditor",
|
|
"Enables the separate Input Editor window.",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "2S2H Input Editor" } } } } });
|
|
|
|
settingsSidebar.push_back({ "Notifications",
|
|
1,
|
|
{ {
|
|
{ "Position",
|
|
"gNotifications.Position",
|
|
"Which corner of the screen notifications appear in.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .defaultVariant = 3, .comboBoxOptions = notificationPosition } },
|
|
{ "Duration: %.0f seconds",
|
|
"gNotifications.Duration",
|
|
"How long notifications are displayed for.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 300.0f, .max = 3000.0f, .defaultVariant = 1000.0f } },
|
|
{ "Background Opacity: %.0f%%",
|
|
"gNotifications.BgOpacity",
|
|
"How opaque the background of notifications is.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 0.0f, .max = 100.0f, .defaultVariant = 50.0f, .isPercentage = true } },
|
|
{ "Size %.1f",
|
|
"gNotifications.Size",
|
|
"How large notifications are.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 100.0f, .max = 500.0f, .defaultVariant = 180.0f } },
|
|
{ "Test Notification",
|
|
"",
|
|
"Displays a test notification.",
|
|
WIDGET_BUTTON,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
Notification::Emit({
|
|
.itemIcon = "__OTR__icon_item_24_static_yar/gQuestIconGoldSkulltulaTex",
|
|
.prefix = "This",
|
|
.message = "is a",
|
|
.suffix = "test.",
|
|
});
|
|
} },
|
|
} } });
|
|
|
|
if (CVarGetInteger("gSettings.SidebarSearch", 0)) {
|
|
settingsSidebar.insert(settingsSidebar.begin() + searchSidebarIndex, searchSidebarEntry);
|
|
}
|
|
}
|
|
int32_t motionBlurStrength;
|
|
|
|
void AddEnhancements() {
|
|
// Camera Snap Fix
|
|
enhancementsSidebar.push_back(
|
|
{ "Camera",
|
|
3,
|
|
{ {
|
|
{ .widgetName = "Fixes", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Fix Targeting Camera Snap",
|
|
"gEnhancements.Camera.FixTargettingCameraSnap",
|
|
"Fixes the camera snap that occurs when you are moving and press the targeting button.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{} },
|
|
{ .widgetName = "First Person", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Disable Auto-Centering",
|
|
"gEnhancements.Camera.FirstPerson.DisableFirstPersonAutoCenterView",
|
|
"Disables the auto-centering of the camera in first person mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_GYRO_ON).active)
|
|
info.activeDisables.push_back(DISABLE_FOR_GYRO_ON);
|
|
} },
|
|
{ "Invert X Axis", "gEnhancements.Camera.FirstPerson.InvertX",
|
|
"Inverts the X Axis of the Camera in First Person Mode.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Invert Y Axis",
|
|
"gEnhancements.Camera.FirstPerson.InvertY",
|
|
"Inverts the Y Axis of the Camera in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{ .defaultVariant = true } },
|
|
{ "X Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.SensitivityX",
|
|
"Adjusts the Sensitivity of the X Axis in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr },
|
|
{ "Y Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.SensitivityY",
|
|
"Adjusts the Sensitivity of the Y Axis in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr },
|
|
{ "Gyro Aiming",
|
|
"gEnhancements.Camera.FirstPerson.GyroEnabled",
|
|
"Enables Gyro Aiming in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr },
|
|
{ "Invert Gyro X Axis",
|
|
"gEnhancements.Camera.FirstPerson.GyroInvertX",
|
|
"Inverts the X Axis of the Gyro in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_GYRO_OFF).active; } },
|
|
{ "Invert Gyro Y Axis",
|
|
"gEnhancements.Camera.FirstPerson.GyroInvertY",
|
|
"Inverts the Y Axis of the Gyro in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_GYRO_OFF).active; } },
|
|
{ "Gyro X Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.GyroSensitivityX",
|
|
"Adjusts the Sensitivity of the X Axis of the Gyro in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_GYRO_OFF).active; } },
|
|
{ "Gyro Y Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.GyroSensitivityY",
|
|
"Adjusts the Sensitivity of the Y Axis of the Gyro in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_GYRO_OFF).active; } },
|
|
{ "Right Stick Aiming", "gEnhancements.Camera.FirstPerson.RightStickEnabled",
|
|
"Enables Right Stick Aiming in First Person Mode.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Move while aiming",
|
|
"gEnhancements.Camera.FirstPerson.MoveInFirstPerson",
|
|
"Allows movement with the left stick while in first person mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_RIGHT_STICK_OFF).active)
|
|
info.activeDisables.push_back(DISABLE_FOR_RIGHT_STICK_OFF);
|
|
} },
|
|
{ "Invert Right Stick X Axis",
|
|
"gEnhancements.Camera.FirstPerson.RightStickInvertX",
|
|
"Inverts the X Axis of the Right Stick in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_RIGHT_STICK_OFF).active; } },
|
|
{ "Invert Right Stick Y Axis",
|
|
"gEnhancements.Camera.FirstPerson.RightStickInvertY",
|
|
"Inverts the Y Axis of the Right Stick in First Person Mode.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{ .defaultVariant = true },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_RIGHT_STICK_OFF).active; } },
|
|
{ "Right Stick X Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.RightStickSensitivityX",
|
|
"Adjusts the Sensitivity of the X Axis of the Right Stick in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_RIGHT_STICK_OFF).active; } },
|
|
{ "Right Stick Y Axis Sensitivity: %.0f%%",
|
|
"gEnhancements.Camera.FirstPerson.RightStickSensitivityY",
|
|
"Adjusts the Sensitivity of the Y Axis of the Right Stick in First Person Mode.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ .min = 10.0f, .max = 200.0f, .defaultVariant = 100.0f, .format = "%.0f%%", .isPercentage = true },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_RIGHT_STICK_OFF).active; } },
|
|
},
|
|
// Camera Enhancements
|
|
{ { .widgetName = "Cameras", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Free Look",
|
|
"gEnhancements.Camera.FreeLook.Enable",
|
|
"Enables free look camera control\nNote: You must remap C buttons off of the right "
|
|
"stick in the controller config menu, and map the camera stick to the right stick.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterCameraFreeLook(); },
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_DEBUG_CAM_ON).active)
|
|
info.activeDisables.push_back(DISABLE_FOR_DEBUG_CAM_ON);
|
|
} },
|
|
{ "Camera Distance: %d",
|
|
"gEnhancements.Camera.FreeLook.MaxCameraDistance",
|
|
"Maximum Camera Distance for Free Look.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 100, 900, 185 },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_FREE_LOOK_OFF).active; } },
|
|
{ "Camera Transition Speed: %d",
|
|
"gEnhancements.Camera.FreeLook.TransitionSpeed",
|
|
"Can someone help me?",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 900, 25 },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_FREE_LOOK_OFF).active; } },
|
|
{ "Max Camera Height Angle: %.0f\xC2\xB0",
|
|
"gEnhancements.Camera.FreeLook.MaxPitch",
|
|
"Maximum Height of the Camera.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ -8900.0f, 8900.0f, 7200.0f },
|
|
[](widgetInfo& info) { FreeLookPitchMinMax(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_FREE_LOOK_OFF).active; } },
|
|
{ "Min Camera Height Angle: %.0f\xC2\xB0",
|
|
"gEnhancements.Camera.FreeLook.MinPitch",
|
|
"Minimum Height of the Camera.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ -8900.0f, 8900.0f, -4900.0f },
|
|
[](widgetInfo& info) { FreeLookPitchMinMax(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_FREE_LOOK_OFF).active; } },
|
|
{ "Debug Camera",
|
|
"gEnhancements.Camera.DebugCam.Enable",
|
|
"Enables free camera control.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
([](widgetInfo& info) { RegisterDebugCam(); }),
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_FREE_LOOK_ON).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_FREE_LOOK_ON);
|
|
}
|
|
} },
|
|
{ "Invert Camera X Axis",
|
|
"gEnhancements.Camera.RightStick.InvertXAxis",
|
|
"Inverts the Camera X Axis",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_CAMERAS_OFF).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_CAMERAS_OFF);
|
|
}
|
|
} },
|
|
{ "Invert Camera Y Axis",
|
|
"gEnhancements.Camera.RightStick.InvertYAxis",
|
|
"Inverts the Camera Y Axis",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_CAMERAS_OFF).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_CAMERAS_OFF);
|
|
}
|
|
} },
|
|
{ "Third-Person Horizontal Sensitivity: %.0f",
|
|
"gEnhancements.Camera.RightStick.CameraSensitivity.X",
|
|
"Adjust the Sensitivity of the x axis when in Third Person.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ 1.0f, 500.0f, 100.0f },
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_CAMERAS_OFF).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_CAMERAS_OFF);
|
|
}
|
|
} },
|
|
{ "Third-Person Vertical Sensitivity: %.0f",
|
|
"gEnhancements.Camera.RightStick.CameraSensitivity.Y",
|
|
"Adjust the Sensitivity of the x axis when in Third Person.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ 1.0f, 500.0f, 100.0f },
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_CAMERAS_OFF).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_CAMERAS_OFF);
|
|
}
|
|
} },
|
|
{ "Enable Roll (6\xC2\xB0 of Freedom)",
|
|
"gEnhancements.Camera.DebugCam.6DOF",
|
|
"This allows for all six degrees of movement with the camera, NOTE: Yaw will work "
|
|
"differently in this system, instead rotating around the focal point"
|
|
", rather than a polar axis.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_CAM_OFF).active; } },
|
|
{ "Camera Speed: %.0f",
|
|
"gEnhancements.Camera.DebugCam.CameraSpeed",
|
|
"Adjusts the speed of the Camera.",
|
|
WIDGET_CVAR_SLIDER_FLOAT,
|
|
{ 10.0f, 300.0f, 50.0f },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_CAM_OFF).active; } } } } });
|
|
// Cheats
|
|
enhancementsSidebar.push_back(
|
|
{ "Cheats",
|
|
3,
|
|
{ { { "Infinite Health", "gCheats.InfiniteHealth", "Always have full Hearts.", WIDGET_CVAR_CHECKBOX, {} },
|
|
{ "Infinite Magic", "gCheats.InfiniteMagic", "Always have full Magic.", WIDGET_CVAR_CHECKBOX, {} },
|
|
{ "Infinite Rupees", "gCheats.InfiniteRupees", "Always have a full Wallet.", WIDGET_CVAR_CHECKBOX, {} },
|
|
{ "Infinite Consumables", "gCheats.InfiniteConsumables",
|
|
"Always have max Consumables, you must have collected the consumables first.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Longer Deku Flower Glide",
|
|
"gCheats.LongerFlowerGlide",
|
|
"Allows Deku Link to glide longer, no longer dropping after a certain distance.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterLongerFlowerGlide(); } },
|
|
{ "No Clip", "gCheats.NoClip", "Allows Link to phase through collision.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Unbreakable Razor Sword", "gCheats.UnbreakableRazorSword",
|
|
"Allows to Razor Sword to be used indefinitely without dulling its blade.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Unrestricted Items", "gCheats.UnrestrictedItems", "Allows all Forms to use all Items.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Moon Jump on L",
|
|
"gCheats.MoonJumpOnL",
|
|
"Holding L makes you float into the air.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterMoonJumpOnL(); } },
|
|
{ "Elegy of Emptiness Anywhere", "gCheats.ElegyAnywhere", "Allows Elegy of Emptiness outside of Ikana",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Stop Time in Dungeons",
|
|
"gCheats.TempleTimeStop",
|
|
"Stops time from advancing in selected areas. Requires a room change to update.\n\n"
|
|
"- Off: Vanilla behaviour.\n"
|
|
"- Temples: Stops time in Woodfall, Snowhead, Great Bay, and Stone Tower Temples.\n"
|
|
"- Temples + Mini Dungeons: In addition to the above temples, stops time in both Spider "
|
|
"Houses, Pirate's Fortress, Beneath the Well, Ancient Castle of Ikana, and Secret Shrine.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = timeStopOptions } } } } });
|
|
// Gameplay Enhancements
|
|
enhancementsSidebar.push_back(
|
|
{ "Gameplay",
|
|
3,
|
|
{ { { .widgetName = "Player", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Fast Deku Flower Launch",
|
|
"gEnhancements.Player.FastFlowerLaunch",
|
|
"Speeds up the time it takes to be able to get maximum height from launching out of a deku flower",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
([](widgetInfo& info) { RegisterFastFlowerLaunch(); }) },
|
|
{ "Instant Putaway", "gEnhancements.Player.InstantPutaway",
|
|
"Allows Link to instantly puts away held item without waiting.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Fierce Deity Putaway", "gEnhancements.Player.FierceDeityPutaway",
|
|
"Allows Fierce Deity Link to put away his sword.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Climb speed",
|
|
"gEnhancements.Player.ClimbSpeed",
|
|
"Increases the speed at which Link climbs vines and ladders.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 5, 1 } },
|
|
{ "Dpad Equips", "gEnhancements.Dpad.DpadEquips", "Allows you to equip items to your d-pad",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Always Win Doggy Race",
|
|
"gEnhancements.Minigames.AlwaysWinDoggyRace",
|
|
"Makes the Doggy Race easier to win.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = alwaysWinDoggyraceOptions } },
|
|
{ "Milk Run Reward Options",
|
|
"gEnhancements.Minigames.CremiaHugs",
|
|
"Choose what reward you get for winning the Milk Run minigame after the first time. \n"
|
|
"-Vanilla: Reward is Random\n"
|
|
"-Hug: Get the hugging cutscene\n"
|
|
"-Rupee: Get the rupee reward",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = cremiaRewardOptions } },
|
|
{ "Swordsman School Winning Score",
|
|
"gEnhancements.Minigames.SwordsmanSchoolScore",
|
|
"Sets the score required to win the Swordsman School.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 30, 30 } },
|
|
{ "Fast Magic Arrow Equip Animation", "gEnhancements.Equipment.MagicArrowEquipSpeed",
|
|
"Removes the animation for equipping Magic Arrows.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Instant Fin Boomerangs Recall", "gEnhancements.PlayerActions.InstantRecall",
|
|
"Pressing B will instantly recall the fin boomerang back to Zora Link after they are thrown.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Two-Handed Sword Spin Attack", "gEnhancements.Equipment.TwoHandedSwordSpinAttack",
|
|
"Enables magic spin attacks for the Fierce Deity Sword and Great Fairy's Sword.",
|
|
WIDGET_CVAR_CHECKBOX } },
|
|
{ { .widgetName = "Modes", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Play as Kafei", "gModes.PlayAsKafei", "Requires scene reload to take effect.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Time Moves when you Move",
|
|
"gModes.TimeMovesWhenYouMove",
|
|
"Time only moves when Link is not standing still.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
([](widgetInfo& info) { RegisterTimeMovesWhenYouMove(); }) },
|
|
{ "Mirrored World",
|
|
"gModes.MirroredWorld.Mode",
|
|
"Mirrors the world horizontally.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
([](widgetInfo& info) {
|
|
if (CVarGetInteger("gModes.MirroredWorld.Mode", 0)) {
|
|
CVarSetInteger("gModes.MirroredWorld.State", 1);
|
|
} else {
|
|
CVarClear("gModes.MirroredWorld.State");
|
|
}
|
|
}) } },
|
|
{ { .widgetName = "Saving", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Persistent Owl Saves", "gEnhancements.Saving.PersistentOwlSaves",
|
|
"Continuing a save will not remove the owl save. Playing Song of "
|
|
"Time, allowing the moon to crash or finishing the "
|
|
"game will remove the owl save and become the new last save.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Pause Menu Save", "gEnhancements.Saving.PauseSave",
|
|
"Re-introduce the pause menu save system. Pressing B in the pause menu will give you the "
|
|
"option to create a persistent Owl Save from your current location.\n\nWhen loading back "
|
|
"into the game, you will be placed either at the entrance of the dungeon you saved in, or "
|
|
"in South Clock Town.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Autosave",
|
|
"gEnhancements.Saving.Autosave",
|
|
"Automatically create a persistent Owl Save on the chosen interval.\n\nWhen loading "
|
|
"back into the game, you will be placed either at the entrance of the dungeon you "
|
|
"saved in, or in South Clock Town.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
([](widgetInfo& info) { RegisterAutosave(); }) },
|
|
{ "Autosave Interval: %d minutes",
|
|
"gEnhancements.Saving.AutosaveInterval",
|
|
"Sets the interval between Autosaves.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 60, 5 },
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
if (disabledMap.at(DISABLE_FOR_AUTO_SAVE_OFF).active) {
|
|
info.activeDisables.push_back(DISABLE_FOR_AUTO_SAVE_OFF);
|
|
}
|
|
} },
|
|
{ .widgetName = "Time Cycle", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Do not reset Bottle content", "gEnhancements.Cycle.DoNotResetBottleContent",
|
|
"Playing the Song Of Time will not reset the bottles' content.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Do not reset Consumables", "gEnhancements.Cycle.DoNotResetConsumables",
|
|
"Playing the Song Of Time will not reset the consumables.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Do not reset Razor Sword", "gEnhancements.Cycle.DoNotResetRazorSword",
|
|
"Playing the Song Of Time will not reset the Sword back to Kokiri Sword.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Do not reset Rupees", "gEnhancements.Cycle.DoNotResetRupees",
|
|
"Playing the Song Of Time will not reset the your rupees.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Do not reset Time Speed", "gEnhancements.Cycle.DoNotResetTimeSpeed",
|
|
"Playing the Song Of Time will not reset the current time speed set by Inverted Song of Time.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{
|
|
.widgetName = "Keep Express Mail",
|
|
.widgetCVar = "gEnhancements.Cycle.KeepExpressMail",
|
|
.widgetTooltip = "Allows the player to keep the Express Mail in their inventory after delivering it "
|
|
"the first time, so that both deliveries can be done within one cycle",
|
|
.widgetType = WIDGET_CVAR_CHECKBOX,
|
|
.widgetCallback = [](widgetInfo& info) { RegisterKeepExpressMail(); },
|
|
},
|
|
{ .widgetName = "Unstable",
|
|
.widgetType = WIDGET_SEPARATOR_TEXT,
|
|
.widgetOptions = { .color = UIWidgets::Colors::Yellow } },
|
|
{ "Disable Save Delay", "gEnhancements.Saving.DisableSaveDelay",
|
|
"Removes the arbitrary 2 second timer for saving from the original game. This is known to "
|
|
"cause issues when attempting the 0th Day Glitch",
|
|
WIDGET_CVAR_CHECKBOX } } } });
|
|
// Graphics Enhancements
|
|
enhancementsSidebar.push_back(
|
|
{ "Graphics",
|
|
3,
|
|
{ {
|
|
{ .widgetName = "Clock", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Clock Type",
|
|
"gEnhancements.Graphics.ClockType",
|
|
"Swaps between Graphical and Text only Clock types.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = clockTypeOptions } },
|
|
{ "24 Hours Clock", "gEnhancements.Graphics.24HoursClock", "Changes from a 12 Hour to a 24 Hour Clock",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ .widgetName = "Mods", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ .widgetName = "Use Alternate Assets",
|
|
.widgetCVar = "gEnhancements.Mods.AlternateAssets",
|
|
.widgetTooltip = "Toggle between standard assets and alternate assets. Usually mods will indicate if "
|
|
"this setting has to be used or not.",
|
|
.widgetType = WIDGET_CVAR_CHECKBOX },
|
|
{ .widgetName = "Motion Blur", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Motion Blur Mode",
|
|
"gEnhancements.Graphics.MotionBlur.Mode",
|
|
"Selects the Mode for Motion Blur.",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ .comboBoxOptions = motionBlurOptions, .labelPosition = UIWidgets::LabelPosition::None } },
|
|
{ "Interpolate",
|
|
"gEnhancements.Graphics.MotionBlur.Interpolate",
|
|
"Change motion blur capture to also happen on interpolated frames instead of only on game frames.\n"
|
|
"This notably reduces the overall motion blur strength but smooths out the trails.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_MOTION_BLUR_MODE).value == 1; } },
|
|
{ "On/Off",
|
|
"",
|
|
"Enables Motion Blur.",
|
|
WIDGET_CHECKBOX,
|
|
{ .valuePointer = (bool*)&R_MOTION_BLUR_ENABLED },
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
info.widgetOptions.valuePointer = (bool*)&R_MOTION_BLUR_ENABLED;
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_MOTION_BLUR_MODE).value != 0;
|
|
} },
|
|
{ "Strength",
|
|
"gEnhancements.Graphics.MotionBlur.Strength",
|
|
"Motion Blur strength.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 0, 255, 180 },
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_MOTION_BLUR_MODE).value != 2; } },
|
|
{ "Strength",
|
|
"",
|
|
"Motion Blur strength.",
|
|
WIDGET_SLIDER_INT,
|
|
{ 0, 255, 180, {}, &motionBlurStrength },
|
|
[](widgetInfo& info) { R_MOTION_BLUR_ALPHA = motionBlurStrength; },
|
|
[](widgetInfo& info) {
|
|
motionBlurStrength = R_MOTION_BLUR_ALPHA;
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_MOTION_BLUR_MODE).value != 0 ||
|
|
disabledMap.at(DISABLE_FOR_MOTION_BLUR_OFF).active;
|
|
} },
|
|
{ .widgetName = "Other", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "3D Item Drops",
|
|
"gEnhancements.Graphics.3DItemDrops",
|
|
"Makes item drops 3D",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { Register3DItemDrops(); } },
|
|
{ "Authentic Logo", "gEnhancements.Graphics.AuthenticLogo",
|
|
"Hide the game version and build details and display the authentic "
|
|
"model and texture on the boot logo start screen",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Bow Reticle", "gEnhancements.Graphics.BowReticle", "Gives the bow a reticle when you draw an arrow.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Disable Black Bar Letterboxes", "gEnhancements.Graphics.DisableBlackBars",
|
|
"Disables Black Bar Letterboxes during cutscenes and Z-targeting\nNote: there may be "
|
|
"minor visual glitches that were covered up by the black bars\nPlease disable this "
|
|
"setting before reporting a bug.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ .widgetName = "Unstable",
|
|
.widgetType = WIDGET_SEPARATOR_TEXT,
|
|
.widgetOptions = { .color = UIWidgets::Colors::Yellow } },
|
|
{ "Disable Scene Geometry Distance Check",
|
|
"gEnhancements.Graphics.DisableSceneGeometryDistanceCheck",
|
|
"Disables the distance check for scene geometry, allowing it to be drawn no matter how far "
|
|
"away it is from the player. This may have unintended side effects.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { GfxPatcher_ApplyGeometryIssuePatches(); } },
|
|
{ "Widescreen Actor Culling", "gEnhancements.Graphics.ActorCullingAccountsForWidescreen",
|
|
"Adjusts the culling planes to account for widescreen resolutions. "
|
|
"This may have unintended side effects.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{
|
|
"Increase Actor Draw Distance: %dx",
|
|
"gEnhancements.Graphics.IncreaseActorDrawDistance",
|
|
"Increase the range in which Actors are drawn. This may have unintended side effects.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 5, 1 },
|
|
},
|
|
} } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Items/Songs",
|
|
3,
|
|
{ // Mask Enhancements
|
|
{ { .widgetName = "Masks", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Blast Mask has Powder Keg Force", "gEnhancements.Masks.BlastMaskKeg",
|
|
"Blast Mask can also destroy objects only the Powder Keg can.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Fast Transformation", "gEnhancements.Masks.FastTransformation",
|
|
"Removes the delay when using transformation masks.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Fierce Deity's Mask Anywhere", "gEnhancements.Masks.FierceDeitysAnywhere",
|
|
"Allow using Fierce Deity's mask outside of boss rooms.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Persistent Bunny Hood",
|
|
"gEnhancements.Masks.PersistentBunnyHood.Enabled",
|
|
"Permanently toggle a speed boost from the bunny hood by pressing "
|
|
"'A' on it in the mask menu.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { UpdatePersistentMasksState(); } },
|
|
{ "No Blast Mask Cooldown", "gEnhancements.Masks.NoBlastMaskCooldown",
|
|
"Eliminates the Cooldown between Blast Mask usage.", WIDGET_CVAR_CHECKBOX } },
|
|
// Song Enhancements
|
|
{ { .widgetName = "Ocarina", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Enable Sun's Song", "gEnhancements.Songs.EnableSunsSong",
|
|
"Enables the partially implemented Sun's Song. RIGHT-DOWN-UP-RIGHT-DOWN-UP to play it. "
|
|
"This song will make time move very fast until either Link moves to a different scene, "
|
|
"or when the time switches to a new time period.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Dpad Ocarina", "gEnhancements.Playback.DpadOcarina", "Enables using the Dpad for Ocarina playback.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Pause Owl Warp", "gEnhancements.Songs.PauseOwlWarp",
|
|
"Allows the player to use the pause menu map to owl warp instead of "
|
|
"having to play the Song of Soaring.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Zora Eggs For Bossa Nova",
|
|
"gEnhancements.Songs.ZoraEggCount",
|
|
"The number of eggs required to unlock new wave bossa nova.",
|
|
WIDGET_CVAR_SLIDER_INT,
|
|
{ 1, 7, 7 } },
|
|
{ "Prevent Dropped Ocarina Inputs", "gEnhancements.Playback.NoDropOcarinaInput",
|
|
"Prevent dropping inputs when playing the ocarina quickly.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip Scarecrow Song", "gEnhancements.Playback.SkipScarecrowSong",
|
|
"Pierre appears when the Ocarina is pulled out.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Faster Song Playback",
|
|
"gEnhancements.Songs.FasterSongPlayback",
|
|
"Speeds up the playback of songs.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterFasterSongPlayback(); } } } } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Time Savers",
|
|
3,
|
|
{ // Cutscene Skips
|
|
{ { .widgetName = "Cutscenes", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Hide Title Cards", "gEnhancements.Cutscenes.HideTitleCards", "Hides Title Cards when entering areas.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip Entrance Cutscenes", "gEnhancements.Cutscenes.SkipEntranceCutscenes",
|
|
"Skip cutscenes that occur when first entering a new area.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip to File Select", "gEnhancements.Cutscenes.SkipToFileSelect",
|
|
"Skip the opening title sequence and go straight to the file select menu after boot.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip Intro Sequence", "gEnhancements.Cutscenes.SkipIntroSequence",
|
|
"When starting a game you will be taken straight to South Clock Town as Deku Link.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip First Cycle",
|
|
"gEnhancements.Cutscenes.SkipFirstCycle",
|
|
"When starting a game you will be taken straight to South Clock Town as Human Link "
|
|
"with Deku Mask, Ocarina, Song of Time, and Song of Healing.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_INTRO_SKIP_OFF).active; } },
|
|
{ "Skip Story Cutscenes", "gEnhancements.Cutscenes.SkipStoryCutscenes",
|
|
"Disclaimer: This doesn't do much yet, we will be progressively adding more skips over time.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Skip Misc Interactions", "gEnhancements.Cutscenes.SkipMiscInteractions",
|
|
"Disclaimer: This doesn't do much yet, we will be progressively adding more skips over time.",
|
|
WIDGET_CVAR_CHECKBOX } },
|
|
// Dialogue Enhancements
|
|
{ { .widgetName = "Dialogue", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Fast Bank Selection", "gEnhancements.Dialogue.FastBankSelection",
|
|
"Pressing the Z or R buttons while the Deposit/Withdrawal Rupees dialogue is open will set "
|
|
"the Rupees to Links current Rupees or 0 respectively.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Fast Text", "gEnhancements.Dialogue.FastText",
|
|
"Speeds up text rendering, and enables holding of B progress to next message.",
|
|
WIDGET_CVAR_CHECKBOX } } } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Fixes",
|
|
3,
|
|
{ // Fixes
|
|
{ { .widgetName = "Fixes", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Fix Ammo Count Color", "gFixes.FixAmmoCountEnvColor",
|
|
"Fixes a missing gDPSetEnvColor, which causes the ammo count to be "
|
|
"the wrong color prior to obtaining magic or other conditions.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Fix Fierce Deity Z-Target movement", "gEnhancements.Fixes.FierceDeityZTargetMovement",
|
|
"Fixes Fierce Deity movement being choppy when Z-targeting", WIDGET_CVAR_CHECKBOX },
|
|
{ "Fix Hess and Weirdshot Crash", "gEnhancements.Fixes.HessCrash",
|
|
"Fixes a crash that can occur when performing a HESS or Weirdshot.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Fix Text Control Characters", "gEnhancements.Fixes.ControlCharacters",
|
|
"Fixes certain control characters not functioning properly "
|
|
"depending on their position within the text.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Fix Ikana Great Fairy Fountain Color", "gFixes.FixIkanaGreatFairyFountainColor",
|
|
"Fixes a bug that results in the Ikana Great Fairy fountain looking green instead of yellow, this was "
|
|
"fixed in the EU version",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ .widgetName = "Fix Texture overflow OOB",
|
|
.widgetCVar = "gEnhancements.Fixes.FixTexturesOOB",
|
|
.widgetTooltip = "Fixes textures that normally overflow to be patched with the correct size or format",
|
|
.widgetType = WIDGET_CVAR_CHECKBOX,
|
|
.widgetOptions = { .defaultVariant = true },
|
|
.widgetCallback = [](widgetInfo& info) { GfxPatcher_ApplyOverflowTexturePatches(); } } } } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Restorations",
|
|
3,
|
|
{ // Restorations
|
|
{ { .widgetName = "Restorations", .widgetType = WIDGET_SEPARATOR_TEXT },
|
|
{ "Constant Distance Backflips and Sidehops", "gEnhancements.Restorations.ConstantFlipsHops",
|
|
"Backflips and Sidehops travel a constant distance as they did in OoT.", WIDGET_CVAR_CHECKBOX },
|
|
{ "Power Crouch Stab", "gEnhancements.Restorations.PowerCrouchStab",
|
|
"Crouch stabs will use the power of Link's previous melee attack, as is in MM JP 1.0 and OoT.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Side Rolls", "gEnhancements.Restorations.SideRoll", "Restores side rolling from OoT.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Tatl ISG", "gEnhancements.Restorations.TatlISG", "Restores Navi ISG from OoT, but now with Tatl.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Woodfall Mountain Appearance",
|
|
"gEnhancements.Restorations.WoodfallMountainAppearance",
|
|
"Restores the appearance of Woodfall mountain to not look poisoned "
|
|
"when viewed from Termina Field after clearing Woodfall Temple\n\n"
|
|
"Requires a scene reload to take effect",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterWoodfallMountainAppearance(); } } } } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Difficulty Options",
|
|
3,
|
|
{ { { "Disable Takkuri Steal",
|
|
"gEnhancements.Cheats.DisableTakkuriSteal",
|
|
"Prevents the Takkuri from stealing key items like bottles and swords. It may still steal other items.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterDisableTakkuriSteal(); } } } } });
|
|
enhancementsSidebar.push_back({ "HUD Editor",
|
|
1,
|
|
{ // HUD Editor
|
|
{ { "Popout HUD Editor",
|
|
"gWindows.HudEditor",
|
|
"Enables the HUD Editor window, allowing you to modify your HUD",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "HUD Editor" } } } } });
|
|
enhancementsSidebar.push_back(
|
|
{ "Item Tracker",
|
|
1,
|
|
{ // Item Tracker Settings
|
|
{ { "Popout Item Tracker Settings",
|
|
"gWindows.ItemTrackerSettings",
|
|
"",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Item Tracker Settings" } } } } });
|
|
}
|
|
|
|
void AddDevTools() {
|
|
devToolsSidebar.push_back(
|
|
{ "General",
|
|
3,
|
|
{ { { "Popout Menu", "gSettings.Menu.Popout", "Changes the menu display from overlay to windowed.",
|
|
WIDGET_CVAR_CHECKBOX },
|
|
{ "Set Warp Point",
|
|
"",
|
|
"Creates warp point that you can teleport to later",
|
|
WIDGET_BUTTON,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
Player* player = GET_PLAYER(gPlayState);
|
|
|
|
CVarSetInteger(WARP_POINT_CVAR "Entrance", gSaveContext.save.entrance);
|
|
CVarSetInteger(WARP_POINT_CVAR "Room", gPlayState->roomCtx.curRoom.num);
|
|
CVarSetFloat(WARP_POINT_CVAR "X", player->actor.world.pos.x);
|
|
CVarSetFloat(WARP_POINT_CVAR "Y", player->actor.world.pos.y);
|
|
CVarSetFloat(WARP_POINT_CVAR "Z", player->actor.world.pos.z);
|
|
CVarSetFloat(WARP_POINT_CVAR "Rotation", player->actor.shape.rot.y);
|
|
CVarSetInteger(WARP_POINT_CVAR "Saved", 1);
|
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
},
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active; } },
|
|
{ "Scene Room ID",
|
|
"",
|
|
"",
|
|
WIDGET_TEXT,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
u32 sceneId = Entrance_GetSceneIdAbsolute(
|
|
CVarGetInteger(WARP_POINT_CVAR "Entrance", ENTRANCE(SOUTH_CLOCK_TOWN, 0)));
|
|
info.widgetName = fmt::format("{} Room {}", warpPointSceneList[sceneId],
|
|
CVarGetInteger(WARP_POINT_CVAR "Room", 0));
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active ||
|
|
disabledMap.at(DISABLE_FOR_WARP_POINT_NOT_SET).active;
|
|
} },
|
|
{ ICON_FA_TIMES,
|
|
"",
|
|
"Clear warp point",
|
|
WIDGET_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .sameLine = true },
|
|
[](widgetInfo& info) {
|
|
CVarClear(WARP_POINT_CVAR "Entrance");
|
|
CVarClear(WARP_POINT_CVAR "Room");
|
|
CVarClear(WARP_POINT_CVAR "X");
|
|
CVarClear(WARP_POINT_CVAR "Y");
|
|
CVarClear(WARP_POINT_CVAR "Z");
|
|
CVarClear(WARP_POINT_CVAR "Rotation");
|
|
CVarClear(WARP_POINT_CVAR "Saved");
|
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
|
},
|
|
[](widgetInfo& info) {
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active ||
|
|
disabledMap.at(DISABLE_FOR_WARP_POINT_NOT_SET).active;
|
|
} },
|
|
{ "Warp",
|
|
"",
|
|
"Teleport to the set warp point",
|
|
WIDGET_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .sameLine = true },
|
|
[](widgetInfo& info) { Warp(); },
|
|
[](widgetInfo& info) {
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active ||
|
|
disabledMap.at(DISABLE_FOR_WARP_POINT_NOT_SET).active;
|
|
} } },
|
|
{ { "Debug Mode",
|
|
"gDeveloperTools.DebugEnabled",
|
|
"Enables Debug Mode, allowing you to select maps with L + R + Z.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) {
|
|
if (!CVarGetInteger("gDeveloperTools.DebugEnabled", 0)) {
|
|
CVarClear("gDeveloperTools.DebugSaveFileMode");
|
|
CVarClear("gDeveloperTools.PreventActorUpdate");
|
|
CVarClear("gDeveloperTools.PreventActorDraw");
|
|
CVarClear("gDeveloperTools.PreventActorInit");
|
|
CVarClear("gDeveloperTools.DisableObjectDependency");
|
|
if (gPlayState != NULL) {
|
|
gPlayState->frameAdvCtx.enabled = false;
|
|
}
|
|
RegisterDebugSaveCreate();
|
|
RegisterPreventActorUpdateHooks();
|
|
RegisterPreventActorDrawHooks();
|
|
RegisterPreventActorInitHooks();
|
|
}
|
|
} },
|
|
{ "Better Map Select",
|
|
"gDeveloperTools.BetterMapSelect.Enabled",
|
|
"Overrides the original map select with a translated, more user-friendly version.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Debug Save File Mode",
|
|
"gDeveloperTools.DebugSaveFileMode",
|
|
"Change the behavior of creating saves while debug mode is enabled:\n\n"
|
|
"- Empty Save: The default 3 heart save file in first cycle\n"
|
|
"- Vanilla Debug Save: Uses the title screen save info (8 hearts, all items and masks)\n"
|
|
"- 100\% Save: All items, equipment, mask, quest status and bombers notebook complete",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ 0, 0, 0, debugSaveOptions },
|
|
[](widgetInfo& info) { RegisterDebugSaveCreate(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Prevent Actor Update",
|
|
"gDeveloperTools.PreventActorUpdate",
|
|
"Prevents Actors from updating.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterPreventActorUpdateHooks(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Prevent Actor Draw",
|
|
"gDeveloperTools.PreventActorDraw",
|
|
"Prevents Actors from drawing.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterPreventActorDrawHooks(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Prevent Actor Init",
|
|
"gDeveloperTools.PreventActorInit",
|
|
"Prevents Actors from initializing.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
[](widgetInfo& info) { RegisterPreventActorInitHooks(); },
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Disable Object Dependency",
|
|
"gDeveloperTools.DisableObjectDependency",
|
|
"Disables dependencies when loading objects.",
|
|
WIDGET_CVAR_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Log Level",
|
|
"gDeveloperTools.LogLevel",
|
|
"The log level determines which messages are printed to the "
|
|
"console. This does not affect the log file output",
|
|
WIDGET_CVAR_COMBOBOX,
|
|
{ 0, 0, 0, logLevels },
|
|
[](widgetInfo& info) {
|
|
Ship::Context::GetInstance()->GetLogger()->set_level(
|
|
(spdlog::level::level_enum)CVarGetInteger("gDeveloperTools.LogLevel", 1));
|
|
},
|
|
[](widgetInfo& info) { info.isHidden = disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active; } },
|
|
{ "Frame Advance",
|
|
"",
|
|
"This allows you to advance through the game one frame at a time on command. "
|
|
"To advance a frame, hold Z and tap R on the second controller. Holding Z "
|
|
"and R will advance a frame every half second. You can also use the buttons below.",
|
|
WIDGET_CHECKBOX,
|
|
{},
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_NULL_PLAY_STATE).active ||
|
|
disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active;
|
|
if (gPlayState != nullptr) {
|
|
info.widgetOptions.valuePointer = (bool*)&gPlayState->frameAdvCtx.enabled;
|
|
} else {
|
|
info.widgetOptions.valuePointer = (bool*)nullptr;
|
|
}
|
|
} },
|
|
{ "Advance 1",
|
|
"",
|
|
"Advance 1 frame.",
|
|
WIDGET_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline },
|
|
[](widgetInfo& info) { CVarSetInteger("gDeveloperTools.FrameAdvanceTick", 1); },
|
|
[](widgetInfo& info) {
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_FRAME_ADVANCE_OFF).active ||
|
|
disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active;
|
|
} },
|
|
{ "Advance (Hold)",
|
|
"",
|
|
"Advance frames while the button is held.",
|
|
WIDGET_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .sameLine = true },
|
|
nullptr,
|
|
[](widgetInfo& info) {
|
|
info.isHidden = disabledMap.at(DISABLE_FOR_FRAME_ADVANCE_OFF).active ||
|
|
disabledMap.at(DISABLE_FOR_DEBUG_MODE_OFF).active;
|
|
},
|
|
[](widgetInfo& info) {
|
|
if (ImGui::IsItemActive()) {
|
|
CVarSetInteger("gDeveloperTools.FrameAdvanceTick", 1);
|
|
}
|
|
} } } } });
|
|
// dev tools windows
|
|
devToolsSidebar.push_back({ "Collision Viewer",
|
|
1,
|
|
{ { { "Popout Collision Viewer",
|
|
"gWindows.CollisionViewer",
|
|
"Makes collision visible on screen",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Collision Viewer" } } } } });
|
|
devToolsSidebar.push_back(
|
|
{ "Stats",
|
|
1,
|
|
{ { { "Popout Stats",
|
|
"gOpenWindows.Stats",
|
|
"Shows the stats window, with your FPS and frametimes, and the OS you're playing on",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Stats" } } } } });
|
|
devToolsSidebar.push_back(
|
|
{ "Console",
|
|
1,
|
|
{ { { "Popout Console",
|
|
"gOpenWindows.Console",
|
|
"Enables the console window, allowing you to input commands. Type help for some examples",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Console" } } } } });
|
|
devToolsSidebar.push_back(
|
|
{ "Gfx Debugger",
|
|
1,
|
|
{ { { "Popout Gfx Debugger",
|
|
"gOpenWindows.GfxDebugger",
|
|
"Enables the Gfx Debugger window, allowing you to input commands, type help for some examples",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "GfxDebuggerWindow" } } } } });
|
|
devToolsSidebar.push_back({ "Save Editor",
|
|
1,
|
|
{ { { "Popout Save Editor",
|
|
"gWindows.SaveEditor",
|
|
"Enables the Save Editor window, allowing you to edit your save file",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Save Editor" } } } } });
|
|
devToolsSidebar.push_back({ "Actor Viewer",
|
|
1,
|
|
{ { { "Popout Actor Viewer",
|
|
"gWindows.ActorViewer",
|
|
"Enables the Actor Viewer window, allowing you to view actors in the world.",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Actor Viewer" } } } } });
|
|
devToolsSidebar.push_back({ "Event Log",
|
|
1,
|
|
{ { { "Popout Event Log",
|
|
"gWindows.EventLog",
|
|
"Enables the event log window",
|
|
WIDGET_WINDOW_BUTTON,
|
|
{ .size = UIWidgets::Sizes::Inline, .windowName = "Event Log" } } } } });
|
|
}
|
|
|
|
void SearchMenuGetItem(widgetInfo& widget) {
|
|
disabledTempTooltip = "This setting is disabled because: \n\n";
|
|
disabledValue = false;
|
|
disabledTooltip = " ";
|
|
|
|
if (widget.preFunc != nullptr) {
|
|
widget.activeDisables.clear();
|
|
widget.isHidden = false;
|
|
widget.preFunc(widget);
|
|
if (widget.isHidden) {
|
|
return;
|
|
}
|
|
if (!widget.activeDisables.empty()) {
|
|
disabledValue = true;
|
|
for (auto option : widget.activeDisables) {
|
|
disabledTempTooltip += std::string("- ") + disabledMap.at(option).reason + std::string("\n");
|
|
}
|
|
disabledTooltip = disabledTempTooltip.c_str();
|
|
}
|
|
}
|
|
|
|
if (widget.widgetOptions.sameLine) {
|
|
ImGui::SameLine();
|
|
}
|
|
|
|
try {
|
|
switch (widget.widgetType) {
|
|
case WIDGET_CHECKBOX: {
|
|
bool* pointer = std::get<bool*>(widget.widgetOptions.valuePointer);
|
|
if (pointer == nullptr) {
|
|
SPDLOG_ERROR("Checkbox Widget requires a value pointer, currently nullptr");
|
|
assert(false);
|
|
return;
|
|
}
|
|
if (UIWidgets::Checkbox(
|
|
widget.widgetName.c_str(), pointer,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.labelPosition = widget.widgetOptions.labelPosition == UIWidgets::LabelPosition::None
|
|
? UIWidgets::LabelPosition::Near
|
|
: widget.widgetOptions.labelPosition })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
}
|
|
} break;
|
|
case WIDGET_CVAR_CHECKBOX:
|
|
if (UIWidgets::CVarCheckbox(
|
|
widget.widgetName.c_str(), widget.widgetCVar,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.defaultValue = static_cast<bool>(std::get<int32_t>(widget.widgetOptions.defaultVariant)),
|
|
.labelPosition = widget.widgetOptions.labelPosition == UIWidgets::LabelPosition::None
|
|
? UIWidgets::LabelPosition::Near
|
|
: widget.widgetOptions.labelPosition })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
};
|
|
break;
|
|
case WIDGET_AUDIO_BACKEND: {
|
|
auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetAudioBackend();
|
|
if (UIWidgets::Combobox(
|
|
"Audio API", ¤tAudioBackend, audioBackendsMap,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled =
|
|
Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1,
|
|
.disabledTooltip = "Only one audio API is available on this platform." })) {
|
|
Ship::Context::GetInstance()->GetAudio()->SetAudioBackend(currentAudioBackend);
|
|
}
|
|
} break;
|
|
case WIDGET_VIDEO_BACKEND: {
|
|
if (UIWidgets::Combobox(
|
|
"Renderer API (Needs reload)", &configWindowBackend, availableWindowBackendsMap,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = availableWindowBackends->size() <= 1,
|
|
.disabledTooltip = "Only one renderer API is available on this platform." })) {
|
|
Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id",
|
|
static_cast<int32_t>(configWindowBackend));
|
|
Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
|
|
windowBackendsMap.at(configWindowBackend));
|
|
Ship::Context::GetInstance()->GetConfig()->Save();
|
|
UpdateWindowBackendObjects();
|
|
}
|
|
} break;
|
|
case WIDGET_SEPARATOR:
|
|
ImGui::Separator();
|
|
break;
|
|
case WIDGET_SEPARATOR_TEXT:
|
|
if (widget.widgetOptions.color != COLOR_NONE) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, widget.widgetOptions.color);
|
|
}
|
|
ImGui::SeparatorText(widget.widgetName.c_str());
|
|
if (widget.widgetOptions.color != COLOR_NONE) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
break;
|
|
case WIDGET_TEXT:
|
|
ImGui::AlignTextToFramePadding();
|
|
ImGui::Text(widget.widgetName.c_str());
|
|
break;
|
|
case WIDGET_COMBOBOX: {
|
|
int32_t* pointer = std::get<int32_t*>(widget.widgetOptions.valuePointer);
|
|
if (pointer == nullptr) {
|
|
SPDLOG_ERROR("Combobox Widget requires a value pointer, currently nullptr");
|
|
assert(false);
|
|
return;
|
|
}
|
|
if (UIWidgets::Combobox(
|
|
widget.widgetName.c_str(), pointer, widget.widgetOptions.comboBoxOptions,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.labelPosition = widget.widgetOptions.labelPosition == UIWidgets::LabelPosition::None
|
|
? UIWidgets::LabelPosition::Above
|
|
: widget.widgetOptions.labelPosition })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
};
|
|
} break;
|
|
case WIDGET_CVAR_COMBOBOX:
|
|
if (UIWidgets::CVarCombobox(
|
|
widget.widgetName.c_str(), widget.widgetCVar, widget.widgetOptions.comboBoxOptions,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.defaultIndex = static_cast<uint32_t>(std::get<int32_t>(widget.widgetOptions.defaultVariant)),
|
|
.labelPosition = widget.widgetOptions.labelPosition == UIWidgets::LabelPosition::None
|
|
? UIWidgets::LabelPosition::Above
|
|
: widget.widgetOptions.labelPosition })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
}
|
|
break;
|
|
case WIDGET_SLIDER_INT: {
|
|
int32_t* pointer = std::get<int32_t*>(widget.widgetOptions.valuePointer);
|
|
if (pointer == nullptr) {
|
|
SPDLOG_ERROR("int32 Slider Widget requires a value pointer, currently nullptr");
|
|
assert(false);
|
|
return;
|
|
}
|
|
if (UIWidgets::SliderInt(widget.widgetName.c_str(), pointer,
|
|
std::get<int32_t>(widget.widgetOptions.min),
|
|
std::get<int32_t>(widget.widgetOptions.max),
|
|
{
|
|
.color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
})) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
};
|
|
} break;
|
|
case WIDGET_SLIDER_FLOAT: {
|
|
float floatMin = (std::get<float>(widget.widgetOptions.min) / 100);
|
|
float floatMax = (std::get<float>(widget.widgetOptions.max) / 100);
|
|
float floatDefault = (std::get<float>(widget.widgetOptions.defaultVariant) / 100);
|
|
float* pointer = std::get<float*>(widget.widgetOptions.valuePointer);
|
|
|
|
if (pointer == nullptr) {
|
|
SPDLOG_ERROR("float Slider Widget requires a value pointer, currently nullptr");
|
|
assert(false);
|
|
return;
|
|
}
|
|
if (UIWidgets::SliderFloat(widget.widgetName.c_str(), pointer, floatMin, floatMax,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.showButtons = widget.widgetOptions.showButtons,
|
|
.format = widget.widgetOptions.format,
|
|
.isPercentage = widget.widgetOptions.isPercentage })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
}
|
|
} break;
|
|
case WIDGET_CVAR_SLIDER_INT:
|
|
if (UIWidgets::CVarSliderInt(widget.widgetName.c_str(), widget.widgetCVar,
|
|
std::get<int32_t>(widget.widgetOptions.min),
|
|
std::get<int32_t>(widget.widgetOptions.max),
|
|
std::get<int32_t>(widget.widgetOptions.defaultVariant),
|
|
{
|
|
.color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
})) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
};
|
|
break;
|
|
case WIDGET_CVAR_SLIDER_FLOAT: {
|
|
float floatMin = (std::get<float>(widget.widgetOptions.min) / 100);
|
|
float floatMax = (std::get<float>(widget.widgetOptions.max) / 100);
|
|
float floatDefault = (std::get<float>(widget.widgetOptions.defaultVariant) / 100);
|
|
if (UIWidgets::CVarSliderFloat(widget.widgetName.c_str(), widget.widgetCVar, floatMin, floatMax,
|
|
floatDefault,
|
|
{ .color = menuTheme[menuThemeIndex],
|
|
.tooltip = widget.widgetTooltip,
|
|
.disabled = disabledValue,
|
|
.disabledTooltip = disabledTooltip,
|
|
.showButtons = widget.widgetOptions.showButtons,
|
|
.format = widget.widgetOptions.format,
|
|
.isPercentage = widget.widgetOptions.isPercentage })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
}
|
|
} break;
|
|
case WIDGET_BUTTON:
|
|
if (UIWidgets::Button(widget.widgetName.c_str(),
|
|
{ menuTheme[menuThemeIndex], widget.widgetOptions.size, widget.widgetTooltip,
|
|
disabledValue, disabledTooltip })) {
|
|
if (widget.widgetCallback != nullptr) {
|
|
widget.widgetCallback(widget);
|
|
}
|
|
}
|
|
break;
|
|
case WIDGET_WINDOW_BUTTON: {
|
|
if (widget.widgetOptions.windowName == nullptr || widget.widgetOptions.windowName[0] == '\0') {
|
|
std::string msg =
|
|
fmt::format("Error drawing window contents for {}: windowName not defined", widget.widgetName);
|
|
SPDLOG_ERROR(msg.c_str());
|
|
break;
|
|
}
|
|
auto window =
|
|
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow(widget.widgetOptions.windowName);
|
|
if (!window) {
|
|
std::string msg = fmt::format("Error drawing window contents: windowName {} does not exist",
|
|
widget.widgetOptions.windowName);
|
|
SPDLOG_ERROR(msg.c_str());
|
|
break;
|
|
}
|
|
UIWidgets::WindowButton(widget.widgetName.c_str(), widget.widgetCVar, window,
|
|
{ .size = widget.widgetOptions.size, .tooltip = widget.widgetTooltip });
|
|
if (!window->IsVisible()) {
|
|
window->DrawElement();
|
|
}
|
|
} break;
|
|
case WIDGET_SEARCH: {
|
|
if (ImGui::Button("Clear")) {
|
|
menuSearch.Clear();
|
|
}
|
|
ImGui::SameLine();
|
|
if (CVarGetInteger("gSettings.SearchAutofocus", 0) &&
|
|
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() &&
|
|
!ImGui::IsMouseClicked(0)) {
|
|
ImGui::SetKeyboardFocusHere(0);
|
|
}
|
|
menuSearch.Draw();
|
|
std::string menuSearchText(menuSearch.InputBuf);
|
|
|
|
if (menuSearchText == "") {
|
|
ImGui::Text("Start typing to see results.");
|
|
return;
|
|
}
|
|
ImGui::BeginChild("Search Results");
|
|
for (auto& [menuLabel, menuSidebar, cvar] : menuEntries) {
|
|
for (auto& sidebar : menuSidebar) {
|
|
for (auto& widgets : sidebar.columnWidgets) {
|
|
int column = 1;
|
|
for (auto& info : widgets) {
|
|
if (info.widgetType == WIDGET_SEARCH || info.widgetType == WIDGET_SEPARATOR ||
|
|
info.widgetType == WIDGET_SEPARATOR_TEXT || info.isHidden) {
|
|
continue;
|
|
}
|
|
std::string widgetStr =
|
|
std::string(info.widgetName) +
|
|
std::string(info.widgetTooltip != NULL ? info.widgetTooltip : "");
|
|
std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(),
|
|
::tolower);
|
|
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '),
|
|
menuSearchText.end());
|
|
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
|
|
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
|
|
if (widgetStr.find(menuSearchText) != std::string::npos) {
|
|
SearchMenuGetItem(info);
|
|
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::Colors::Gray);
|
|
std::string origin =
|
|
fmt::format(" ({} -> {}, Clmn {})", menuLabel, sidebar.label, column);
|
|
ImGui::Text("%s", origin.c_str());
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
column++;
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
if (widget.postFunc != nullptr) {
|
|
widget.postFunc(widget);
|
|
}
|
|
} catch (const std::bad_variant_access& e) {
|
|
SPDLOG_ERROR("Failed to draw menu item \"{}\" due to: {}", widget.widgetName, e.what());
|
|
assert(false);
|
|
}
|
|
}
|
|
} // namespace BenGui
|