- image scroller menus working.

This commit is contained in:
Christoph Oelckers 2019-11-28 23:27:10 +01:00
parent 48f62ef1ed
commit 943cb64eda
8 changed files with 266 additions and 94 deletions

View file

@ -827,6 +827,7 @@ set (PCH_SOURCES
common/music/backend/oalsound.cpp
common/music/backend/i_sound.cpp
common/menu/imagescroller.cpp
common/menu/joystickmenu.cpp
common/menu/listmenu.cpp
common/menu/loadsavemenu.cpp

View file

@ -0,0 +1,177 @@
/*
** imagescroller.cpp
** Scrolls through multiple fullscreen image pages,
**
**---------------------------------------------------------------------------
** Copyright 2019 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.
**---------------------------------------------------------------------------
**
*/
#include "v_font.h"
#include "cmdlib.h"
#include "gstrings.h"
#include "d_gui.h"
#include "d_event.h"
#include "menu.h"
#include "v_draw.h"
#include "baselayer.h"
#include "gamecontrol.h"
#include "build.h"
//=============================================================================
//
// Show a fullscreen image / centered text screen for an image scroller
//
//=============================================================================
class ImageScreen : public DMenu // Todo: This should be global
{
const FImageScrollerDescriptor::ScrollerItem* mDesc;
public:
ImageScreen(const FImageScrollerDescriptor::ScrollerItem* it)
{
mDesc = it;
}
void Drawer() override;
};
//=============================================================================
//
// Fullscreen image drawer (move to its own source file!)
//
//=============================================================================
void ImageScreen::Drawer()
{
if (mDesc->type == 0)
{
auto tileindexp = NameToTileIndex.CheckKey(FName(mDesc->text, true));
int tileindex;
if (tileindexp == nullptr)
{
// If this isn't a name, try a literal tile index;
auto c = mDesc->text.GetChars();
if (*c == '#') tileindex = (int)strtoll(c + 1, nullptr, 0);
// Error out if the screen cannot be found, this is always a definition error that needs to be reported.
else I_Error("Invalid menu screen '%s'", mDesc->text.GetChars());
}
else tileindex = *tileindexp;
if (!gi->DrawSpecialScreen(origin, tileindex)) // allows the front end to do custom handling for a given image.
{
rotatesprite_fs(int(origin.X * 65536) + (160 << 16), int(origin.Y * 65536) + (100 << 16), 65536L, 0, tileindex, 0, 0, 10 + 64);
}
}
else
{
gi->DrawCenteredTextScreen(origin, mDesc->text, mDesc->type);
}
}
DImageScrollerMenu::DImageScrollerMenu(DMenu* parent, FImageScrollerDescriptor* desc)
: DMenu(parent)
{
index = 0;
mDesc = desc;
canAnimate = !!(mDesc->mFlags & LMF_Animate);
mCurrent = new ImageScreen(&mDesc->mItems[0]);
mCurrent->canAnimate = canAnimate;
}
bool DImageScrollerMenu::MenuEvent(int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
// Before going back the currently running transition must be terminated.
pageTransition.previous = nullptr;
pageTransition.current->origin = { 0,0 };
return DMenu::MenuEvent(mkey, fromcontroller);
case MKEY_Left:
if (pageTransition.previous == nullptr)
{
if (--index < 0) index = mDesc->mItems.Size() - 1;
auto next = new ImageScreen(&mDesc->mItems[index]);
next->canAnimate = canAnimate;
if (!pageTransition.StartTransition(mCurrent, next, MA_Return))
{
delete mCurrent;
}
mCurrent = next;
//gi->MenuSound(GameInterface::ChooseSound);
}
return true;
case MKEY_Right:
case MKEY_Enter:
if (pageTransition.previous == nullptr)
{
if (++index >= (int)mDesc->mItems.Size()) index = 0;
auto next = new ImageScreen(&mDesc->mItems[index]);
next->canAnimate = canAnimate;
if (!pageTransition.StartTransition(mCurrent, next, MA_Advance))
{
delete mCurrent;
}
mCurrent = next;
//gi->MenuSound(GameInterface::ChooseSound);
}
return true;
default:
return DMenu::MenuEvent(mkey, fromcontroller);
}
}
bool DImageScrollerMenu::MouseEvent(int type, int x, int y)
{
// Todo: Implement some form of drag event to switch between pages.
return DMenu::MouseEvent(type, x, y);
}
void DImageScrollerMenu::Ticker()
{
}
void DImageScrollerMenu::Drawer()
{
if (pageTransition.previous != nullptr)
{
auto res = pageTransition.Draw();
if (res) return;
delete pageTransition.previous;
pageTransition.previous = nullptr;
}
mCurrent->origin = origin;
mCurrent->Drawer();
mCurrent->origin = {};
}

