options_menu: implemented greyed out options and localisation

This commit is contained in:
Hyper 2024-11-21 01:34:10 +00:00
parent 49b6b0e262
commit 1f7f093e22
12 changed files with 300 additions and 181 deletions

View file

@ -36,6 +36,7 @@
#include "SWA/CSD/CsdProject.h" #include "SWA/CSD/CsdProject.h"
#include "SWA/CSD/CsdTexListMirage.h" #include "SWA/CSD/CsdTexListMirage.h"
#include "SWA/CSD/GameObjectCSD.h" #include "SWA/CSD/GameObjectCSD.h"
#include "SWA/HUD/Pause/HudPause.h"
#include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h" #include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h" #include "SWA/Player/Character/EvilSonic/EvilSonic.h"
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.h" #include "SWA/Player/Character/EvilSonic/EvilSonicContext.h"

View file

@ -0,0 +1,55 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
enum EActionType : uint32_t
{
eActionType_Undefined,
eActionType_Status,
eActionType_Return,
eActionType_Inventory,
eActionType_Skills,
eActionType_Lab,
eActionType_Wait,
eActionType_Restart = 8,
eActionType_Continue
};
enum EMenuType : uint32_t
{
eMenuType_WorldMap,
eMenuType_Village,
eMenuType_Stage,
eMenuType_Hub,
eMenuType_Misc
};
enum EStatusType : uint32_t
{
eStatusType_Idle,
eStatusType_Accept,
eStatusType_Decline
};
enum ETransitionType : uint32_t
{
eTransitionType_Undefined,
eTransitionType_Quit = 2,
eTransitionType_Dialog = 5,
eTransitionType_Hide,
eTransitionType_Abort,
eTransitionType_SubMenu
};
class CHudPause : public CGameObject
{
public:
SWA_INSERT_PADDING(0xC8);
be<EActionType> m_Action;
be<EMenuType> m_Menu;
be<EStatusType> m_Status;
be<ETransitionType> m_Transition;
};
}

View file

