From 716a2b402077e670703b500133dcd8f5430927d9 Mon Sep 17 00:00:00 2001 From: JaxonKEKW <39291630+JaxonKEKW@users.noreply.github.com> Date: Wed, 20 May 2026 13:55:38 -0600 Subject: [PATCH] Improve Collection/File Select screens at Widescreen/Ultrawide (#1614) * Improve Widescreen/Ultrawide Collection/File Select Menus Re-scale (unstretch) and center elements of the Collection Screen/File Select details screen for Widescreen/Ultrawide * Fix oversight Fix default behavior * Support ultrawide on Collection menu, target PC support ultrawide instead of reverting to default behavior wrap logic in target PC ifdefs (both changed functions themselves are still behind them as a whole as well) and use old function behavior otherwise * Fix icon overshift at ultrawide Icon shifted too much at ultrawide Move redundant duplicate line * Finished Collection/File Select screen changes Added settings for the scaling mode (GameCube, Wii, Dusklight) Depending on the setting in the Interface menu (Dusklight preset automatically sets scaling to Dusklight option, Classic preset sets to Gamecube, Wii/all old behavior available as well) Collection and File Select screens get scaled differently Fixed backdrop behind slots on File Select with Dusklight setting (the Magic Armor background slot seeming too low on all aspects is vanilla behavior) Fixed Fused Shadow/Mirror size and position with Dusklight setting All logic is behind TARGET_PC gates (not the logic specifically, but the functions themselves have always been) Changes dSelect_cursor_c::refreshAspectScale to take a parameter so the scale of the selection cursor can be reset to default (only ever called in TARGET_PC functions or wrapped in gates) * Ultrawide oversight * Update d_file_select.cpp copy paste oopsie * Update d_file_select.cpp im tired, never tested msvc * Menu Scaling Mode changed definitions to be more open ended header additions now in TARGET_PC ifdefs fixed/added scaling for Save/Option buttons in Collection menu with Dusklight setting, stopped scaling just the text * Update settings.cpp --- include/d/d_file_select.h | 35 +++++ include/d/d_menu_collect.h | 43 ++++++ include/d/d_select_cursor.h | 2 +- include/dusk/settings.h | 13 ++ src/d/d_file_select.cpp | 278 +++++++++++++++++++++++++++--------- src/d/d_menu_collect.cpp | 222 ++++++++++++++++++---------- src/d/d_menu_save.cpp | 2 +- src/d/d_name.cpp | 4 +- src/d/d_select_cursor.cpp | 4 +- src/dusk/config.cpp | 1 + src/dusk/settings.cpp | 2 + src/dusk/ui/preset.cpp | 2 + src/dusk/ui/settings.cpp | 44 +++++- 13 files changed, 507 insertions(+), 145 deletions(-) diff --git a/include/d/d_file_select.h b/include/d/d_file_select.h index cf081a3b10..66c4696c37 100644 --- a/include/d/d_file_select.h +++ b/include/d/d_file_select.h @@ -11,7 +11,42 @@ class dFile_info_c; class J2DPicture; +#if TARGET_PC +static bool cachedPanes = false; +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mSelDtPanes[] = { + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('gray_n'), 0.0f, false}, + {MULTI_CHAR('b_base'), 0.0f, false}, + {MULTI_CHAR('b_base1'), 0.0f, false}, +}; + +static PaneCache fileSelPanes[] = { + {MULTI_CHAR('w_uzu00'), 0.0f, false}, + {MULTI_CHAR('w_uzu01'), 0.0f, false}, + {MULTI_CHAR('w_uzu02'), 0.0f, false}, + {MULTI_CHAR('w_uzu03'), 0.0f, false}, + {MULTI_CHAR('w_uzu04'), 0.0f, false}, + {MULTI_CHAR('w_uzu05'), 0.0f, false}, + {MULTI_CHAR('w_uzu06'), 0.0f, false}, + {MULTI_CHAR('w_uzu07'), 0.0f, false}, + {MULTI_CHAR('w_uzu08'), 0.0f, false}, + {MULTI_CHAR('w_uzu09'), 0.0f, false}, +}; +#endif class dDlst_FileSel_c : public dDlst_base_c { public: void draw(); diff --git a/include/d/d_menu_collect.h b/include/d/d_menu_collect.h index 55d5373c71..d259027a65 100644 --- a/include/d/d_menu_collect.h +++ b/include/d/d_menu_collect.h @@ -15,6 +15,49 @@ class dMenu_Fishing_c; class dMenu_Skill_c; class dMenu_Insect_c; class dSelect_cursor_c; +#if TARGET_PC +static bool cachedPanes = false; + +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mpScreenPanes[] = { + {MULTI_CHAR('sa_tex_n'), 0.0f, false}, + {MULTI_CHAR('op_tex_n'), 0.0f, false}, + {MULTI_CHAR('heart_n'), 0.0f, false}, + {MULTI_CHAR('wolf_n'), 0.0f, false}, + {MULTI_CHAR('item_0_n'), 0.0f, false}, + {MULTI_CHAR('item_1_n'), 0.0f, false}, + {MULTI_CHAR('item_2_n'), 0.0f, false}, + {MULTI_CHAR('fish_3_n'), 0.0f, false}, + {MULTI_CHAR('lett_4_n'), 0.0f, false}, + {MULTI_CHAR('maki_5_n'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('kabu_6n'), 0.0f, false}, + {MULTI_CHAR('t_t00'), 0.0f, false}, + {MULTI_CHAR('f_t00'), 0.0f, false}, + {MULTI_CHAR('itemn_n'), 0.0f, false}, + {MULTI_CHAR('infotxtn'), 0.0f, false}, + {MULTI_CHAR('sa_op_n'), 0.0f, false}, + {MULTI_CHAR('title_n'), 0.0f, false}, + {MULTI_CHAR('menu_n'), 0.0f, false}, + {MULTI_CHAR('w_er_n'), 0.0f, false}, + {MULTI_CHAR('center_n'), 0.0f, false}, + {MULTI_CHAR('info_n'), 0.0f, false}, + {MULTI_CHAR('lavel_n'), 0.0f, false}, + {MULTI_CHAR('modelbgn'), 0.0f, false}, +}; +#endif class dMenu_Collect2D_c; class dMenu_Collect2DTop_c : public dDlst_base_c { diff --git a/include/d/d_select_cursor.h b/include/d/d_select_cursor.h index 1ca226611b..e98db19731 100644 --- a/include/d/d_select_cursor.h +++ b/include/d/d_select_cursor.h @@ -51,7 +51,7 @@ public: f32 getPositionX() const { return mPositionX; } f32 getPositionY() const { return mPositionY; } - void refreshAspectScale(); + void refreshAspectScale(f32 param_0); #endif void onUpdateFlag() { mUpdateFlag = true; } diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 0d85b714a5..cc8af3556a 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -45,6 +45,12 @@ enum class FrameInterpMode : u8 { Unlimited = 2, }; +enum class MenuScaling : u8 { + GameCube = 0, + Wii = 1, + Dusklight = 2, +}; + namespace config { template <> struct ConfigEnumRange { @@ -81,6 +87,12 @@ struct ConfigEnumRange { static constexpr auto min = FrameInterpMode::Off; static constexpr auto max = FrameInterpMode::Unlimited; }; + +template <> +struct ConfigEnumRange { + static constexpr auto min = MenuScaling::GameCube; + static constexpr auto max = MenuScaling::Dusklight; +}; } // namespace config // Persistent user settings @@ -142,6 +154,7 @@ struct UserSettings { ConfigVar enableAchievementToasts; ConfigVar enableControllerToasts; ConfigVar enableDiscordPresence; + ConfigVar menuScalingMode; // Graphics ConfigVar bloomMode; diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 17d5f2c4e4..53f2450ac8 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -3750,74 +3750,220 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) { #if TARGET_PC void dFile_select_c::fileSelectWide() { - mYnSel.ScrYn->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mYnSel.ScrYn->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - m3mSel.Scr3m->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - m3mSel.Scr3m->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - fileSel.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Spirals - fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mSelIcon) { - mSelIcon->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; } - - if (mSelIcon2) { - mSelIcon2->refreshAspectScale(); + + // Reset all panes + mSelDt.ScrDt->scale(1.0f, 1.0f); + mSelDt.ScrDt->translate(0.0f, 0.0f); + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + bool wideScaling = dusk::getSettings().game.menuScalingMode.getValue() != dusk::MenuScaling::GameCube; + const f32 rootScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleUp : 1.0f; + const f32 childScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleDown : 1.0f; + const f32 rootTransX = wideScaling ? mDoGph_gInf_c::getSafeMinXF() : 0.0f; + + mYnSel.ScrYn->scale(rootScale, 1.0f); + mYnSel.ScrYn->translate(rootTransX, 0.0f); + + mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(childScale, 1.0f); + + m3mSel.Scr3m->scale(rootScale, 1.0f); + m3mSel.Scr3m->translate(rootTransX, 0.0f); + + m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(childScale, 1.0f); + + fileSel.Scr->scale(rootScale, 1.0f); + fileSel.Scr->translate(rootTransX, 0.0f); + + fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + + mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case (dusk::MenuScaling::GameCube): + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(1.0f); + } + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(1.0f); + } + break; + case (dusk::MenuScaling::Wii): + // Icons + mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case (dusk::MenuScaling::Dusklight): + constexpr f32 minAspect = 4.0f / 2.94f; + constexpr f32 wideAspect = 16.0f / 9.0f + 0.05f; + constexpr f32 ultraAspect = 21.0f / 9.0f + 0.05f; + const f32 screenAspect = mDoGph_gInf_c::getAspect(); + + const f32 wideScaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + const f32 ultraScaleFactor = 1.0f + 0.115f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + + const f32 wideShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (wideScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + const f32 ultraShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (ultraScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + + for (PaneCache& entry : mSelDtPanes) { + const size_t index = &entry - mSelDtPanes; + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_0); + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + if (screenAspect >= minAspect && screenAspect <= wideAspect) { // Handle widescreen + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX + 11.0f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX + 8.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX - 21.5f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX - 12.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * wideScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + wideShiftFactor - 60.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else if (screenAspect >= minAspect && screenAspect >= wideAspect && screenAspect <= ultraAspect) // Handle ultrawide + { + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->translate((entry.origTransX + 18.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->translate((entry.origTransX - 40.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * ultraScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + ultraShiftFactor - 62.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else { // 4:3/default behavior + pane->setBasePosition(J2DBasePosition_4); + pane->translate(entry.origTransX, pane->getTranslateY()); + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->scale(1.0f, 1.0f); + } + if (index <= 6) { // Icons + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + } + } + } + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + break; } - #endif } #endif diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 612806d372..8af6dc943a 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -97,79 +97,157 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() { #if TARGET_PC void dMenu_Collect2D_c::menuCollectWide() { - // Main Canvas - mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - // Pieces of Heart - mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Scents - mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Quiver - mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Wallet - mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Poes - mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Fish Bestiary - mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Letters - mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hidden Skills - mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Green Tunic - mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Zora Armor - mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Magic Armor - mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Shield - mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hylian Shield - mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Sword - mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Master Sword - mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Bugs - mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Collection" Text - mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Save" Text - mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Options" Text - mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Name Text - mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Description Text - mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mpDrawCursor) { - mpDrawCursor->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; + } + + // Reset all panes + mpScreen->scale(1.0f, 1.0f); + mpScreen->translate(0.0f, 0.0f); + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + // Reset button overlay + mpScreenIcon->translate(0.0f, 0.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case dusk::MenuScaling::GameCube: + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; + case dusk::MenuScaling::Wii: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Button Overlay + mpScreenIcon->translate(-mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // "Save" Text + mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Options" Text + mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Pieces of Heart + mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Scents + mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Quiver + mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Wallet + mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Poes + mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Fish Bestiary + mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Letters + mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hidden Skills + mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Green Tunic + mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Zora Armor + mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Magic Armor + mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Shield + mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hylian Shield + mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Sword + mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Master Sword + mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Bugs + mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Text + mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Name Text + mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Description Text + mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case dusk::MenuScaling::Dusklight: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Save/Options Buttons + mpScreen->search(MULTI_CHAR('sa_op_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Title Bar + mpScreen->search(MULTI_CHAR('title_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Main Central Elements + mpScreen->search(MULTI_CHAR('menu_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('w_er_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('center_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + const f32 leftShift = 48.0f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); // Shifting certain items left to keep center (> 4:3 only) + + // Item Name/Description Text + J2DPane* info_n = mpScreen->search(MULTI_CHAR('info_n')); + static f32 infoTransX_orig = info_n->getTranslateX(); + info_n->translate(infoTransX_orig - leftShift, info_n->getTranslateY()); + + // Designs + J2DPane* lavel_n = mpScreen->search(MULTI_CHAR('lavel_n')); + static f32 lavelTransX_orig = lavel_n->getTranslateX(); + lavel_n->translate(lavelTransX_orig - leftShift, lavel_n->getTranslateY()); + + // Fused Shadow/Mirror Background + J2DPane* modelbgn = mpScreen->search(MULTI_CHAR('modelbgn')); + static f32 modelbgnTransX_orig = modelbgn->getTranslateX(); // Get pre-scale value + modelbgn->setBasePosition(J2DBasePosition_0); + modelbgn->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.3f); + f32 modelbgn_scaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleDown - 1.0f); + modelbgn->translate((modelbgnTransX_orig - 12.0f) * modelbgn_scaleFactor, modelbgn->getTranslateY()); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; } - #endif } #endif diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index 53dbe37ca4..23beb29b0b 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -2817,7 +2817,7 @@ void dMenu_save_c::menuSaveWide() { #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif } diff --git a/src/d/d_name.cpp b/src/d/d_name.cpp index abc7512f14..3c0a7d35e1 100644 --- a/src/d/d_name.cpp +++ b/src/d/d_name.cpp @@ -1430,10 +1430,10 @@ void dName_c::selectCursorPosSet(int row) { #if TARGET_PC void dName_c::nameWide() { - //Resize Select Icon + // Resize Select Icon #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif diff --git a/src/d/d_select_cursor.cpp b/src/d/d_select_cursor.cpp index 0773433b4d..1268865cb6 100644 --- a/src/d/d_select_cursor.cpp +++ b/src/d/d_select_cursor.cpp @@ -577,7 +577,7 @@ void dSelect_cursor_c::moveCenter(J2DPane* i_pane, f32 i_x, f32 i_y) { } #ifdef TARGET_PC -void dSelect_cursor_c::refreshAspectScale() { - mParam1 = mBaseParam1 * mDoGph_gInf_c::hudAspectScaleUp; +void dSelect_cursor_c::refreshAspectScale(f32 param_0) { + mParam1 = mBaseParam1 * param_0; } #endif diff --git a/src/dusk/config.cpp b/src/dusk/config.cpp index 1f9e7c54a6..caa65bc0b4 100644 --- a/src/dusk/config.cpp +++ b/src/dusk/config.cpp @@ -176,6 +176,7 @@ namespace dusk::config { template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; + template class ConfigImpl; template class ConfigImpl; } diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e9768e9250..2ecda7df5c 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -54,6 +54,7 @@ UserSettings g_userSettings = { .enableAchievementToasts {"game.enableAchievementToasts", true}, .enableControllerToasts {"game.enableControllerToasts", true}, .enableDiscordPresence {"game.enableDiscordPresence", true}, + .menuScalingMode {"game.menuScalingMode", MenuScaling::Wii}, // Graphics .bloomMode {"game.bloomMode", BloomMode::Dusk}, @@ -247,6 +248,7 @@ void registerSettings() { Register(g_userSettings.game.liveSplitEnabled); Register(g_userSettings.game.showSpeedrunRTATimer); Register(g_userSettings.game.recordingMode); + Register(g_userSettings.game.menuScalingMode); Register(g_userSettings.game.removeQuestMapMarkers); Register(g_userSettings.game.showInputViewer); Register(g_userSettings.game.showInputViewerGyro); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index e5af8f2d50..de2dac84c3 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -19,6 +19,7 @@ void applyPresetClassic() { s.game.internalResolutionScale.setValue(1); s.game.shadowResolutionMultiplier.setValue(1); s.game.hideTvSettingsScreen.setValue(false); + s.game.menuScalingMode.setValue(MenuScaling::GameCube); AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT); } @@ -47,6 +48,7 @@ void applyPresetDusk() { s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); s.game.autoSave.setValue(true); + s.game.menuScalingMode.setValue(MenuScaling::Dusklight); } } // namespace diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 6187f97d4b..6518ede336 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -70,6 +70,12 @@ constexpr std::array kGyroInputModeLabels = { "Mouse", }; +constexpr std::array kMenuScalingModeLabels = { + "GameCube", + "Wii", + "Dusklight", +}; + bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) { if (backend == "auto") { outBackend = BACKEND_AUTO; @@ -1418,8 +1424,44 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .helpText = "Show gyro sensor values in the input viewer.", .isDisabled = [] { return !getSettings().game.showInputViewer; }, }); - leftPane.add_section("Game"); + leftPane.register_control( + leftPane.add_select_button({ + .key = "Menu Scaling Mode", + .getValue = + [] { + return kMenuScalingModeLabels[static_cast( + getSettings().game.menuScalingMode.getValue())]; + }, + .isModified = + [] { + const auto& mode = getSettings().game.menuScalingMode; + return mode.getValue() != mode.getDefaultValue(); + }, + }), + rightPane, [](Pane& pane) { + for (int i = 0; i < static_cast(kMenuScalingModeLabels.size()); ++i) { + pane + .add_button({ + .text = kMenuScalingModeLabels[i], + .isSelected = + [i] { + return getSettings().game.menuScalingMode.getValue() == + static_cast(i); + ; + }, + }) + .on_pressed([i] { + mDoAud_seStartMenu(kSoundItemChange); + getSettings().game.menuScalingMode.setValue( + static_cast(i)); + ; + config::Save(); + }); + } + pane.add_rml("
Changes how the Collection and File Select menus scale to your " + "aspect ratio."); + }); config_bool_select(leftPane, rightPane, getSettings().game.hideTvSettingsScreen, { .key = "Skip TV Settings Screen",