View file

@ -621,34 +621,3 @@ int FListMenuItemPatch::GetWidth()
: 0;
}
//=============================================================================
//
// Fullscreen image drawer (move to its own source file!)
//
//=============================================================================
void ImageScreen::Drawer()
{
if (mDesc->type == 0)
{
auto tileindexp = NameToTileIndex.CheckKey(FName(mDesc->text, true));
int tileindex;
if (tileindexp == nullptr)
{
// If this isn't a name, try a literal tile index;
auto c = mDesc->text.GetChars();
if (*c == '#') tileindex = (int)strtoll(c+1, nullptr, 0);
// Error out if the screen cannot be found, this is always a definition error that needs to be reported.
else I_Error("Invalid menu screen '%s'", mDesc->text.GetChars());
}
else tileindex = *tileindexp;
if (!gi->DrawSpecialScreen(origin, tileindex)) // allows the front end to do custom handling for a given image.
{
rotatesprite_fs(int(origin.X * 65536) + (160<<16), int(origin.Y * 65536) + (100<<16), 65536L,0,tileindex,0,0,10+64);
}
}
else
{
gi->DrawCenteredTextScreen(origin, mDesc->text, mDesc->type);
}
}

View file

@ -957,3 +957,11 @@ bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller)
return false;
}
static TMenuClassDescriptor<DLoadMenu> _lm("LoadMenu");
static TMenuClassDescriptor<DSaveMenu> _sm("SaveMenu");
void RegisterRedneckMenus()
{
menuClasses.Push(&_sm);
menuClasses.Push(&_lm);
}

View file

