diff --git a/src/common/2d/v_draw.cpp b/src/common/2d/v_draw.cpp index a37f930f3..86a85c55c 100644 --- a/src/common/2d/v_draw.cpp +++ b/src/common/2d/v_draw.cpp @@ -410,6 +410,29 @@ void CalcFullscreenScale(DrawParms *parms, double srcwidth, double srcheight, in } } +DEFINE_ACTION_FUNCTION(_Screen, GetFullscreenRect) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(virtw); + PARAM_FLOAT(virth); + PARAM_INT(fsmode); + + if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); + + DrawParms parms; + DoubleRect rect; + parms.viewport.width = twod->GetWidth(); + parms.viewport.height = twod->GetHeight(); + CalcFullscreenScale(&parms, virtw, virth, fsmode, rect); + if (numret >= 1) ret[0].SetFloat(rect.left); + if (numret >= 2) ret[1].SetFloat(rect.top); + if (numret >= 3) ret[2].SetFloat(rect.width); + if (numret >= 4) ret[3].SetFloat(rect.height); + return MIN(numret, 4); +} + + + //========================================================================== // // Draw parameter parsing @@ -460,6 +483,13 @@ bool SetTextureParms(F2DDrawer * drawer, DrawParms *parms, FGameTexture *img, do parms->destheight = parms->texheight * CleanYfac; break; + case DTA_CleanTop: + parms->x = (parms->x - 160.0) * CleanXfac + (GetWidth() * 0.5); + parms->y = (parms->y) * CleanYfac; + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; + break; + case DTA_CleanNoMove: parms->destwidth = parms->texwidth * CleanXfac; parms->destheight = parms->texheight * CleanYfac; @@ -716,6 +746,7 @@ bool ParseDrawTextureTags(F2DDrawer *drawer, FGameTexture *img, double x, double break; case DTA_Clean: + case DTA_CleanTop: boolval = ListGetInt(tags); if (boolval) { diff --git a/src/common/2d/v_draw.h b/src/common/2d/v_draw.h index c18566936..dc9b8d28c 100644 --- a/src/common/2d/v_draw.h +++ b/src/common/2d/v_draw.h @@ -130,6 +130,7 @@ enum DTA_Rotate, DTA_FlipOffsets, // Flips offsets when using DTA_FlipX and DTA_FlipY, this cannot be automatic due to unexpected behavior with unoffsetted graphics. DTA_Indexed, // Use an indexed texture combined with the given translation. + DTA_CleanTop, // Like DTA_Clean but aligns to the top of the screen instead of the center. }; diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 947e62abb..35ad695e8 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -270,6 +270,8 @@ static bool CheckSkipOptionBlock(FScanner &sc) static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) { + bool sizeset = false; + bool sizecompatible = true; sc.MustGetStringName("{"); while (!sc.CheckString("}")) { @@ -303,6 +305,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) sc.ScriptError("Unknown menu class '%s'", sc.String); } desc->mClass = cls; + sizecompatible = false; } else if (sc.Compare("Selector")) { @@ -382,14 +385,23 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) { desc->mVirtWidth = -1; } - if (sc.Compare("optclean")) + else if (sc.Compare("optclean")) { desc->mVirtWidth = -2; } + else + { + sc.ScriptError("Invalid value '%s' for 'size'", sc.String); + } } } else { + // all item classes from which we know that they support sized scaling. + // If anything else comes through here the option to swich scaling mode is disabled for this menu. + static const char* const compatibles[] = { "StaticPatch", "StaticText", "StaticTextCentered", "TextItem", "PatchItem", "PlayerDisplay", nullptr }; + if (sc.MatchString(compatibles) < 0) sizecompatible = false; + bool success = false; FStringf buildname("ListMenuItem%s", sc.String); PClass *cls = PClass::FindClass(buildname); @@ -521,6 +533,10 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) } } } + if (!sizeset && sizecompatible) // allow unclean scaling on this menu + { + desc->mVirtWidth = -2; + } for (auto &p : desc->mItems) { GC::WriteBarrier(p); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index e56ad7230..533026994 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -231,7 +231,6 @@ ListMenu "EpisodeMenu" ListMenu "SkillMenu" { - IfGame(Doom, Chex) { StaticPatch 96, 14, "M_NEWG", 0, "$MNU_NEWGAME" @@ -289,6 +288,7 @@ ListMenu "LoadGameMenu" StaticTextCentered 160, -10, "$MNU_LOADGAME" Position 80,54 Class "LoadMenu" // uses its own implementation + Size Clean } //------------------------------------------------------------------------------------------- @@ -302,6 +302,7 @@ ListMenu "SaveGameMenu" StaticTextCentered 160, -10, "$MNU_SAVEGAME" Position 80,54 Class "SaveMenu" // uses its own implementation + Size Clean } //------------------------------------------------------------------------------------------- diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index b1a86b017..b44b703f7 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -124,6 +124,30 @@ struct TexMan native static void SetCameraToTexture(Actor viewpoint, String texture, double fov); } +enum EScaleMode +{ + FSMode_None = 0, + FSMode_ScaleToFit = 1, + FSMode_ScaleToFill = 2, + FSMode_ScaleToFit43 = 3, + FSMode_ScaleToScreen = 4, + FSMode_ScaleToFit43Top = 5, + FSMode_ScaleToFit43Bottom = 6, + FSMode_ScaleToHeight = 7, + + + FSMode_Max, + + // These all use ScaleToFit43, their purpose is to cut down on verbosity because they imply the virtual screen size. + FSMode_Predefined = 1000, + FSMode_Fit320x200 = 1000, + FSMode_Fit320x240, + FSMode_Fit640x400, + FSMode_Fit640x480, + FSMode_Fit320x200Top, + FSMode_Predefined_Max, +}; + enum DrawTextureTags { TAG_USER = (1<<30), @@ -208,6 +232,7 @@ enum DrawTextureTags DTA_Rotate, DTA_FlipOffsets, // Flips offsets when using DTA_FlipX and DTA_FlipY, this cannot be automatic due to unexpected behavior with unoffsetted graphics. DTA_Indexed, // Use an indexed texture combined with the given translation. + DTA_CleanTop, // Like DTA_Clean but aligns to the top of the screen instead of the center. }; @@ -257,6 +282,8 @@ struct Screen native native static void ClearClipRect(); native static int, int, int, int GetClipRect(); native static int, int, int, int GetViewWindow(); + native static double, double, double, double GetFullscreenRect(double vwidth, double vheight, int fsmode); + // This is a leftover of the abandoned Inventory.DrawPowerup method. diff --git a/wadsrc/static/zscript/ui/menu/listmenu.zs b/wadsrc/static/zscript/ui/menu/listmenu.zs index 37a896496..76b384ba6 100644 --- a/wadsrc/static/zscript/ui/menu/listmenu.zs +++ b/wadsrc/static/zscript/ui/menu/listmenu.zs @@ -2,7 +2,7 @@ class ListMenuDescriptor : MenuDescriptor native { - enum + enum EScale { CleanScale = -1, OptCleanScale = -2 @@ -249,10 +249,10 @@ class ListMenu : Menu { for(int i=0;i= 0 && mDesc.mSelectedItem < mDesc.mItems.Size()) - mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector); + mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector, mDesc); Super.Drawer(); } diff --git a/wadsrc/static/zscript/ui/menu/listmenuitems.zs b/wadsrc/static/zscript/ui/menu/listmenuitems.zs index 16b75ad0d..dafbfd5d2 100644 --- a/wadsrc/static/zscript/ui/menu/listmenuitems.zs +++ b/wadsrc/static/zscript/ui/menu/listmenuitems.zs @@ -35,26 +35,54 @@ class ListMenuItem : MenuItemBase { - virtual void DrawSelector(double xofs, double yofs, TextureID tex) + protected void DrawText(ListMenuDescriptor desc, Font fnt, int color, double x, double y, String text, bool ontop = false) + { + int w = desc ? desc.DisplayWidth() : ListMenuDescriptor.CleanScale; + int h = desc ? desc.DisplayHeight() : -1; + if (w == ListMenuDescriptor.CleanScale) + { + screen.DrawText(fnt, color, x, y, text, ontop? DTA_CleanTop : DTA_Clean, true); + } + else + { + screen.DrawText(fnt, color, x, y, text, DTA_VirtualWidth, w, DTA_VirtualHeight, h, DTA_FullscreenScale, FSMode_ScaleToFit43); + } + } + + protected void DrawTexture(ListMenuDescriptor desc, TextureID tex, double x, double y, bool ontop = false) + { + int w = desc ? desc.DisplayWidth() : ListMenuDescriptor.CleanScale; + int h = desc ? desc.DisplayHeight() : -1; + if (w == ListMenuDescriptor.CleanScale) + { + screen.DrawTexture(tex, true, x, y, ontop ? DTA_CleanTop : DTA_Clean, true); + } + else + { + screen.DrawTexture(tex, true, x, y, DTA_VirtualWidth, w, DTA_VirtualHeight, h, DTA_FullscreenScale, FSMode_ScaleToFit43); + } + } + + virtual void DrawSelector(double xofs, double yofs, TextureID tex, ListMenuDescriptor desc = null) { if (tex.isNull()) { if ((Menu.MenuTime() % 8) < 6) { - screen.DrawText(ConFont, OptionMenuSettings.mFontColorSelection, - (mXpos + xofs - 160) * CleanXfac + screen.GetWidth() / 2, - (mYpos + yofs - 100) * CleanYfac + screen.GetHeight() / 2, - "\xd", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac - ); + DrawText(desc, ConFont, OptionMenuSettings.mFontColorSelection, mXpos + xofs, mYpos + yofs + 8, "\xd"); } } else { - screen.DrawTexture (tex, true, mXpos + xofs, mYpos + yofs, DTA_Clean, true); + DrawTexture(desc, tex, mXpos + xofs, mYpos + yofs); } } + + // We cannot extend Drawer here because it is inherited from the parent class. + virtual void Draw(bool selected, ListMenuDescriptor desc) + { + Drawer(selected); // fall back to the legacy version, if not overridden + } } //============================================================================= @@ -82,43 +110,26 @@ class ListMenuItemStaticPatch : ListMenuItem } - override void Drawer(bool selected) + override void Draw(bool selected, ListMenuDescriptor desc) { if (!mTexture.Exists()) { return; } - let font = generic_ui? NewSmallFont : mFont; - double x = mXpos; Vector2 vec = TexMan.GetScaledSize(mTexture); - if (mYpos >= 0) + + if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute)) { - if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute)) - { - if (mCentered) x -= vec.X / 2; - screen.DrawTexture (mTexture, true, x, mYpos, DTA_Clean, true); - } - else - { - if (mCentered) x -= font.StringWidth(mSubstitute)/2; - screen.DrawText(font, mColor, x, mYpos, mSubstitute, DTA_Clean, true); - } + if (mCentered) x -= vec.X / 2; + DrawTexture(desc, mTexture, x, abs(mYpos), mYpos < 0); } else { - x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1); - if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute)) - { - if (mCentered) x -= (vec.X * CleanXfac)/2; - screen.DrawTexture (mTexture, true, x, -mYpos*CleanYfac, DTA_CleanNoMove, true); - } - else - { - if (mCentered) x -= (font.StringWidth(mSubstitute) * CleanXfac)/2; - screen.DrawText(font, mColor, x, mYpos, mSubstitute, DTA_CleanNoMove, true); - } + let font = generic_ui ? NewSmallFont : mFont; + if (mCentered) x -= font.StringWidth(mSubstitute) / 2; + DrawText(desc, font, mColor, x, abs(mYpos), mSubstitute, mYpos < 0); } } } @@ -162,25 +173,17 @@ class ListMenuItemStaticText : ListMenuItem mCentered = centered; } - override void Drawer(bool selected) + override void Draw(bool selected, ListMenuDescriptor desc) { if (mText.Length() != 0) { let font = generic_ui? NewSmallFont : mFont; String text = Stringtable.Localize(mText); - if (mYpos >= 0) - { - double x = mXpos; - if (mCentered) x -= font.StringWidth(text)/2; - screen.DrawText(font, mColor, x, mYpos, text, DTA_Clean, true); - } - else - { - double x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1); - if (mCentered) x -= (font.StringWidth(text) * CleanXfac)/2; - screen.DrawText (font, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true); - } + + double x = mXpos; + if (mCentered) x -= font.StringWidth(text) / 2; + DrawText(desc, font, mColor, x, abs(mYpos), text, mYpos < 0); } } } @@ -288,10 +291,10 @@ class ListMenuItemTextItem : ListMenuItemSelectable mHotkey = hotkey.GetNextCodePoint(0); } - override void Drawer(bool selected) + override void Draw(bool selected, ListMenuDescriptor desc) { - let font = generic_ui? NewSmallFont : mFont; - screen.DrawText(font, selected ? mColorSelected : mColor, mXpos, mYpos, mText, DTA_Clean, true); + let font = generic_ui ? NewSmallFont : mFont; + DrawText(desc, font, selected ? mColorSelected : mColor, mXpos, mYpos, mText); } override int GetWidth() @@ -325,9 +328,9 @@ class ListMenuItemPatchItem : ListMenuItemSelectable mTexture = patch; } - override void Drawer(bool selected) + override void Draw(bool selected, ListMenuDescriptor desc) { - screen.DrawTexture (mTexture, true, mXpos, mYpos, DTA_Clean, true); + DrawTexture(desc, mTexture, mXpos, mYpos); } override int GetWidth() diff --git a/wadsrc/static/zscript/ui/menu/playerdisplay.zs b/wadsrc/static/zscript/ui/menu/playerdisplay.zs index 557185d1d..e3d24438e 100644 --- a/wadsrc/static/zscript/ui/menu/playerdisplay.zs +++ b/wadsrc/static/zscript/ui/menu/playerdisplay.zs @@ -228,7 +228,7 @@ class ListMenuItemPlayerDisplay : ListMenuItem // //============================================================================= - override void Drawer(bool selected) + override void Draw(bool selected, ListMenuDescriptor desc) { if (mMode == 0 && !UpdatePlayerClass()) { @@ -241,12 +241,31 @@ class ListMenuItemPlayerDisplay : ListMenuItem if (portrait != 'None' && !mNoportrait) { TextureID texid = TexMan.CheckForTexture(portrait, TexMan.Type_MiscPatch); - screen.DrawTexture (texid, true, mXpos, mYpos, DTA_Clean, true); + DrawTexture (desc, texid, mXpos, mYpos); } else { - int x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1); - int y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); + // Here we need to calculate the coordinates manually because Screen.DrawFrame only works in window coordinates and have to match the rest to it. + int x, y; + int w = desc.DisplayWidth(); + int h = desc.DisplayHeight(); + double sx, sy; + if (w == ListMenuDescriptor.CleanScale) + { + x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1); + y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); + sx = CleanXfac; + sy = CleanYfac; + } + else + { + double fx, fy, fw, fh; + [fx, fy, fw, fh] = Screen.GetFullscreenRect(w, h, FSMode_ScaleToFit43); + sx = fw / w; + sy = fh / h; + x = int(fx + mXpos * sx); + y = int(fy + mYpos * sy); + } int r = mBaseColor.r + mAddColor.r; int g = mBaseColor.g + mAddColor.g; @@ -258,12 +277,12 @@ class ListMenuItemPlayerDisplay : ListMenuItem Color c = Color(255, r, g, b); screen.DrawTexture(mBackdrop, false, x, y - 1, - DTA_DestWidth, 72 * CleanXfac, - DTA_DestHeight, 80 * CleanYfac, + DTA_DestWidthF, 72. * sx, + DTA_DestHeightF, 80. * sy, DTA_Color, c, DTA_Masked, true); - Screen.DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); + Screen.DrawFrame (x, y, 72*sx, 80*sy-1); if (mPlayerState != NULL) { @@ -277,11 +296,11 @@ class ListMenuItemPlayerDisplay : ListMenuItem { int trans = mTranslate? Translation.MakeID(TRANSLATION_Players, MAXPLAYERS) : 0; let tscale = TexMan.GetScaledSize(sprite); - Scale.X *= CleanXfac * tscale.X; - Scale.Y *= CleanYfac * tscale.Y; + Scale.X *= sx * tscale.X; + Scale.Y *= sy * tscale.Y; screen.DrawTexture (sprite, false, - x + 36*CleanXfac, y + 71*CleanYfac, + x + 36*sx, y + 71*sy, DTA_DestWidthF, Scale.X, DTA_DestHeightF, Scale.Y, DTA_TranslationIndex, trans, DTA_FlipX, flip);