diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp index 32b55f1ca..ef34bbb57 100644 --- a/source/common/2d/v_draw.cpp +++ b/source/common/2d/v_draw.cpp @@ -1553,3 +1553,11 @@ void V_CalcCleanFacs(int designwidth, int designheight, int realwidth, int realh *cleanx = *cleany = std::min(realwidth / designwidth, realheight / designheight); } + +DEFINE_ACTION_FUNCTION(_Screen, SetOffset) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + ACTION_RETURN_VEC2(twod->SetOffset(DVector2(x, y))); +} \ No newline at end of file diff --git a/source/common/menu/menu.cpp b/source/common/menu/menu.cpp index eb8320687..5fc5021aa 100644 --- a/source/common/menu/menu.cpp +++ b/source/common/menu/menu.cpp @@ -122,6 +122,7 @@ bool OkForLocalization(FTextureID texnum, const char* substitute); IMPLEMENT_CLASS(DMenuDescriptor, false, false) IMPLEMENT_CLASS(DListMenuDescriptor, false, false) IMPLEMENT_CLASS(DOptionMenuDescriptor, false, false) +IMPLEMENT_CLASS(DImageScrollerDescriptor, false, false) DMenuDescriptor *GetMenuDescriptor(int name) { @@ -136,6 +137,13 @@ DEFINE_ACTION_FUNCTION_NATIVE(DMenuDescriptor, GetDescriptor, GetMenuDescriptor) ACTION_RETURN_OBJECT(GetMenuDescriptor(name.GetIndex())); } +size_t DMenuDescriptor::PropagateMark() +{ + for (auto item : mItems) GC::Mark(item); + return 0; +} + + void DListMenuDescriptor::Reset() { // Reset the default settings (ignore all other values in the struct) @@ -162,12 +170,6 @@ DEFINE_ACTION_FUNCTION(DListMenuDescriptor, Reset) } -size_t DListMenuDescriptor::PropagateMark() -{ - for (auto item : mItems) GC::Mark(item); - return 0; -} - void DOptionMenuDescriptor::Reset() { // Reset the default settings (ignore all other values in the struct) @@ -178,12 +180,6 @@ void DOptionMenuDescriptor::Reset() mFont = BigUpper; } -size_t DOptionMenuDescriptor::PropagateMark() -{ - for (auto item : mItems) GC::Mark(item); - return 0; -} - void M_MarkMenus() { MenuDescriptorList::Iterator it(MenuDescriptors); @@ -1045,6 +1041,15 @@ DEFINE_FIELD(FOptionMenuSettings, mFontColorHighlight) DEFINE_FIELD(FOptionMenuSettings, mFontColorSelection) DEFINE_FIELD(FOptionMenuSettings, mLinespacing) +DEFINE_FIELD(DImageScrollerDescriptor, mItems) +DEFINE_FIELD(DImageScrollerDescriptor, textBackground) +DEFINE_FIELD(DImageScrollerDescriptor, textBackgroundBrightness) +DEFINE_FIELD(DImageScrollerDescriptor,textFont) +DEFINE_FIELD(DImageScrollerDescriptor, textScale) +DEFINE_FIELD(DImageScrollerDescriptor, mAnimatedTransition) +DEFINE_FIELD(DImageScrollerDescriptor, virtWidth) +DEFINE_FIELD(DImageScrollerDescriptor, virtHeight) + struct IJoystickConfig; // These functions are used by dynamic menu creation. diff --git a/source/common/menu/menu.h b/source/common/menu/menu.h index e57052c99..86db161ce 100644 --- a/source/common/menu/menu.h +++ b/source/common/menu/menu.h @@ -66,7 +66,7 @@ public: bool mProtected = false; TArray mItems; - virtual size_t PropagateMark() { return 0; } + size_t PropagateMark() override; }; @@ -95,7 +95,6 @@ public: int mVirtHeight; void Reset(); - size_t PropagateMark() override; }; struct FOptionMenuSettings @@ -128,10 +127,22 @@ public: void CalcIndent(); DMenuItemBase *GetItem(FName name); void Reset(); - size_t PropagateMark() override; ~DOptionMenuDescriptor() = default; }; - + +class DImageScrollerDescriptor : public DMenuDescriptor +{ + DECLARE_CLASS(DOptionMenuDescriptor, DMenuDescriptor) +public: + FTextureID textBackground; + PalEntry textBackgroundBrightness; + + FFont *textFont; + double textScale; + bool mAnimatedTransition; + int virtWidth, virtHeight; + +}; typedef TMap MenuDescriptorList; diff --git a/source/common/menu/menudef.cpp b/source/common/menu/menudef.cpp index 73978e1f2..86ce761e8 100644 --- a/source/common/menu/menudef.cpp +++ b/source/common/menu/menudef.cpp @@ -50,6 +50,7 @@ #include "texturemanager.h" #include "printf.h" #include "i_interface.h" +#include "templates.h" bool CheckSkipGameOptionBlock(FScanner& sc); @@ -629,7 +630,7 @@ static bool ReplaceMenu(FScanner &sc, DMenuDescriptor *desc) { // If this tries to replace an option menu with an option menu, let's append all new entries to the old menu. // Otherwise bail out because for list menus it's not that simple. - if (desc->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)) || (*pOld)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) + if (!desc->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)) || !(*pOld)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) { sc.ScriptMessage("Cannot replace protected menu %s.", desc->mMenuName.GetChars()); return true; @@ -1052,6 +1053,214 @@ static void ParseAddOptionMenu(FScanner &sc) } +//============================================================================= +// +// +// +//============================================================================= + +static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc) +{ + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("else")) + { + SkipSubBlock(sc); + } + else if (sc.Compare("ifgame")) + { + if (!CheckSkipGameBlock(sc)) + { + // recursively parse sub-block + ParseImageScrollerBody(sc, desc); + } + } + else if (sc.Compare("ifnotgame")) + { + if (!CheckSkipGameBlock(sc, true)) + { + // recursively parse sub-block + ParseImageScrollerBody(sc, desc); + } + } + else if (sc.Compare("ifoption")) + { + if (!CheckSkipOptionBlock(sc)) + { + // recursively parse sub-block + ParseImageScrollerBody(sc, desc); + } + } + else if (sc.Compare("animatedtransition")) + { + desc->mAnimatedTransition = true; + } + else if (sc.Compare("textBackground")) + { + sc.MustGetString(); + desc->textBackground = GetMenuTexture(sc.String); + } + else if (sc.Compare("textBackgroundBrightness")) + { + sc.MustGetFloat(); + int bb = clamp(int(sc.Float * 255), 0, 255); + desc->textBackgroundBrightness = PalEntry(255, bb, bb, bb); + } + else if (sc.Compare("textScale")) + { + sc.MustGetFloat(); + desc->textScale = sc.Float; + } + else if (sc.Compare("textFont")) + { + sc.MustGetString(); + FFont* newfont = V_GetFont(sc.String); + if (newfont != nullptr) desc->textFont = newfont; + } + else + { + bool success = false; + FStringf buildname("ImageScrollerPage%s", sc.String); + // Handle one special case: MapControl maps to Control with one parameter different + PClass* cls = PClass::FindClass(buildname); + if (cls != nullptr && cls->IsDescendantOf("ImageScrollerPage")) + { + auto func = dyn_cast(cls->FindSymbol("Init", true)); + if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protected init method. + { + auto& args = func->Variants[0].Proto->ArgumentTypes; + TArray params; + + int start = 1; + + params.Push(0); + if (args.Size() > 1 && args[1] == NewPointer(PClass::FindClass("ImageScrollerDescriptor"))) + { + params.Push(desc); + start = 2; + } + auto TypeCVar = NewPointer(NewStruct("CVar", nullptr, true)); + + // Note that this array may not be reallocated so its initial size must be the maximum possible elements. + TArray strings(args.Size()); + for (unsigned i = start; i < args.Size(); i++) + { + sc.MustGetString(); + if (args[i] == TypeString) + { + strings.Push(sc.String); + params.Push(&strings.Last()); + } + else if (args[i] == TypeName) + { + params.Push(FName(sc.String).GetIndex()); + } + else if (args[i] == TypeColor) + { + params.Push(V_GetColor(nullptr, sc)); + } + else if (args[i]->isIntCompatible()) + { + char* endp; + int v = (int)strtoll(sc.String, &endp, 0); + if (*endp != 0) + { + // special check for font color ranges. + v = V_FindFontColor(sc.String); + if (v == CR_UNTRANSLATED && !sc.Compare("untranslated")) + { + // todo: check other data types that may get used. + sc.ScriptError("Integer expected, got %s", sc.String); + } + } + if (args[i] == TypeBool) v = !!v; + params.Push(v); + } + else if (args[i]->isFloat()) + { + char* endp; + double v = strtod(sc.String, &endp); + if (*endp != 0) + { + sc.ScriptError("Float expected, got %s", sc.String); + } + params.Push(v); + } + else if (args[i] == TypeCVar) + { + auto cv = FindCVar(sc.String, nullptr); + if (cv == nullptr && *sc.String) + { + if (func->Variants[0].ArgFlags[i] & VARF_Optional) + sc.ScriptMessage("Unknown CVar %s", sc.String); + else + sc.ScriptError("Unknown CVar %s", sc.String); + } + params.Push(cv); + } + else + { + sc.ScriptError("Invalid parameter type %s for image page", args[i]->DescriptiveName()); + } + if (sc.CheckString(",")) + { + if (i == args.Size() - 1) + { + sc.ScriptError("Too many parameters for %s", cls->TypeName.GetChars()); + } + } + else + { + if (i < args.Size() - 1 && !(func->Variants[0].ArgFlags[i + 1] & VARF_Optional)) + { + sc.ScriptError("Insufficient parameters for %s", cls->TypeName.GetChars()); + } + break; + } + } + + DMenuItemBase* item = (DMenuItemBase*)cls->CreateNew(); + params[0] = item; + VMCallWithDefaults(func->Variants[0].Implementation, params, nullptr, 0); + desc->mItems.Push((DMenuItemBase*)item); + + success = true; + } + } + if (!success) + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseImageScroller(FScanner& sc) +{ + sc.MustGetString(); + + DImageScrollerDescriptor* desc = Create(); + + desc->textBackground.SetInvalid(); + desc->textBackgroundBrightness = 0xffffffff; + desc->textFont = SmallFont; + desc->textScale = 1; + desc->mAnimatedTransition = false; + + ParseImageScrollerBody(sc, desc); + bool scratch = ReplaceMenu(sc, desc); + if (scratch) delete desc; +} + + //============================================================================= // // @@ -1118,6 +1327,10 @@ void M_ParseMenuDefs() I_FatalError("You cannot add menu items to the menu default settings."); } } + else if (sc.Compare("IMAGESCROLLER")) + { + ParseImageScroller(sc); + } else { sc.ScriptError("Unknown keyword '%s'", sc.String); diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index ac14f74c3..813a0adbb 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -137,8 +137,6 @@ bool AppActive = true; FString currentGame; FString LumpFilter; -TMap NameToTileIndex; // for assigning names to tiles. The menu accesses this list. By default it gets everything from the dynamic tile map in Duke Nukem and Redneck Rampage. - // Todo: Add additional definition file for the other games or textures not in that list so that the menu does not have to rely on indices. CVAR(Bool, queryiwad, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(String, defaultiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG); @@ -1822,6 +1820,7 @@ static const gamefilter games[] = { { "Blood", GAMEFLAG_BLOOD}, { "ShadowWarrior", GAMEFLAG_SW}, { "Exhumed", GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED}, + { "Plutopak", GAMEFLAG_PLUTOPAK}, { "Worldtour", GAMEFLAG_WORLDTOUR}, { "Shareware", GAMEFLAG_SHAREWARE}, }; diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index 13a96e46c..67a5c738b 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -23,12 +23,10 @@ extern bool r_NoInterpolate; struct MapRecord; extern MapRecord* g_nextmap; -extern int g_nextskill; +extern int g_nextskill; extern FMemArena dump; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown. -extern TMap NameToTileIndex; - int CONFIG_Init(); // I am not sure if anything below will survive for long... diff --git a/source/games/duke/src/namelist_d.h b/source/games/duke/src/namelist_d.h index 7f62378b0..e098e67b4 100644 --- a/source/games/duke/src/namelist_d.h +++ b/source/games/duke/src/namelist_d.h @@ -518,6 +518,9 @@ x(INGAMEDUKETHREEDEE, 2499) x(TENSCREEN, 2500) x(PLUTOPAKSPRITE, 2501) x(MENUPLUTOPAKSPRITE, 2503) +x(CREDITPAGE1, 2504) +x(CREDITPAGE2, 2505) +x(CREDITPAGE3, 2506) x(DEVISTATOR, 2510) x(KNEE, 2521) x(CROSSHAIR, 2523) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ed9c4a4fa..1e9ec8c48 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -199,19 +199,15 @@ LISTMENU "SkillMenu" // //------------------------------------------------------------------------------------------- -/* ImageScroller "HelpMenu" { ifgame(Duke, Nam, WW2GI) { - ImageItem "#3280" - ImageItem "#2445" - class "$.ImageScrollerMenu" - ifgame(Duke, Nam, WW2GI) - { - animatedtransition - } + ImageItem "TEXTSTORY" + ImageItem "F1HELP" + animatedtransition } + /* ifgame(Redneck, RedneckRides) { ImageItem "#2541" @@ -242,8 +238,8 @@ ImageScroller "HelpMenu" ImageItem "#5261" } } + */ } -*/ //------------------------------------------------------------------------------------------- // @@ -256,17 +252,16 @@ ImageScroller "HelpMenu" // //------------------------------------------------------------------------------------------- -/* ImageScroller "CreditsMenu" { ifgame(Duke, Nam, WW2GI) { - ImageItem "#2504" - ImageItem "#2505" - ImageItem "#2506" + ImageItem "CREDITPAGE1" + ImageItem "CREDITPAGE2" + ImageItem "CREDITPAGE3" animatedtransition - class "Duke.ImageScrollerMenu" } + /* ifgame(Redneck) { // no point putting this into the string table. @@ -346,8 +341,8 @@ ImageScroller "CreditsMenu" ImageItem "#4979" ImageItem "#5113" } + */ } -*/ //------------------------------------------------------------------------------------------- // diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 9ce361f43..6de3c1913 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -20,6 +20,7 @@ version "4.3" #include "zscript/ui/menu/reverbedit.zs" #include "zscript/ui/menu/textentermenu.zs" #include "zscript/ui/menu/menucustomize.zs" +#include "zscript/ui/menu/imagescroller.zs" #include "zscript/games/duke/ui/menu.zs" #include "zscript/games/blood/ui/menu.zs" diff --git a/wadsrc/static/zscript/base.zs b/wadsrc/static/zscript/base.zs index fb381262a..65d3d47b2 100644 --- a/wadsrc/static/zscript/base.zs +++ b/wadsrc/static/zscript/base.zs @@ -399,6 +399,7 @@ struct Screen native 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); + native static Vector2 SetOffset(double x, double y); } struct Font native diff --git a/wadsrc/static/zscript/ui/menu/imagescroller.zs b/wadsrc/static/zscript/ui/menu/imagescroller.zs new file mode 100644 index 000000000..f0a2fadb2 --- /dev/null +++ b/wadsrc/static/zscript/ui/menu/imagescroller.zs @@ -0,0 +1,269 @@ +/* +** imagescroller.cpp +** Scrolls through multiple fullscreen image pages, +** +**--------------------------------------------------------------------------- +** Copyright 2019-220 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +class ImageScrollerDescriptor : MenuDescriptor native +{ + native Array mItems; + native Font textFont; + native TextureID textBackground; + native Color textBackgroundBrightness; + native double textScale; + native bool mAnimatedTransition; + native int virtWidth, virtHeight; +} + +class ImageScrollerPage : MenuItemBase +{ + int virtWidth, virtHeight; + + protected void DrawText(Font fnt, int color, double x, double y, String text) + { + screen.DrawText(fnt, color, x, y, text, DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43); + } + + protected void DrawTexture(TextureID tex, double x, double y) + { + screen.DrawTexture(tex, true, x, y, DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43); + } +} + +//============================================================================= +// +// an image page +// +//============================================================================= + +class ImageScrollerPageImageItem : ImageScrollerPage +{ + TextureID mTexture; + + void Init(ImageScrollerDescriptor desc, String patch) + { + Super.Init(); + mTexture = TexMan.CheckForTexture(patch); + } + + override void Drawer(bool selected) + { + Screen.DrawTexture(mTexture, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//============================================================================= +// +// a simple text page +// +//============================================================================= + +class ImageScrollerPageTextItem : ImageScrollerPage +{ + Font mFont; + BrokenLines mText; + TextureID mTexture; + Color mBrightness; + double mTextScale; + + void Init(ImageScrollerDescriptor desc, String txt, int y = -1) + { + Super.Init(); + mTexture = desc.textBackground; + mBrightness = desc.textBackgroundBrightness; + mFont = desc.textFont; + mTextScale = desc.textScale; + virtWidth = desc.virtWidth; + virtHeight = desc.virtHeight; + mText = mFont.BreakLines(Stringtable.Localize(txt), virtWidth / mTextScale); + mYpos = y >= 0? y : virtHeight / 2 - mText.Count() * mFont.GetHeight() / 2; + + } + + override void Drawer(bool selected) + { + Screen.DrawTexture(mTexture, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_Color, mBrightness); + + int fontheight = mFont.GetHeight() * mTextScale; + let y = mYpos; + let c = mText.Count(); + for (int i = 0; i < c; i++) + { + screen.DrawText (mFont, Font.CR_UNTRANSLATED, virtWidth/2 - mText.StringWidth(i) * mTextScale / 2, y, mText.StringAt(i), DTA_ScaleX, mTextScale, DTA_ScaleY, mTextScale, + DTA_VirtualWidth, virtWidth, DTA_VirtualHeight, virtHeight, DTA_FullscreenScale, FSMode_ScaleToFit43); + y += fontheight; + } + } +} + +//============================================================================= +// +// The main class +// +//============================================================================= + +class ImageScrollerMenu : Menu +{ + ImageScrollerPage previous; + ImageScrollerPage current; + + double start; + int length; + int dir; + int index; + ImageScrollerDescriptor mDesc; + + + private void StartTransition(ImageScrollerPage to, int animtype) + { + if (AnimatedTransition) + { + start = MSTime() * (120. / 1000.); + length = 30; + dir = animtype; + previous = current; + } + current = to; + } + + void Init(Menu parent, ImageScrollerDescriptor desc) + { + mParentMenu = parent; + index = 0; + mDesc = desc; + AnimatedTransition = desc.mAnimatedTransition; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mDesc.mItems.Size() <= 1) + { + if (mkey == MKEY_Enter) mkey = MKEY_Back; + else if (mkey == MKEY_Right || mkey == MKEY_Left) return true; + } + switch (mkey) + { + case MKEY_Back: + // Before going back the currently running transition must be terminated. + previous = null; + return Super.MenuEvent(mkey, fromcontroller); + + + case MKEY_Left: + if (previous == null) + { + if (--index < 0) index = mDesc.mItems.Size() - 1; + let next = mDesc.mItems[index]; + StartTransition(next, -1); + MenuSound("menu/choose"); + } + return true; + + case MKEY_Right: + case MKEY_Enter: + if (previous == null) + { + int oldindex = index; + if (++index >= mDesc.mItems.Size()) index = 0; + let next = mDesc.mItems[index]; + StartTransition(next, 1); + MenuSound("menu/choose"); + } + return true; + + default: + return Super.MenuEvent(mkey, fromcontroller); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + // Todo: Implement some form of drag event to switch between pages. + if (type == MOUSE_Release) + { + return MenuEvent(MKEY_Enter, false); + } + return Super.MouseEvent(type, x, y); + } + + //============================================================================= + // + // + // + //============================================================================= + + private bool DrawTransition() + { + double now = MSTime() * (120. / 1000.); + if (now < start + length) + { + double factor = screen.GetWidth()/2; + double phase = (now - start) / length * 180. + 90.; + + screen.SetOffset(0, factor * dir * (sin(phase) - 1.)); + previous.Drawer(false); + screen.SetOffset(0, factor * dir * (sin(phase) + 1.)); + current.Drawer(false); + screen.SetOffset(0, 0); + return true; + } + previous = null; + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer() + { + if (previous != null) + { + if (DrawTransition()) return; + previous = null; + } + current.Drawer(false); + } +}