@ -15,7 +15,7 @@ PPC_FUNC(sub_822C1130)
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
Window::Update(); Window::Update();
audio_patches::Update(g_deltaTime); AudioPatches::Update(g_deltaTime);
__imp__sub_822C1130(ctx, base); __imp__sub_822C1130(ctx, base);
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "config_detail.h" #include <cfg/config_detail.h>
#include "config_locale.h" #include <locale/config_locale.h>
#include "exports.h" #include <exports.h>
class Config class Config
{ {

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "config_detail.h" #include <cfg/config_detail.h>
#define CONFIG_DEFINE_LOCALE(name) \ #define CONFIG_DEFINE_LOCALE(name) \
inline static std::unordered_map<ELanguage, std::tuple<std::string, std::string>> g_##name##_locale = inline static std::unordered_map<ELanguage, std::tuple<std::string, std::string>> g_##name##_locale =

View file

@ -0,0 +1,65 @@
#pragma once
#include <cfg/config.h>
inline static std::string g_localeMissing = "<missing string>";
inline static std::unordered_map<std::string, std::unordered_map<ELanguage, std::string>> g_locale
{
{
"Options_Category_System",
{
{ ELanguage::English, "SYSTEM" }
}
},
{
"Options_Category_Input",
{
{ ELanguage::English, "INPUT" }
}
},
{
"Options_Category_Audio",
{
{ ELanguage::English, "AUDIO" }
}
},
{
"Options_Category_Video",
{
{ ELanguage::English, "VIDEO" }
}
},
{
"Options_Value_Max",
{
{ ELanguage::English, "MAX" }
}
},
{
"Options_Desc_NotAvailable",
{
{ ELanguage::English, "This option is not available at this location." }
}
}
};
static std::string& Localise(const char* key)
{
if (!g_locale.count(key))
return g_localeMissing;
if (!g_locale[key].count(Config::Language))
{
if (g_locale[key].count(ELanguage::English))
{
return g_locale[key][ELanguage::English];
}
else
{
return g_localeMissing;
}
}
return g_locale[key][Config::Language];
}

View file

@ -14,7 +14,7 @@ be<float>* GetVolume(bool isMusic = true)
return (be<float>*)g_memory.Translate(4 * ((int)isMusic + 0x1C) + ((be<uint32_t>*)g_memory.Translate(ppUnkClass->get() + 4))->get()); return (be<float>*)g_memory.Translate(4 * ((int)isMusic + 0x1C) + ((be<uint32_t>*)g_memory.Translate(ppUnkClass->get() + 4))->get());
} }
void audio_patches::Update(float deltaTime) void AudioPatches::Update(float deltaTime)
{ {
auto pMusicVolume = GetVolume(); auto pMusicVolume = GetVolume();
auto pSEVolume = GetVolume(false); auto pSEVolume = GetVolume(false);

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
class audio_patches class AudioPatches
{ {
public: public:
static void Update(float deltaTime); static void Update(float deltaTime);

View file

@ -3,8 +3,6 @@
#include <api/SWA.h> #include <api/SWA.h>
#include <ui/options_menu.h> #include <ui/options_menu.h>
bool m_isOptionsFromPause = false;
void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis) void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis)
{ {
auto pStrMemory = __HH_ALLOC(8); auto pStrMemory = __HH_ALLOC(8);
@ -23,74 +21,43 @@ void CHudPauseAddOptionsItemMidAsmHook(PPCRegister& pThis)
bool InjectOptionsBehaviour(uint32_t pThis, uint32_t count) bool InjectOptionsBehaviour(uint32_t pThis, uint32_t count)
{ {
auto status = *(be<uint32_t>*)g_memory.Translate(pThis + 0x190); auto pHudPause = (SWA::CHudPause*)g_memory.Translate(pThis);
auto pauseType = *(be<uint32_t>*)g_memory.Translate(pThis + 0x18C);
auto cursorIndex = *(be<uint32_t>*)g_memory.Translate(4 * (*(be<uint32_t>*)g_memory.Translate(pThis + 0x19C) + 0x68) + pThis); auto cursorIndex = *(be<uint32_t>*)g_memory.Translate(4 * (*(be<uint32_t>*)g_memory.Translate(pThis + 0x19C) + 0x68) + pThis);
/* auto exitType = SWA::eActionType_Undefined;
0 ---- Undefined auto transitionType = SWA::eTransitionType_Undefined;
1 ---- Status
2 ---- Return to Previous Area
3 ---- Inventory
4 ---- Skills
5 ---- Go to the Lab
6 ---- Wait until Day/Night
7 ---- Undefined
8 ---- Restart Stage
9 ---- Continue Stage
<=10 - Undefined
*/
auto pExitType = (be<uint32_t>*)g_memory.Translate(pThis + 0x188);
/* switch (pHudPause->m_Menu)
0 --- Undefined
1 --- Unknown menu
2 --- Quit menu
3 --- Pause menu?
4 --- Undefined
5 --- Make cursor small?
6 --- Hide UI and ignore face buttons
7 --- Stop updating pause menu
8 --- Hide UI (apart from pause header) and ignore face buttons
<=9 - Stop updating pause menu
*/
auto pTransitionType = (be<uint32_t>*)g_memory.Translate(pThis + 0x194);
auto exitType = 0;
auto transitionType = 0;
switch (pauseType)
{ {
case 0: // World Map case SWA::eMenuType_WorldMap:
case 2: // Stage case SWA::eMenuType_Stage:
case 4: // Misc case SWA::eMenuType_Misc:
exitType = 2; exitType = SWA::eActionType_Return;
transitionType = 2; transitionType = SWA::eTransitionType_Quit;
break; break;
case 1: // Village case SWA::eMenuType_Village:
case 3: // Hub case SWA::eMenuType_Hub:
exitType = 2; exitType = SWA::eActionType_Return;
transitionType = 6; transitionType = SWA::eTransitionType_Hide;
break; break;
} }
if (status == 1) if (pHudPause->m_Status == SWA::eStatusType_Accept)
{ {
if (cursorIndex == count - 2) if (cursorIndex == count - 2)
{ {
OptionsMenu::Open(pauseType); OptionsMenu::Open(true, pHudPause->m_Menu);
m_isOptionsFromPause = true;
*pExitType = 0; pHudPause->m_Action = SWA::eActionType_Undefined;
*pTransitionType = 6; pHudPause->m_Transition = SWA::eTransitionType_Hide;
return true; return true;
} }
else if (cursorIndex == count - 1) else if (cursorIndex == count - 1)
{ {
*pExitType = exitType; pHudPause->m_Action = exitType;
*pTransitionType = transitionType; pHudPause->m_Transition = transitionType;
return true; return true;
} }
@ -130,21 +97,18 @@ bool CHudPauseMiscInjectOptionsMidAsmHook(PPCRegister& pThis)
PPC_FUNC_IMPL(__imp__sub_824B0930); PPC_FUNC_IMPL(__imp__sub_824B0930);
PPC_FUNC(sub_824B0930) PPC_FUNC(sub_824B0930)
{ {
if (!OptionsMenu::s_isVisible || !m_isOptionsFromPause) if (!OptionsMenu::s_isVisible || !OptionsMenu::s_isPause)
{ {
__imp__sub_824B0930(ctx, base); __imp__sub_824B0930(ctx, base);
return; return;
} }
auto pauseType = *(be<uint32_t>*)g_memory.Translate(ctx.r3.u32 + 0x18C);
if (auto pInputState = SWA::CInputState::GetInstance()) if (auto pInputState = SWA::CInputState::GetInstance())
{ {
// TODO: disable Start button closing menu. // TODO: disable Start button closing menu.
if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
{ {
OptionsMenu::Close(pauseType); OptionsMenu::Close();
m_isOptionsFromPause = false;
// Re-open pause menu. // Re-open pause menu.
GuestToHostFunction<int>(0x824AFD28, ctx.r3.u32, 0, 0, 0, 1); GuestToHostFunction<int>(0x824AFD28, ctx.r3.u32, 0, 0, 0, 1);

View file

@ -17,6 +17,7 @@ PPC_FUNC(sub_825882B8)
{ {
Game_PlaySound("sys_worldmap_window"); Game_PlaySound("sys_worldmap_window");
Game_PlaySound("sys_worldmap_decide"); Game_PlaySound("sys_worldmap_decide");
OptionsMenu::Open(); OptionsMenu::Open();
} }
} }
@ -29,6 +30,7 @@ PPC_FUNC(sub_825882B8)
if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B)) if (OptionsMenu::CanClose() && pInputState->GetPadState().IsTapped(SWA::eKeyState_B))
{ {
Game_PlaySound("sys_worldmap_cansel"); Game_PlaySound("sys_worldmap_cansel");
OptionsMenu::Close(); OptionsMenu::Close();
} }
} }

View file

@ -6,6 +6,7 @@
#include <gpu/imgui_common.h> #include <gpu/imgui_common.h>
#include <kernel/heap.h> #include <kernel/heap.h>
#include <kernel/memory.h> #include <kernel/memory.h>
#include <locale/locale.h>
constexpr float COMMON_PADDING_POS_Y = 118.0f; constexpr float COMMON_PADDING_POS_Y = 118.0f;
constexpr float COMMON_PADDING_POS_X = 30.0f; constexpr float COMMON_PADDING_POS_X = 30.0f;
@ -16,6 +17,7 @@ static ImFont* g_dfsogeistdFont;
static ImFont* g_newRodinFont; static ImFont* g_newRodinFont;
static const IConfigDef* g_selectedItem; static const IConfigDef* g_selectedItem;
static bool g_isSelectedItemAccessible;
static bool g_isEnterKeyBuffered = false; static bool g_isEnterKeyBuffered = false;
@ -151,7 +153,7 @@ static void DrawScanlineBars()
auto& res = ImGui::GetIO().DisplaySize; auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
if (OptionsMenu::s_isStage) if (OptionsMenu::s_pauseMenuType != SWA::eMenuType_WorldMap)
{ {
// Top bar fade // Top bar fade
drawList->AddRectFilledMultiColor( drawList->AddRectFilledMultiColor(
@ -291,19 +293,23 @@ static void DrawContainer(ImVec2 min, ImVec2 max)
drawList->PushClipRect({ min.x + gridSize * 2.0f, min.y + gridSize * 2.0f }, { max.x - gridSize * 2.0f + 1.0f, max.y - gridSize * 2.0f + 1.0f }); drawList->PushClipRect({ min.x + gridSize * 2.0f, min.y + gridSize * 2.0f }, { max.x - gridSize * 2.0f + 1.0f, max.y - gridSize * 2.0f + 1.0f });
} }
// TODO: localise this. static int32_t g_categoryCount = 4;
static constexpr const char* CATEGORIES[] =
{
"SYSTEM",
"INPUT",
"AUDIO",
"VIDEO"
};
static int32_t g_categoryIndex; static int32_t g_categoryIndex;
static ImVec2 g_categoryAnimMin; static ImVec2 g_categoryAnimMin;
static ImVec2 g_categoryAnimMax; static ImVec2 g_categoryAnimMax;
static std::string& GetCategory(int index)
{
// TODO: Don't use raw numbers here!
switch (index)
{
case 0: return Localise("Options_Category_System");
case 1: return Localise("Options_Category_Input");
case 2: return Localise("Options_Category_Audio");
case 3: return Localise("Options_Category_Video");
}
}
static int32_t g_firstVisibleRowIndex; static int32_t g_firstVisibleRowIndex;
static int32_t g_prevSelectedRowIndex; static int32_t g_prevSelectedRowIndex;
static int32_t g_selectedRowIndex; static int32_t g_selectedRowIndex;
@ -352,12 +358,12 @@ static bool DrawCategories()
{ {
--g_categoryIndex; --g_categoryIndex;
if (g_categoryIndex < 0) if (g_categoryIndex < 0)
g_categoryIndex = std::size(CATEGORIES) - 1; g_categoryIndex = g_categoryCount - 1;
} }
else if (moveRight) else if (moveRight)
{ {
++g_categoryIndex; ++g_categoryIndex;
if (g_categoryIndex >= std::size(CATEGORIES)) if (g_categoryIndex >= g_categoryCount)
g_categoryIndex = 0; g_categoryIndex = 0;
} }
@ -376,22 +382,22 @@ static bool DrawCategories()
float tabPadding = gridSize; float tabPadding = gridSize;
float size = Scale(32.0f); float size = Scale(32.0f);
ImVec2 textSizes[std::size(CATEGORIES)]; ImVec2 textSizes[g_categoryCount];
float tabWidthSum = 0.0f; float tabWidthSum = 0.0f;
for (size_t i = 0; i < std::size(CATEGORIES); i++) for (size_t i = 0; i < g_categoryCount; i++)
{ {
textSizes[i] = g_dfsogeistdFont->CalcTextSizeA(size, FLT_MAX, 0.0f, CATEGORIES[i]); textSizes[i] = g_dfsogeistdFont->CalcTextSizeA(size, FLT_MAX, 0.0f, GetCategory(i).c_str());
tabWidthSum += textSizes[i].x + textPadding * 2.0f; tabWidthSum += textSizes[i].x + textPadding * 2.0f;
} }
tabWidthSum += (std::size(CATEGORIES) - 1) * tabPadding; tabWidthSum += (g_categoryCount - 1) * tabPadding;
float tabHeight = gridSize * 4.0f; float tabHeight = gridSize * 4.0f;
float xOffset = ((clipRectMax.x - clipRectMin.x) - tabWidthSum) / 2.0f; float xOffset = ((clipRectMax.x - clipRectMin.x) - tabWidthSum) / 2.0f;
xOffset -= (1.0 - motion) * gridSize * 4.0; xOffset -= (1.0 - motion) * gridSize * 4.0;
ImVec2 minVec[std::size(CATEGORIES)]; ImVec2 minVec[g_categoryCount];
for (size_t i = 0; i < std::size(CATEGORIES); i++) for (size_t i = 0; i < g_categoryCount; i++)
{ {
ImVec2 min = { clipRectMin.x + xOffset, clipRectMin.y }; ImVec2 min = { clipRectMin.x + xOffset, clipRectMin.y };
@ -454,7 +460,7 @@ static bool DrawCategories()
minVec[i] = min; minVec[i] = min;
} }
for (size_t i = 0; i < std::size(CATEGORIES); i++) for (size_t i = 0; i < g_categoryCount; i++)
{ {
auto& min = minVec[i]; auto& min = minVec[i];
uint8_t alpha = (i == g_categoryIndex ? 235 : 128) * motion; uint8_t alpha = (i == g_categoryIndex ? 235 : 128) * motion;
@ -470,7 +476,7 @@ static bool DrawCategories()
size, size,
min, min,
IM_COL32_WHITE, IM_COL32_WHITE,
CATEGORIES[i], GetCategory(i).c_str(),
Scale(3), Scale(3),
IM_COL32_BLACK); IM_COL32_BLACK);
@ -490,16 +496,13 @@ static bool DrawCategories()
extern void VideoConfigValueChangedCallback(IConfigDef* config); extern void VideoConfigValueChangedCallback(IConfigDef* config);
template<typename T> template<typename T>
static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* config, T valueMin = T(0), T valueCenter = T(0.5), T valueMax = T(1)) static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* config, bool isAccessible, T valueMin = T(0), T valueCenter = T(0.5), T valueMax = T(1))
{ {
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMin = drawList->GetClipRectMin();
auto clipRectMax = drawList->GetClipRectMax(); auto clipRectMax = drawList->GetClipRectMax();
auto& padState = SWA::CInputState::GetInstance()->GetPadState(); auto& padState = SWA::CInputState::GetInstance()->GetPadState();
constexpr ImU32 COLOR0 = IM_COL32(0xE2, 0x71, 0x22, 0x80);
constexpr ImU32 COLOR1 = IM_COL32(0x92, 0xFF, 0x31, 0x80);
auto gridSize = Scale(GRID_SIZE); auto gridSize = Scale(GRID_SIZE);
auto optionWidth = gridSize * 54.0f; auto optionWidth = gridSize * 54.0f;
auto optionHeight = gridSize * 5.5f; auto optionHeight = gridSize * 5.5f;
@ -524,64 +527,75 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
if (g_selectedRowIndex == rowIndex) if (g_selectedRowIndex == rowIndex)
{ {
g_selectedItem = config; g_selectedItem = config;
g_isSelectedItemAccessible = isAccessible;
if (!g_isEnterKeyBuffered) if (!g_isEnterKeyBuffered)
{ {
if constexpr (std::is_same_v<T, bool>) if (isAccessible)
{ {
if (padState.IsTapped(SWA::eKeyState_A)) if constexpr (std::is_same_v<T, bool>)
{ {
config->Value = !config->Value; if (padState.IsTapped(SWA::eKeyState_A))
if (config->Callback)
config->Callback(config);
VideoConfigValueChangedCallback(config);
Game_PlaySound("sys_worldmap_finaldecide");
}
}
else
{
static T s_oldValue;
if (padState.IsTapped(SWA::eKeyState_A))
{
g_lockedOnOption ^= true;
if (g_lockedOnOption)
{ {
g_leftWasHeld = false; config->Value = !config->Value;
g_rightWasHeld = false;
// remember value
s_oldValue = config->Value;
Game_PlaySound("sys_worldmap_decide"); if (config->Callback)
} config->Callback(config);
else
{ VideoConfigValueChangedCallback(config);
// released lock, call video callbacks if value is different
if (config->Value != s_oldValue)
VideoConfigValueChangedCallback(config);
Game_PlaySound("sys_worldmap_finaldecide"); Game_PlaySound("sys_worldmap_finaldecide");
} }
} }
else if (padState.IsTapped(SWA::eKeyState_B)) else
{ {
// released lock, restore old value static T s_oldValue;
config->Value = s_oldValue;
g_lockedOnOption = false;
Game_PlaySound("sys_worldmap_cansel"); if (padState.IsTapped(SWA::eKeyState_A))
{
g_lockedOnOption ^= true;
if (g_lockedOnOption)
{
g_leftWasHeld = false;
g_rightWasHeld = false;
// remember value
s_oldValue = config->Value;
Game_PlaySound("sys_worldmap_decide");
}
else
{
// released lock, call video callbacks if value is different
if (config->Value != s_oldValue)
VideoConfigValueChangedCallback(config);
Game_PlaySound("sys_worldmap_finaldecide");
}
}
else if (padState.IsTapped(SWA::eKeyState_B))
{
// released lock, restore old value
config->Value = s_oldValue;
g_lockedOnOption = false;
Game_PlaySound("sys_worldmap_cansel");
}
lockedOnOption = g_lockedOnOption;
} }
}
lockedOnOption = g_lockedOnOption; else
{
if (padState.IsTapped(SWA::eKeyState_A))
Game_PlaySound("sys_actstg_stateserror");
} }
} }
} }
bool fadedOut = g_lockedOnOption && g_selectedItem != config; auto fadedOut = (g_lockedOnOption && g_selectedItem != config) || !isAccessible;
float alpha = fadedOut ? 0.5f : 1.0f; auto alpha = fadedOut ? 0.5f : 1.0f;
auto textColour = IM_COL32(255, 255, 255, 255 * alpha);
if (g_selectedItem == config) if (g_selectedItem == config)
{ {
@ -589,13 +603,16 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
double animRatio = std::clamp((ImGui::GetTime() - g_rowSelectionTime) * 60.0 / 8.0, 0.0, 1.0); double animRatio = std::clamp((ImGui::GetTime() - g_rowSelectionTime) * 60.0 / 8.0, 0.0, 1.0);
prevItemOffset *= pow(1.0 - animRatio, 3.0); prevItemOffset *= pow(1.0 - animRatio, 3.0);
drawList->AddRectFilledMultiColor({ min.x, min.y + prevItemOffset }, { max.x, max.y + prevItemOffset }, COLOR0, COLOR0, COLOR1, COLOR1); auto c0 = IM_COL32(0xE2, 0x71, 0x22, isAccessible ? 0x80 : 0x30);
auto c1 = IM_COL32(0x92, 0xFF, 0x31, isAccessible ? 0x80 : 0x30);
DrawTextWithMarquee(g_seuratFont, size, textPos, min, max, IM_COL32_WHITE, configName.c_str(), g_rowSelectionTime, 0.9, 250.0); drawList->AddRectFilledMultiColor({ min.x, min.y + prevItemOffset }, { max.x, max.y + prevItemOffset }, c0, c0, c1, c1);
DrawTextWithMarquee(g_seuratFont, size, textPos, min, max, textColour, configName.c_str(), g_rowSelectionTime, 0.9, 250.0);
} }
else else
{ {
drawList->AddText(g_seuratFont, size, textPos, IM_COL32(255, 255, 255, 255 * alpha), configName.c_str(), 0, 0.0f, &textClipRect); drawList->AddText(g_seuratFont, size, textPos, textColour, configName.c_str(), 0, 0.0f, &textClipRect);
} }
// Right side // Right side
@ -773,7 +790,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
if constexpr (std::is_same_v<T, float>) if constexpr (std::is_same_v<T, float>)
valueText = std::format("{}%", int32_t(round(config->Value * 100.0f))); valueText = std::format("{}%", int32_t(round(config->Value * 100.0f)));
else if constexpr (std::is_same_v<T, int32_t>) else if constexpr (std::is_same_v<T, int32_t>)
valueText = config->Value >= valueMax ? "MAX" : std::format("{}", config->Value); valueText = config->Value >= valueMax ? Localise("Options_Value_Max") : std::format("{}", config->Value);
else else
valueText = config->GetValueLocalised(); valueText = config->GetValueLocalised();
@ -816,48 +833,50 @@ static void DrawConfigOptions()
int32_t rowCount = 0; int32_t rowCount = 0;
bool isStage = OptionsMenu::s_pauseMenuType == SWA::eMenuType_Stage || OptionsMenu::s_pauseMenuType == SWA::eMenuType_Hub;
// TODO: Don't use raw numbers here! // TODO: Don't use raw numbers here!
switch (g_categoryIndex) switch (g_categoryIndex)
{ {
case 0: // SYSTEM case 0: // SYSTEM
DrawConfigOption(rowCount++, yOffset, &Config::Language); DrawConfigOption(rowCount++, yOffset, &Config::Language, !OptionsMenu::s_isPause);
DrawConfigOption(rowCount++, yOffset, &Config::Hints); DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage);
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial); DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage);
DrawConfigOption(rowCount++, yOffset, &Config::SaveScoreAtCheckpoints); DrawConfigOption(rowCount++, yOffset, &Config::SaveScoreAtCheckpoints, true);
DrawConfigOption(rowCount++, yOffset, &Config::UnleashGaugeBehaviour); DrawConfigOption(rowCount++, yOffset, &Config::UnleashGaugeBehaviour, true);
DrawConfigOption(rowCount++, yOffset, &Config::WerehogHubTransformVideo); DrawConfigOption(rowCount++, yOffset, &Config::WerehogHubTransformVideo, true);
DrawConfigOption(rowCount++, yOffset, &Config::LogoSkip); DrawConfigOption(rowCount++, yOffset, &Config::LogoSkip, true);
break; break;
case 1: // INPUT case 1: // INPUT
DrawConfigOption(rowCount++, yOffset, &Config::CameraXInvert); DrawConfigOption(rowCount++, yOffset, &Config::CameraXInvert, true);
DrawConfigOption(rowCount++, yOffset, &Config::CameraYInvert); DrawConfigOption(rowCount++, yOffset, &Config::CameraYInvert, true);
DrawConfigOption(rowCount++, yOffset, &Config::XButtonHoming); DrawConfigOption(rowCount++, yOffset, &Config::XButtonHoming, !OptionsMenu::s_isPause); // TODO: make this editable in stages.
DrawConfigOption(rowCount++, yOffset, &Config::UnleashCancel); DrawConfigOption(rowCount++, yOffset, &Config::UnleashCancel, true);
DrawConfigOption(rowCount++, yOffset, &Config::BackgroundInput); DrawConfigOption(rowCount++, yOffset, &Config::BackgroundInput, true);
break; break;
case 2: // AUDIO case 2: // AUDIO
DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume); DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::SEVolume); DrawConfigOption(rowCount++, yOffset, &Config::SEVolume, true);
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage); DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, true);
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles); DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
DrawConfigOption(rowCount++, yOffset, &Config::WerehogBattleMusic); DrawConfigOption(rowCount++, yOffset, &Config::WerehogBattleMusic, true);
break; break;
case 3: // VIDEO case 3: // VIDEO
// TODO: expose WindowWidth/WindowHeight as WindowSize. // TODO: expose WindowWidth/WindowHeight as WindowSize.
DrawConfigOption(rowCount++, yOffset, &Config::ResolutionScale, 0.25f, 1.0f, 2.0f); DrawConfigOption(rowCount++, yOffset, &Config::ResolutionScale, true, 0.25f, 1.0f, 2.0f);
DrawConfigOption(rowCount++, yOffset, &Config::Fullscreen); DrawConfigOption(rowCount++, yOffset, &Config::Fullscreen, true);
DrawConfigOption(rowCount++, yOffset, &Config::VSync); DrawConfigOption(rowCount++, yOffset, &Config::VSync, true);
DrawConfigOption(rowCount++, yOffset, &Config::TripleBuffering); DrawConfigOption(rowCount++, yOffset, &Config::TripleBuffering, true);
DrawConfigOption(rowCount++, yOffset, &Config::FPS, 15, 120, 240); DrawConfigOption(rowCount++, yOffset, &Config::FPS, true, 15, 120, 240);
DrawConfigOption(rowCount++, yOffset, &Config::Brightness); DrawConfigOption(rowCount++, yOffset, &Config::Brightness, true);
DrawConfigOption(rowCount++, yOffset, &Config::AntiAliasing); DrawConfigOption(rowCount++, yOffset, &Config::AntiAliasing, true);
DrawConfigOption(rowCount++, yOffset, &Config::AlphaToCoverage); DrawConfigOption(rowCount++, yOffset, &Config::AlphaToCoverage, true);
DrawConfigOption(rowCount++, yOffset, &Config::ShadowResolution); DrawConfigOption(rowCount++, yOffset, &Config::ShadowResolution, true);
DrawConfigOption(rowCount++, yOffset, &Config::GITextureFiltering); DrawConfigOption(rowCount++, yOffset, &Config::GITextureFiltering, true);
DrawConfigOption(rowCount++, yOffset, &Config::MotionBlur); DrawConfigOption(rowCount++, yOffset, &Config::MotionBlur, true);
DrawConfigOption(rowCount++, yOffset, &Config::Xbox360ColourCorrection); DrawConfigOption(rowCount++, yOffset, &Config::Xbox360ColourCorrection, true);
DrawConfigOption(rowCount++, yOffset, &Config::MovieScaleMode); DrawConfigOption(rowCount++, yOffset, &Config::MovieScaleMode, true);
DrawConfigOption(rowCount++, yOffset, &Config::UIScaleMode); DrawConfigOption(rowCount++, yOffset, &Config::UIScaleMode, true);
break; break;
} }
@ -974,20 +993,27 @@ static void DrawInfoPanel()
{ {
auto desc = g_selectedItem->GetDescription(); auto desc = g_selectedItem->GetDescription();
// Specialised description for resolution scale if (g_isSelectedItemAccessible)
if (g_selectedItem->GetName() == "ResolutionScale")
{ {
char buf[100]; // Specialised description for resolution scale
auto resScale = round(*(float*)g_selectedItem->GetValue() * 1000) / 1000; if (g_selectedItem->GetName() == "ResolutionScale")
{
char buf[100];
auto resScale = round(*(float*)g_selectedItem->GetValue() * 1000) / 1000;
std::snprintf(buf, sizeof(buf), desc.c_str(), std::snprintf(buf, sizeof(buf), desc.c_str(),
(int)((float)Window::s_width * resScale), (int)((float)Window::s_width * resScale),
(int)((float)Window::s_height * resScale)); (int)((float)Window::s_height * resScale));
desc = buf; desc = buf;
}
desc += "\n\n" + g_selectedItem->GetValueDescription();
}
else
{
desc = Localise("Options_Desc_NotAvailable");
} }
desc += "\n\n" + g_selectedItem->GetValueDescription();
auto size = Scale(26.0f); auto size = Scale(26.0f);
@ -1022,7 +1048,7 @@ void OptionsMenu::Draw()
auto& res = ImGui::GetIO().DisplaySize; auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
if (s_isStage) if (s_isPause && s_pauseMenuType != SWA::eMenuType_WorldMap)
drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223)); drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223));
DrawScanlineBars(); DrawScanlineBars();
@ -1030,10 +1056,13 @@ void OptionsMenu::Draw()
DrawInfoPanel(); DrawInfoPanel();
} }
void OptionsMenu::Open(bool stage) void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
{ {
s_isVisible = true; s_isVisible = true;
s_isStage = stage; s_isPause = isPause;
s_pauseMenuType = pauseMenuType;
g_appearTime = ImGui::GetTime(); g_appearTime = ImGui::GetTime();
g_categoryIndex = 0; g_categoryIndex = 0;
g_categoryAnimMin = { 0.0f, 0.0f }; g_categoryAnimMin = { 0.0f, 0.0f };
@ -1051,10 +1080,9 @@ void OptionsMenu::Open(bool stage)
// TODO: animate Miles Electric in if we're in a stage. // TODO: animate Miles Electric in if we're in a stage.
} }
void OptionsMenu::Close(bool stage) void OptionsMenu::Close()
{ {
s_isVisible = false; s_isVisible = false;
s_isStage = stage;
*(bool*)g_memory.Translate(0x8328BB26) = true; *(bool*)g_memory.Translate(0x8328BB26) = true;

View file

@ -1,15 +1,19 @@
#pragma once #pragma once
#include <api/SWA.h>
struct OptionsMenu struct OptionsMenu
{ {
public: public:
inline static bool s_isVisible = false; inline static bool s_isVisible = false;
inline static bool s_isStage = false; inline static bool s_isPause = false;
inline static SWA::EMenuType s_pauseMenuType;
static void Init(); static void Init();
static void Draw(); static void Draw();
static void Open(bool stage = false); static void Open(bool isPause = false, SWA::EMenuType pauseMenuType = SWA::eMenuType_WorldMap);
static void Close(bool stage = false); static void Close();
static bool CanClose(); static bool CanClose();
}; };