@ -54,6 +54,7 @@
void RegisterDukeMenus();
void RegisterRedneckMenus();
void RegisterLoadsaveMenus();
extern bool rotatesprite_2doverride;
bool help_disabled, credits_disabled;
int g_currentMenu; // accessible by CON scripts - contains the current menu's script ID if defined or INT_MAX if none given.
@ -89,26 +90,10 @@ static bool MenuEnabled = true;
#define KEY_REPEAT_DELAY (MENU_TICRATE*5/12)
#define KEY_REPEAT_RATE (3)
enum MenuTransitionType
{ // Note: This enum is for logical categories, not visual types.
MA_None,
MA_Return,
MA_Advance,
} ;
struct MenuTransition
{
DMenu *previous;
DMenu *current;
int32_t start;
int32_t length;
int32_t dir;
};
static MenuTransition transition;
bool M_StartTransition(DMenu *from, DMenu *to, MenuTransitionType animtype, MenuTransition &transition)
bool MenuTransition::StartTransition(DMenu *from, DMenu *to, MenuTransitionType animtype)
{
if (!from->canAnimate || !to->canAnimate || animtype == MA_None)
{
@ -116,27 +101,26 @@ bool M_StartTransition(DMenu *from, DMenu *to, MenuTransitionType animtype, Menu
}
else
{
transition.start = (int32_t) totalclock;
transition.length = 30;
transition.dir = animtype == MA_Advance? 1 : -1;
transition.previous = from;
transition.current = to;
start = (int32_t) totalclock;
length = 300;
dir = animtype == MA_Advance? 1 : -1;
previous = from;
current = to;
return true;
}
}
bool M_DrawTransition(MenuTransition &transition)
bool MenuTransition::Draw()
{
if (totalclock < transition.start + transition.length)
if (totalclock < start + length)
{
double factor = 120 * xdim / ydim;
double phase = ((int32_t) totalclock - transition.start) / double(transition.length) * M_PI + M_PI/2;
double phase = ((int32_t) totalclock - start) / double(length) * M_PI + M_PI/2;
transition.previous->origin.X = factor * transition.dir * (sin(phase) - 1.);
transition.current->origin.X = factor * transition.dir * (sin(phase) + 1.);
Printf("prev.X = %2.5f, next.X = %2.5f\n", transition.previous->origin.X, transition.current->origin.X);
transition.previous->Drawer();
transition.current->Drawer();
previous->origin.X = factor * dir * (sin(phase) - 1.);
current->origin.X = factor * dir * (sin(phase) + 1.);
previous->Drawer();
current->Drawer();
return true;
}
return false;
@ -231,7 +215,7 @@ void DMenu::Close ()
assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu;
if (mParentMenu && M_StartTransition(this, mParentMenu, MA_Return, transition))
if (mParentMenu && transition.StartTransition(this, mParentMenu, MA_Return))
{
g_currentMenu = DMenu::CurrentMenu->scriptID;
}
@ -415,7 +399,7 @@ void M_ActivateMenu(DMenu *menu)
if (DMenu::CurrentMenu != NULL)
{
DMenu::CurrentMenu->ReleaseCapture();
M_StartTransition(DMenu::CurrentMenu, menu, MA_Advance, transition);
transition.StartTransition(DMenu::CurrentMenu, menu, MA_Advance);
}
DMenu::CurrentMenu = menu;
}
@ -556,6 +540,15 @@ bool M_SetMenu(FName menu, int param, FName caller)
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
else if ((*desc)->mType == MDESC_ImageScroller)
{
FImageScrollerDescriptor* ld = static_cast<FImageScrollerDescriptor*>(*desc);
if (ld->mItems.Size() > 0) // only open the submenu if it isn't empty.
{
DImageScrollerMenu* newmenu = new DImageScrollerMenu(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
}
return true;
}
else
@ -824,7 +817,7 @@ void M_Drawer (void)
bool going = false;
if (transition.previous)
{
going = M_DrawTransition(transition);
going = transition.Draw();
if (!going)
{
if (transition.dir == -1) delete transition.previous;
@ -876,6 +869,7 @@ void M_Init (void)
{
RegisterDukeMenus();
RegisterRedneckMenus();
RegisterLoadsaveMenus();
timerSetCallback(M_Ticker);
M_ParseMenuDefs();
}

View file

@ -32,6 +32,29 @@ const int MENU_TICRATE = 30;
extern bool help_disabled, credits_disabled;
extern int g_currentMenu;
enum MenuTransitionType
{ // Note: This enum is for logical categories, not visual types.
MA_None,
MA_Return,
MA_Advance,
};
class DMenu;
struct MenuTransition
{
DMenu* previous;
DMenu* current;
int32_t start;
int32_t length;
int32_t dir;
bool StartTransition(DMenu* from, DMenu* to, MenuTransitionType animtype);
bool Draw();
};
enum
{
EF_HIDEFROMSP = 1 << 0,
@ -262,7 +285,8 @@ struct FImageScrollerDescriptor : public FMenuDescriptor
int scriptID;
FString text;
};
int mFlags = 0;
TArray<ScrollerItem> mItems;
};
@ -635,23 +659,17 @@ public:
class DImageScrollerMenu : public DMenu
{
};
DMenu* mCurrent;
FImageScrollerDescriptor* mDesc;
int index;
MenuTransition pageTransition = {};
//=============================================================================
//
// Show a fullscreen image / centered text screen for an image scroller
//
//=============================================================================
class ImageScreen : public DMenu // Todo: This should be global
{
const FImageScrollerDescriptor::ScrollerItem *mDesc;
ImageScreen(const FImageScrollerDescriptor::ScrollerItem *it)
{
mDesc = it;
}
void Drawer() override;
public:
DImageScrollerMenu(DMenu* parent = nullptr, FImageScrollerDescriptor* desc = nullptr);
bool MenuEvent(int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker();
void Drawer();
};
//=============================================================================

View file

@ -586,15 +586,16 @@ static void ParseImageScrollerBody(FScanner &sc, FImageScrollerDescriptor *desc)
else if (sc.Compare("TextItem") || sc.Compare("ImageItem"))
{
FImageScrollerDescriptor::ScrollerItem item;
sc.MustGetString();
item.text = sc.String;
int type = sc.Compare("TextItem");
sc.MustGetString();
item.text = strbin1(sc.String);
if (type)
{
sc.MustGetStringName(",");
sc.MustGetNumber();
item.type = sc.Number; // y-coordinate
}
else item.type = 0;
item.scriptID = INT_MAX;
if (sc.CheckString(","))
{
@ -603,6 +604,10 @@ static void ParseImageScrollerBody(FScanner &sc, FImageScrollerDescriptor *desc)
}
desc->mItems.Push(item);
}
else if (sc.Compare("animatedtransition"))
{
desc->mFlags |= LMF_Animate;
}
else
{
sc.ScriptError("Unknown keyword '%s'", sc.String);

View file

@ -361,20 +361,18 @@ LISTMENU "MultiMenu"
//
//
//-------------------------------------------------------------------------------------------
/*
ImageScroller "HelpMenu"
{
ifgame(Duke, Nam, WW2GI, Fury)
ifgame(Duke, Nam, WW2GI, Fury, Redneck, RedneckRides)
{
ImageItem "TEXTSTORY", 400
ImageItem "F1HELP", 401
}
ifgame(Redneck, RedneckRides)
{
ImageItem "TEXTSTORY"
ImageItem "F1HELP"
ImageItem "RRTILE1636"
ifgame(RedneckRides)
{
ImageItem "RRTILE1636"
}
animatedtransition
}
}
@ -396,6 +394,7 @@ ImageScroller "CreditsMenu"
ImageItem "CREDITSTEXT1", 990
ImageItem "CREDITSTEXT2", 991
ImageItem "CREDITSTEXT3", 992
animatedtransition
}
ifgame(Redneck)
{
@ -408,7 +407,7 @@ ImageScroller "CreditsMenu"
TextItem "SENIOR ANIMATOR AND ARTIST\n\nJASON HOOVER", 80
TextItem "TECHNICAL DIRECTOR\n\nBARRY DEMPSEY", 80
TextItem "MOTION CAPTURE SPECIALIST AND\nCHARACTER ANIMATION\nAMIT DORON\n\nA.I. PROGRAMMING\nARTHUR DONAVAN\n\nADDITIONAL ANIMATION\nGEORGE KARL", 60
TextItem "CHARACTER DESIGN\nCORKY LEHMKUHL\n\nMAP PAINTERS\n"VIKTOR ANTONOV\nMATTHIAS BEEGUER\nSTEPHAN BURLE\n\nSCULPTORS\nGEORGE ENGEL\nJAKE GARBER\nJEFF HIMMEL", 50
TextItem "CHARACTER DESIGN\nCORKY LEHMKUHL\n\nMAP PAINTERS\nVIKTOR ANTONOV\nMATTHIAS BEEGUER\nSTEPHAN BURLE\n\nSCULPTORS\nGEORGE ENGEL\nJAKE GARBER\nJEFF HIMMEL", 50
TextItem "CHARACTER VOICES\n\nLEONARD\nBURTON GILLIAM\n\nBUBBA, BILLY RAY, SKINNY OL' COOT\nAND THE TURD MINION\nDREW MARKHAM\n\nSHERIFF LESTER T. HOBBES\nMOJO NIXON\n\nALIEN VIXEN\nPEGGY JO JACOBS", 40
TextItem "SOUND DESIGN\nGARY BRADFIELD\n\nMUSIC\nMOJO NIXON\nTHE BEAT FARMERS\nTHE REVEREND HORTON HEAT\nCEMENT POND\n\nADDITIONAL SOUND EFFECTS\nJIM SPURGIN", 50
TextItem "MOTION CAPTURE ACTOR\nJ.P. MANOUX\n\nMOTION CAPTURE VIXEN\nSHAWN WOLFE", 80
@ -423,6 +422,7 @@ ImageScroller "CreditsMenu"
TextItem "SPECIAL THANKS\n\nJIM GAUER\nPAUL VAIS\nSCOTT MILLER\nTODD REPLOGLE\nCHUCK BUECHE\nCARTER LIPSCOMB\nJOHN CONLEY\nDON MAGGI", 60
TextItem "EXTRA SPECIAL THANKS\n\nBRIAN FARGO", 80
TextItem "REDNECK RAMPAGE\n(c) 1997 XATRIX ENTERTAINMENT, INC.\n\nREDNECK RAMPAGE IS A TRADEMARK OF\nINTERPLAY PRODUCTIONS", 60
animatedtransition
}
ifgame(RedneckRides)
{
@ -456,7 +456,7 @@ ImageScroller "CreditsMenu"
TextItem "SPECIAL THANKS\n\nSCOTT MILLER\nGEORGE BROUSSARD", 80
TextItem "EXTRA SPECIAL THANKS\n\nBRIAN FARGO", 80
TextItem "REDNECK RAMPAGE RIDES AGAIN\n(c) 1998 XATRIX ENTERTAINMENT, INC.\n\nREDNECK RAMPAGE RIDES AGAIN\nIS A TRADEMARK OF\nINTERPLAY PRODUCTIONS", 70
animatedtransition
}
}
*/