options_menu: implemented Miles Electric transition

This commit is contained in:
Hyper 2025-01-02 22:07:27 +00:00
parent 967a0ce17f
commit e24fa84e16
6 changed files with 154 additions and 34 deletions

View file

@ -487,6 +487,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" ARRAY_NAME "g_xbox_color_correction_false" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_false.dds" ARRAY_NAME "g_xbox_color_correction_false" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" ARRAY_NAME "g_xbox_color_correction_true" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction_true.dds" ARRAY_NAME "g_xbox_color_correction_true" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/miles_electric.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/miles_electric.dds" ARRAY_NAME "g_miles_electric" COMPRESSION_TYPE "zstd")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night")
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor")

View file

@ -2032,9 +2032,9 @@ static void DrawImGui()
OptionsMenu::Draw(); OptionsMenu::Draw();
AchievementOverlay::Draw(); AchievementOverlay::Draw();
InstallerWizard::Draw(); InstallerWizard::Draw();
Fader::Draw();
MessageWindow::Draw(); MessageWindow::Draw();
ButtonGuide::Draw(); ButtonGuide::Draw();
Fader::Draw();
DrawProfiler(); DrawProfiler();

View file

@ -47,6 +47,8 @@ public:
Button(std::string name, EButtonIcon icon, EButtonAlignment alignment, EFontQuality fontQuality = EFontQuality::High, bool* visibility = nullptr) Button(std::string name, EButtonIcon icon, EButtonAlignment alignment, EFontQuality fontQuality = EFontQuality::High, bool* visibility = nullptr)
: Name(name), Icon(icon), Alignment(alignment), FontQuality(fontQuality), Visibility(visibility) {} : Name(name), Icon(icon), Alignment(alignment), FontQuality(fontQuality), Visibility(visibility) {}
Button(std::string name, EButtonIcon icon, EButtonAlignment alignment, bool* visibility) : Name(name), Icon(icon), Alignment(alignment), Visibility(visibility) {}
Button(std::string name, EButtonIcon icon, bool* visibility) : Name(name), Icon(icon), Visibility(visibility) {} Button(std::string name, EButtonIcon icon, bool* visibility) : Name(name), Icon(icon), Visibility(visibility) {}
Button(std::string name, EButtonIcon icon) : Name(name), Icon(icon) {} Button(std::string name, EButtonIcon icon) : Name(name), Icon(icon) {}

View file

@ -8,7 +8,7 @@ static bool g_isFadeIn;
static float g_startTime; static float g_startTime;
static float g_duration; static float g_duration;
static ImU32 g_colour = IM_COL32(0, 0, 0, 255); static ImU32 g_colour = IM_COL32_BLACK;
static std::function<void()> g_endCallback; static std::function<void()> g_endCallback;
static float g_endCallbackDelay; static float g_endCallbackDelay;
@ -25,7 +25,10 @@ void Fader::Draw()
if (time >= g_duration + g_endCallbackDelay) if (time >= g_duration + g_endCallbackDelay)
{ {
if (g_endCallback) if (g_endCallback)
{
g_endCallback(); g_endCallback();
g_endCallback = nullptr;
}
g_isFading = false; g_isFading = false;
} }
@ -37,6 +40,9 @@ void Fader::Draw()
: Lerp(0, 1, time); : Lerp(0, 1, time);
} }
if (g_isFadeIn && !g_isFading)
return;
auto colour = IM_COL32(g_colour & 0xFF, (g_colour >> 8) & 0xFF, (g_colour >> 16) & 0xFF, 255 * alpha); auto colour = IM_COL32(g_colour & 0xFF, (g_colour >> 8) & 0xFF, (g_colour >> 16) & 0xFF, 255 * alpha);
ImGui::GetForegroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour); ImGui::GetForegroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour);
@ -47,13 +53,14 @@ static void DoFade(bool isFadeIn, float duration, std::function<void()> endCallb
if (g_isFading) if (g_isFading)
return; return;
Fader::s_isVisible = true;
g_isFading = true; g_isFading = true;
g_isFadeIn = isFadeIn; g_isFadeIn = isFadeIn;
g_startTime = ImGui::GetTime(); g_startTime = ImGui::GetTime();
g_duration = duration; g_duration = duration;
g_endCallback = endCallback; g_endCallback = endCallback;
g_endCallbackDelay = endCallbackDelay; g_endCallbackDelay = endCallbackDelay;
Fader::s_isVisible = true;
} }
void Fader::SetFadeColour(ImU32 colour) void Fader::SetFadeColour(ImU32 colour)

View file

