- 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/oalsound.cpp
common/music/backend/i_sound.cpp common/music/backend/i_sound.cpp
common/menu/imagescroller.cpp
common/menu/joystickmenu.cpp common/menu/joystickmenu.cpp
common/menu/listmenu.cpp common/menu/listmenu.cpp
common/menu/loadsavemenu.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; : 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; 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 RegisterDukeMenus();
void RegisterRedneckMenus(); void RegisterRedneckMenus();
void RegisterLoadsaveMenus();
extern bool rotatesprite_2doverride; extern bool rotatesprite_2doverride;
bool help_disabled, credits_disabled; 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. 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_DELAY (MENU_TICRATE*5/12)
#define KEY_REPEAT_RATE (3) #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; 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) if (!from->canAnimate || !to->canAnimate || animtype == MA_None)
{ {
@ -116,27 +101,26 @@ bool M_StartTransition(DMenu *from, DMenu *to, MenuTransitionType animtype, Menu
} }
else else
{ {
transition.start = (int32_t) totalclock; start = (int32_t) totalclock;
transition.length = 30; length = 300;
transition.dir = animtype == MA_Advance? 1 : -1; dir = animtype == MA_Advance? 1 : -1;
transition.previous = from; previous = from;
transition.current = to; current = to;
return true; 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 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.); previous->origin.X = factor * dir * (sin(phase) - 1.);
transition.current->origin.X = factor * transition.dir * (sin(phase) + 1.); current->origin.X = factor * dir * (sin(phase) + 1.);
Printf("prev.X = %2.5f, next.X = %2.5f\n", transition.previous->origin.X, transition.current->origin.X); previous->Drawer();
transition.previous->Drawer(); current->Drawer();
transition.current->Drawer();
return true; return true;
} }
return false; return false;
@ -231,7 +215,7 @@ void DMenu::Close ()
assert(DMenu::CurrentMenu == this); assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu; 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; g_currentMenu = DMenu::CurrentMenu->scriptID;
} }
@ -415,7 +399,7 @@ void M_ActivateMenu(DMenu *menu)
if (DMenu::CurrentMenu != NULL) if (DMenu::CurrentMenu != NULL)
{ {
DMenu::CurrentMenu->ReleaseCapture(); DMenu::CurrentMenu->ReleaseCapture();
M_StartTransition(DMenu::CurrentMenu, menu, MA_Advance, transition); transition.StartTransition(DMenu::CurrentMenu, menu, MA_Advance);
} }
DMenu::CurrentMenu = menu; DMenu::CurrentMenu = menu;
} }
@ -556,6 +540,15 @@ bool M_SetMenu(FName menu, int param, FName caller)
newmenu->Init(DMenu::CurrentMenu, ld); newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu); 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; return true;
} }
else else
@ -824,7 +817,7 @@ void M_Drawer (void)
bool going = false; bool going = false;
if (transition.previous) if (transition.previous)
{ {
going = M_DrawTransition(transition); going = transition.Draw();
if (!going) if (!going)
{ {
if (transition.dir == -1) delete transition.previous; if (transition.dir == -1) delete transition.previous;
@ -876,6 +869,7 @@ void M_Init (void)
{ {
RegisterDukeMenus(); RegisterDukeMenus();
RegisterRedneckMenus(); RegisterRedneckMenus();
RegisterLoadsaveMenus();
timerSetCallback(M_Ticker); timerSetCallback(M_Ticker);
M_ParseMenuDefs(); M_ParseMenuDefs();
} }

View file

@ -32,6 +32,29 @@ const int MENU_TICRATE = 30;
extern bool help_disabled, credits_disabled; extern bool help_disabled, credits_disabled;
extern int g_currentMenu; 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 enum
{ {
EF_HIDEFROMSP = 1 << 0, EF_HIDEFROMSP = 1 << 0,
@ -262,6 +285,7 @@ struct FImageScrollerDescriptor : public FMenuDescriptor
int scriptID; int scriptID;
FString text; FString text;
}; };
int mFlags = 0;
TArray<ScrollerItem> mItems; TArray<ScrollerItem> mItems;
}; };
@ -635,23 +659,17 @@ public:
class DImageScrollerMenu : public DMenu class DImageScrollerMenu : public DMenu
{ {
DMenu* mCurrent;
FImageScrollerDescriptor* mDesc;
int index;
MenuTransition pageTransition = {};
}; public:
DImageScrollerMenu(DMenu* parent = nullptr, FImageScrollerDescriptor* desc = nullptr);
//============================================================================= bool MenuEvent(int mkey, bool fromcontroller);
// bool MouseEvent(int type, int x, int y);
// Show a fullscreen image / centered text screen for an image scroller void Ticker();
// void Drawer();
//=============================================================================
class ImageScreen : public DMenu // Todo: This should be global
{
const FImageScrollerDescriptor::ScrollerItem *mDesc;
ImageScreen(const FImageScrollerDescriptor::ScrollerItem *it)
{
mDesc = it;
}
void Drawer() override;
}; };
//============================================================================= //=============================================================================

View file

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

View file

@ -361,21 +361,19 @@ LISTMENU "MultiMenu"
// //
// //
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
/*
ImageScroller "HelpMenu" ImageScroller "HelpMenu"
{ {
ifgame(Duke, Nam, WW2GI, Fury) ifgame(Duke, Nam, WW2GI, Fury, Redneck, RedneckRides)
{ {
ImageItem "TEXTSTORY", 400 ImageItem "TEXTSTORY", 400
ImageItem "F1HELP", 401 ImageItem "F1HELP", 401
} ifgame(RedneckRides)
ifgame(Redneck, RedneckRides)
{ {
ImageItem "TEXTSTORY"
ImageItem "F1HELP"
ImageItem "RRTILE1636" ImageItem "RRTILE1636"
} }
animatedtransition
}
} }
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
@ -396,6 +394,7 @@ ImageScroller "CreditsMenu"
ImageItem "CREDITSTEXT1", 990 ImageItem "CREDITSTEXT1", 990
ImageItem "CREDITSTEXT2", 991 ImageItem "CREDITSTEXT2", 991
ImageItem "CREDITSTEXT3", 992 ImageItem "CREDITSTEXT3", 992
animatedtransition
} }
ifgame(Redneck) ifgame(Redneck)
{ {
@ -408,7 +407,7 @@ ImageScroller "CreditsMenu"
TextItem "SENIOR ANIMATOR AND ARTIST\n\nJASON HOOVER", 80 TextItem "SENIOR ANIMATOR AND ARTIST\n\nJASON HOOVER", 80
TextItem "TECHNICAL DIRECTOR\n\nBARRY DEMPSEY", 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 "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 "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 "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 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 "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 "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 TextItem "REDNECK RAMPAGE\n(c) 1997 XATRIX ENTERTAINMENT, INC.\n\nREDNECK RAMPAGE IS A TRADEMARK OF\nINTERPLAY PRODUCTIONS", 60
animatedtransition
} }
ifgame(RedneckRides) ifgame(RedneckRides)
{ {
@ -456,7 +456,7 @@ ImageScroller "CreditsMenu"
TextItem "SPECIAL THANKS\n\nSCOTT MILLER\nGEORGE BROUSSARD", 80 TextItem "SPECIAL THANKS\n\nSCOTT MILLER\nGEORGE BROUSSARD", 80
TextItem "EXTRA SPECIAL THANKS\n\nBRIAN FARGO", 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 TextItem "REDNECK RAMPAGE RIDES AGAIN\n(c) 1998 XATRIX ENTERTAINMENT, INC.\n\nREDNECK RAMPAGE RIDES AGAIN\nIS A TRADEMARK OF\nINTERPLAY PRODUCTIONS", 70
animatedtransition
} }
} }
*/