@ -417,8 +417,8 @@ void MessageWindow::Draw()
for (int i = 0; i < rowCount; i++) for (int i = 0; i < rowCount; i++)
{ {
ImVec2 itemMin = { clipRectMin.x + windowMarginX, clipRectMin.y + windowMarginY + itemHeight * i }; ImVec2 itemMin = { listMin.x, listMin.y + itemHeight * i };
ImVec2 itemMax = { clipRectMax.x - windowMarginX, clipRectMin.y + windowMarginY + itemHeight * i + itemHeight }; ImVec2 itemMax = { listMax.x, clipRectMin.y + windowMarginY + itemHeight * i + itemHeight };
if (ImGui::IsMouseHoveringRect(itemMin, itemMax, false)) if (ImGui::IsMouseHoveringRect(itemMin, itemMax, false))
{ {

View file

@ -13,9 +13,18 @@
#include <locale/locale.h> #include <locale/locale.h>
#include <ui/button_guide.h> #include <ui/button_guide.h>
#include <app.h> #include <app.h>
#include <decompressor.h>
#include <patches/audio_patches.h> #include <patches/audio_patches.h>
#include <res/images/options_menu/miles_electric.dds.h>
static constexpr double MILES_ELECTRIC_SCALE_DURATION = 16.0;
static constexpr double MILES_ELECTRIC_BACKGROUND_DURATION = 16.0;
static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_DURATION = 6.0;
static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME = MILES_ELECTRIC_SCALE_DURATION - 2.0;
static constexpr double MILES_ELECTRIC_FOREGROUND_FADE_OUT_TIME = MILES_ELECTRIC_FOREGROUND_FADE_IN_TIME + MILES_ELECTRIC_FOREGROUND_FADE_DURATION;
static constexpr double CONTAINER_LINE_ANIMATION_DURATION = 8.0; static constexpr double CONTAINER_LINE_ANIMATION_DURATION = 8.0;
static constexpr double CONTAINER_OUTER_TIME = CONTAINER_LINE_ANIMATION_DURATION + 8.0; // 8 frame delay static constexpr double CONTAINER_OUTER_TIME = CONTAINER_LINE_ANIMATION_DURATION + 8.0; // 8 frame delay
@ -66,12 +75,17 @@ static const IConfigDef* g_selectedItem;
static std::string* g_inaccessibleReason; static std::string* g_inaccessibleReason;
static bool g_isStage = false;
static bool g_isClosing = false;
static bool g_isControlsVisible = false;
static bool g_isEnterKeyBuffered = false; static bool g_isEnterKeyBuffered = false;
static bool g_canReset = false; static bool g_canReset = false;
static bool g_isLanguageOptionChanged = false; static bool g_isLanguageOptionChanged = false;
static double g_appearTime = 0.0; static double g_appearTime = 0.0;
static std::unique_ptr<GuestTexture> g_upMilesElectric;
static void DrawScanlineBars() static void DrawScanlineBars()
{ {
constexpr uint32_t COLOR0 = IM_COL32(203, 255, 0, 0); constexpr uint32_t COLOR0 = IM_COL32(203, 255, 0, 0);
@ -174,7 +188,7 @@ static float AlignToNextGrid(float value)
static void DrawContainer(ImVec2 min, ImVec2 max) static void DrawContainer(ImVec2 min, ImVec2 max)
{ {
double containerHeight = ComputeMotion(g_appearTime, 0.0, CONTAINER_LINE_ANIMATION_DURATION); double containerHeight = g_isStage ? 1.0 : ComputeMotion(g_appearTime, 0.0, CONTAINER_LINE_ANIMATION_DURATION);
float center = (min.y + max.y) / 2.0f; float center = (min.y + max.y) / 2.0f;
min.y = Lerp(center, min.y, containerHeight); min.y = Lerp(center, min.y, containerHeight);
@ -183,9 +197,9 @@ static void DrawContainer(ImVec2 min, ImVec2 max)
auto& res = ImGui::GetIO().DisplaySize; auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList(); auto drawList = ImGui::GetForegroundDrawList();
double outerAlpha = ComputeMotion(g_appearTime, CONTAINER_OUTER_TIME, CONTAINER_OUTER_DURATION); double outerAlpha = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_OUTER_TIME, CONTAINER_OUTER_DURATION);
double innerAlpha = ComputeMotion(g_appearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION); double innerAlpha = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_INNER_TIME, CONTAINER_INNER_DURATION);
double backgroundAlpha = ComputeMotion(g_appearTime, CONTAINER_BACKGROUND_TIME, CONTAINER_BACKGROUND_DURATION); double backgroundAlpha = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_BACKGROUND_TIME, CONTAINER_BACKGROUND_DURATION);
const uint32_t lineColor = IM_COL32(0, 89, 0, 255 * containerHeight); const uint32_t lineColor = IM_COL32(0, 89, 0, 255 * containerHeight);
const uint32_t outerColor = IM_COL32(0, 49, 0, 255 * outerAlpha); const uint32_t outerColor = IM_COL32(0, 49, 0, 255 * outerAlpha);
@ -252,7 +266,7 @@ static void ResetSelection()
static bool DrawCategories() static bool DrawCategories()
{ {
double motion = ComputeMotion(g_appearTime, CONTAINER_CATEGORY_TIME, CONTAINER_CATEGORY_DURATION); double motion = g_isStage ? 1.0 : ComputeMotion(g_appearTime, CONTAINER_CATEGORY_TIME, CONTAINER_CATEGORY_DURATION);
if (motion == 0.0) if (motion == 0.0)
return false; return false;
@ -412,7 +426,7 @@ static bool DrawCategories()
ResetGradient(); ResetGradient();
} }
if ((ImGui::GetTime() - g_appearTime) >= (CONTAINER_FULL_DURATION / 60.0)) if (g_isStage || (ImGui::GetTime() - g_appearTime) >= (CONTAINER_FULL_DURATION / 60.0))
{ {
drawList->PushClipRect({ clipRectMin.x, clipRectMin.y + gridSize * 6.0f }, { clipRectMax.x - gridSize, clipRectMax.y - gridSize }); drawList->PushClipRect({ clipRectMin.x, clipRectMin.y + gridSize * 6.0f }, { clipRectMax.x - gridSize, clipRectMax.y - gridSize });
return true; return true;
@ -555,7 +569,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef<T>* conf
DrawTextWithMarquee(g_seuratFont, size, textPos, min, max, textColour, configName.c_str(), g_rowSelectionTime, 0.9, Scale(250.0)); DrawTextWithMarquee(g_seuratFont, size, textPos, min, max, textColour, configName.c_str(), g_rowSelectionTime, 0.9, Scale(250.0));
// Show reset button if this option is accessible or not a language option. // Show reset button if this option is accessible or not a language option.
g_canReset = g_selectedItem->GetName().find("Language") == std::string::npos && isAccessible; g_canReset = !g_lockedOnOption && g_selectedItem->GetName().find("Language") == std::string::npos && isAccessible;
} }
else else
{ {
@ -1026,6 +1040,78 @@ static void DrawInfoPanel()
drawList->PopClipRect(); drawList->PopClipRect();
} }
static bool DrawMilesElectric()
{
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto scaleMotion = ComputeMotion(g_appearTime, 0, MILES_ELECTRIC_SCALE_DURATION);
if (scaleMotion >= 1.0)
{
if (g_isClosing)
OptionsMenu::s_isVisible = false;
return true;
}
auto bgAlphaMotion = ComputeMotion(g_appearTime, 0, MILES_ELECTRIC_BACKGROUND_DURATION);
auto bgAlpha = g_isClosing
? Hermite(255, 0, bgAlphaMotion)
: Hermite(0, 255, bgAlphaMotion);
drawList->AddRectFilled({ 0, 0 }, res, IM_COL32(64, 64, 64, bgAlpha));
auto y = g_isClosing
? Hermite(140, 0, scaleMotion)
: Hermite(0, 140, scaleMotion);
auto scale = g_isClosing
? Hermite(Scale(1400), Scale(64), scaleMotion)
: Hermite(Scale(64), Scale(1400), scaleMotion);
ImVec2 centre = { res.x / 2, res.y / 2 - Scale(y) };
auto alpha = g_isClosing
? Hermite(255, 0, scaleMotion)
: Hermite(255, 127, scaleMotion);
drawList->AddImage
(
g_upMilesElectric.get(),
{ centre.x - scale, centre.y - scale },
{ centre.x + scale, centre.y + scale },
{ 0, 0 },
{ 1, 1 },
IM_COL32(255, 255, 255, alpha)
);
return false;
}
static bool DrawFadeTransition()
{
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
auto scaleMotion = ComputeMotion(g_appearTime, 0, MILES_ELECTRIC_SCALE_DURATION);
auto fgAlphaOutMotion = ComputeMotion(g_appearTime, MILES_ELECTRIC_FOREGROUND_FADE_OUT_TIME, MILES_ELECTRIC_FOREGROUND_FADE_DURATION);
if (scaleMotion < 0.8)
return false;
if (fgAlphaOutMotion >= 1.0)
{
g_isControlsVisible = true;
}
else
{
drawList->AddRectFilled({ 0, 0 }, res, IM_COL32(0, 0, 0, Lerp(255, 0, fgAlphaOutMotion)));
}
return fgAlphaOutMotion >= 1.0;
}
void OptionsMenu::Init() void OptionsMenu::Init()
{ {
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
@ -1035,27 +1121,48 @@ void OptionsMenu::Init()
g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf"); g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf");
LoadThumbnails(); LoadThumbnails();
g_upMilesElectric = LOAD_ZSTD_TEXTURE(g_miles_electric);
} }
void OptionsMenu::Draw() void OptionsMenu::Draw()
{ {
if (!s_isVisible) if (!s_isVisible)
{
g_isControlsVisible = false;
return; return;
}
// We've entered the menu now, no need to check this. // We've entered the menu now, no need to check this.
auto pInputState = SWA::CInputState::GetInstance(); auto pInputState = SWA::CInputState::GetInstance();
if (pInputState->GetPadState().IsReleased(SWA::eKeyState_A)) if (pInputState->GetPadState().IsReleased(SWA::eKeyState_A))
g_isEnterKeyBuffered = false; g_isEnterKeyBuffered = false;
auto& res = ImGui::GetIO().DisplaySize;
auto drawList = ImGui::GetForegroundDrawList();
if (s_isPause && s_pauseMenuType != SWA::eMenuType_WorldMap)
drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223));
DrawScanlineBars(); if (g_isStage)
DrawSettingsPanel(); {
DrawInfoPanel(); if (!DrawMilesElectric())
return;
}
else
{
g_isControlsVisible = true;
}
if (!g_isClosing)
{
auto drawList = ImGui::GetForegroundDrawList();
auto& res = ImGui::GetIO().DisplaySize;
if (g_isStage)
drawList->AddRectFilled({ 0.0f, 0.0f }, res, IM_COL32(0, 0, 0, 223));
DrawScanlineBars();
DrawSettingsPanel();
DrawInfoPanel();
if (g_isStage)
DrawFadeTransition();
}
s_isRestartRequired = Config::Language != App::s_language; s_isRestartRequired = Config::Language != App::s_language;
} }
@ -1063,10 +1170,11 @@ void OptionsMenu::Draw()
void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType) void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
{ {
s_isVisible = true; s_isVisible = true;
g_isClosing = false;
s_isPause = isPause; s_isPause = isPause;
s_pauseMenuType = pauseMenuType; s_pauseMenuType = pauseMenuType;
g_isStage = isPause && pauseMenuType != SWA::eMenuType_WorldMap;
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 };
@ -1079,34 +1187,36 @@ void OptionsMenu::Open(bool isPause, SWA::EMenuType pauseMenuType)
g_isEnterKeyBuffered = true; g_isEnterKeyBuffered = true;
ResetSelection(); ResetSelection();
// Hide CSD UI.
*(bool*)g_memory.Translate(0x8328BB26) = false; *(bool*)g_memory.Translate(0x8328BB26) = false;
std::array<Button, 4> buttons = std::array<Button, 4> buttons =
{ {
Button(Localise("Common_Switch"), EButtonIcon::LBRB, EButtonAlignment::Left), Button(Localise("Common_Switch"), EButtonIcon::LBRB, EButtonAlignment::Left, &g_isControlsVisible),
Button(Localise("Common_Reset"), EButtonIcon::X, &g_canReset), Button(Localise("Common_Reset"), EButtonIcon::X, &g_canReset),
Button(Localise("Common_Select"), EButtonIcon::A), Button(Localise("Common_Select"), EButtonIcon::A, &g_isControlsVisible),
Button(Localise("Common_Back"), EButtonIcon::B) Button(Localise("Common_Back"), EButtonIcon::B, &g_isControlsVisible)
}; };
ButtonGuide::Open(buttons);
ButtonGuide::Open(buttons);
ButtonGuide::SetSideMargins(250); ButtonGuide::SetSideMargins(250);
// TODO: animate Miles Electric in if we're in a stage.
} }
void OptionsMenu::Close() void OptionsMenu::Close()
{ {
s_isVisible = false; g_isClosing = true;
g_appearTime = ImGui::GetTime();
// Skip Miles Electric animation at main menu.
if (!g_isStage)
s_isVisible = false;
// Show CSD UI.
*(bool*)g_memory.Translate(0x8328BB26) = true; *(bool*)g_memory.Translate(0x8328BB26) = true;
ButtonGuide::Close(); ButtonGuide::Close();
Config::Save(); Config::Save();
// TODO: animate Miles Electric out if we're in a stage.
} }
bool OptionsMenu::CanClose() bool OptionsMenu::CanClose()