Merge branch 'menu2'

# Conflicts:
#	source/exhumed/src/sound.cpp
This commit is contained in:
Christoph Oelckers 2020-10-10 19:03:29 +02:00
commit b06a847d9a
161 changed files with 17715 additions and 13910 deletions

View file

@ -802,6 +802,7 @@ set (PCH_SOURCES
core/palette.cpp core/palette.cpp
core/zcompile.cpp core/zcompile.cpp
core/statusbar.cpp core/statusbar.cpp
core/gi.cpp
core/console/c_console.cpp core/console/c_console.cpp
core/console/d_event.cpp core/console/d_event.cpp
@ -810,7 +811,7 @@ set (PCH_SOURCES
common/audio/sound/oalsound.cpp common/audio/sound/oalsound.cpp
common/audio/sound/s_environment.cpp common/audio/sound/s_environment.cpp
common/audio/sound/s_sound.cpp common/audio/sound/s_sound.cpp
#common/audio/sound/s_reverbedit.cpp common/audio/sound/s_reverbedit.cpp
common/audio/music/music_midi_base.cpp common/audio/music/music_midi_base.cpp
common/audio/music/music.cpp common/audio/music/music.cpp
common/audio/music/i_music.cpp common/audio/music/i_music.cpp
@ -910,6 +911,7 @@ set (PCH_SOURCES
common/filesystem/resourcefile.cpp common/filesystem/resourcefile.cpp
common/engine/cycler.cpp common/engine/cycler.cpp
common/engine/d_event.cpp common/engine/d_event.cpp
common/engine/date.cpp
common/engine/stats.cpp common/engine/stats.cpp
common/engine/sc_man.cpp common/engine/sc_man.cpp
common/engine/palettecontainer.cpp common/engine/palettecontainer.cpp
@ -924,6 +926,13 @@ set (PCH_SOURCES
common/objects/dobject.cpp common/objects/dobject.cpp
common/objects/dobjgc.cpp common/objects/dobjgc.cpp
common/objects/dobjtype.cpp common/objects/dobjtype.cpp
common/menu/joystickmenu.cpp
common/menu/menu.cpp
common/menu/messagebox.cpp
common/menu/optionmenu.cpp
common/menu/resolutionmenu.cpp
common/menu/menudef.cpp
common/menu/savegamemanager.cpp
common/rendering/v_framebuffer.cpp common/rendering/v_framebuffer.cpp
common/rendering/v_video.cpp common/rendering/v_video.cpp
@ -975,22 +984,12 @@ set (PCH_SOURCES
common/scripting/backend/codegen.cpp common/scripting/backend/codegen.cpp
core/textures/buildtiles.cpp
#core/textures/texture.cpp
core/textures/buildtiles.cpp
core/music/s_advsound.cpp core/music/s_advsound.cpp
core/menu/imagescroller.cpp
core/menu/joystickmenu.cpp
core/menu/listmenu.cpp
core/menu/savegamemanager.cpp
core/menu/loadsavemenu.cpp core/menu/loadsavemenu.cpp
core/menu/menu.cpp core/menu/razemenu.cpp
core/menu/menudef.cpp
core/menu/menuinput.cpp
core/menu/messagebox.cpp
core/menu/optionmenu.cpp
core/menu/resolutionmenu.cpp
) )
if( ${HAVE_VM_JIT} ) if( ${HAVE_VM_JIT} )

View file

@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "nnexts.h" #include "nnexts.h"
#include "zstring.h" #include "zstring.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_video.h" #include "v_video.h"

View file

@ -51,7 +51,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gamecontrol.h" #include "gamecontrol.h"
#include "m_argv.h" #include "m_argv.h"
#include "statistics.h" #include "statistics.h"
#include "menu.h" #include "razemenu.h"
#include "raze_sound.h" #include "raze_sound.h"
#include "nnexts.h" #include "nnexts.h"
#include "secrets.h" #include "secrets.h"
@ -61,6 +61,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "choke.h" #include "choke.h"
#include "d_net.h" #include "d_net.h"
#include "v_video.h" #include "v_video.h"
#include "v_draw.h"
#include "texturemanager.h"
#include "statusbar.h" #include "statusbar.h"
BEGIN_BLD_NS BEGIN_BLD_NS
@ -394,6 +396,16 @@ void GameInterface::DrawBackground()
DrawTexture(twod, tileGetTexture(2518, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); DrawTexture(twod, tileGetTexture(2518, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
} }
#define x(a, b) registerName(#a, b);
static void SetTileNames()
{
auto registerName = [](const char* name, int index)
{
TexMan.AddAlias(name, tileGetTexture(index));
};
#include "namelist.h"
}
#undef x
void ReadAllRFS(); void ReadAllRFS();
@ -416,6 +428,7 @@ void GameInterface::app_init()
levelLoadDefaults(); levelLoadDefaults();
LoadDefinitions(); LoadDefinitions();
SetTileNames();
TileFiles.SetBackup(); TileFiles.SetBackup();
powerupInit(); powerupInit();

View file

@ -80,11 +80,8 @@ struct GameInterface : ::GameInterface
void MenuClosed() override; void MenuClosed() override;
bool CanSave() override; bool CanSave() override;
bool StartGame(FNewGameStartup& gs) override; bool StartGame(FNewGameStartup& gs) override;
void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override; bool SaveGame() override;
void DrawMenuCaption(const DVector2& origin, const char* text) override; bool LoadGame() override;
bool SaveGame(FSaveGameNode*) override;
bool LoadGame(FSaveGameNode*) override;
void DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool bg) override;
void QuitToTitle() override; void QuitToTitle() override;
FString GetCoordString() override; FString GetCoordString() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override; ReservedSpace GetReservedScreenSpace(int viewsize) override;

View file

@ -42,59 +42,57 @@ void _consoleSysMsg(const char* pMessage, ...);
#define ThrowError(...) \ #define ThrowError(...) \
{ \ { \
_SetErrorLoc(__FILE__,__LINE__); \
I_Error(__VA_ARGS__); \ I_Error(__VA_ARGS__); \
} }
// print error to console only // print error to console only
#define consoleSysMsg(...) \ #define consoleSysMsg(...) \
{ \ { \
_SetErrorLoc(__FILE__,__LINE__); \
_consoleSysMsg(__VA_ARGS__); \ _consoleSysMsg(__VA_ARGS__); \
} }
#define dassert(x) assert(x) #define dassert(x) assert(x)
#define kMaxSectors MAXSECTORS
#define kMaxWalls MAXWALLS
#define kMaxSprites MAXSPRITES
#define kMaxTiles MAXTILES
#define kMaxStatus MAXSTATUS
#define kMaxPlayers 8
#define kMaxViewSprites maxspritesonscreen #define kMaxViewSprites maxspritesonscreen
#define kMaxVoxels MAXVOXELS enum
{
kMaxSectors = MAXSECTORS,
kMaxWalls = MAXWALLS,
kMaxSprites = MAXSPRITES,
#define kTicRate 120 kMaxTiles = MAXTILES,
#define kTicsPerFrame 4 kMaxStatus = MAXSTATUS,
#define kTicsPerSec (kTicRate/kTicsPerFrame) kMaxPlayers = 8,
kMaxVoxels = MAXVOXELS,
#define TILTBUFFER 4078 kTicRate = 120,
kTicsPerFrame = 4,
kTicsPerSec = (kTicRate / kTicsPerFrame),
#define kExplodeMax 8 kExplodeMax = 8,
#define kLensSize 80 kLensSize = 80,
#define kViewEffectMax 19 kViewEffectMax = 19,
#define kNoTile -1 kNoTile = -1,
// defined by NoOne: //= = = = // = defined = by = NoOne:
// ------------------------------- //= = = = // = -------------------------------
#define kMaxPAL 5 kMaxPAL = 5,
#define kUserPLUStart 15 kUserPLUStart = 15,
#define kDmgFall 0 kDmgFall = 0,
#define kDmgBurn 1 kDmgBurn = 1,
#define kDmgBullet 2 kDmgBullet = 2,
#define kDmgExplode 3 kDmgExplode = 3,
#define kDmgChoke 4 kDmgChoke = 4,
#define kDmgSpirit 5 kDmgSpirit = 5,
#define kDmgElectric 6 kDmgElectric = 6,
#define kDmgMax 7 kDmgMax = 7,
};
// MEDIUM ///////////////////////////////////////////////////// // MEDIUM /////////////////////////////////////////////////////
enum { enum {

View file

@ -26,7 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mmulti.h" #include "mmulti.h"
#include "view.h" #include "view.h"
#include "gamestate.h" #include "gamestate.h"
#include "menu.h" #include "razemenu.h"
BEGIN_BLD_NS BEGIN_BLD_NS

View file

@ -38,7 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "screenjob.h" #include "screenjob.h"
#include "gamestate.h" #include "gamestate.h"
#include "seq.h" #include "seq.h"
#include "menu.h" #include "razemenu.h"
BEGIN_BLD_NS BEGIN_BLD_NS

View file

@ -2,8 +2,9 @@
/* /*
Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT Copyright (C) 2019 Nuke.YKT
Copyright (C) 2020 Christoph Oelckers
This file is part of NBlood. This file is part of Raze.
NBlood is free software; you can redistribute it and/or NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2 modify it under the terms of the GNU General Public License version 2
@ -27,7 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "compat.h" #include "compat.h"
#include "mmulti.h" #include "mmulti.h"
#include "c_bind.h" #include "c_bind.h"
#include "menu.h" #include "razemenu.h"
#include "gamestate.h" #include "gamestate.h"
#include "blood.h" #include "blood.h"
@ -36,6 +37,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "sound.h" #include "sound.h"
#include "v_video.h" #include "v_video.h"
#include "v_draw.h"
#include "vm.h"
bool ShowOptionMenu(); bool ShowOptionMenu();
@ -118,112 +121,34 @@ void CGameMenuItemQAV::Draw(void)
} }
static std::unique_ptr<CGameMenuItemQAV> itemBloodQAV; // This must be global to ensure that the animation remains consistent across menus. static std::unique_ptr<CGameMenuItemQAV> itemBloodQAV; // This must be global to ensure that the animation remains consistent across menus.
void UpdateNetworkMenus(void) void UpdateNetworkMenus(void)
{ {
// For now disable the network menu item as it is not yet functional. // For now disable the network menu item as it is not functional.
for (auto name : { NAME_Mainmenu, NAME_IngameMenu }) for (auto name : { NAME_Mainmenu, NAME_IngameMenu })
{ {
FMenuDescriptor** desc = MenuDescriptors.CheckKey(name); DMenuDescriptor** desc = MenuDescriptors.CheckKey(name);
if (desc != NULL && (*desc)->mType == MDESC_ListMenu) if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{ {
FListMenuDescriptor* ld = static_cast<FListMenuDescriptor*>(*desc); DListMenuDescriptor* ld = static_cast<DListMenuDescriptor*>(*desc);
for (auto& li : ld->mItems) for (auto& li : ld->mItems)
{ {
if (li->GetAction(nullptr) == NAME_MultiMenu) if (li->mAction == NAME_MultiMenu)
{ {
li->mEnabled = false; li->mEnabled = -1;
} }
} }
} }
} }
} }
//----------------------------------------------------------------------------
//
// Implements the native looking menu used for the main menu
// and the episode/skill selection screens, i.e. the parts
// that need to look authentic
//
//----------------------------------------------------------------------------
class BloodListMenu : public DListMenu
{
using Super = DListMenu;
protected:
void PostDraw()
{
// For narrow screens this would be mispositioned so skip drawing it there.
double ratio = screen->GetWidth() / double(screen->GetHeight());
if (ratio > 1.32) itemBloodQAV->Draw();
}
};
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
class BloodImageScreen : public ImageScreen
{
CGameMenuItemQAV anim;
public:
BloodImageScreen(FImageScrollerDescriptor::ScrollerItem* desc)
: ImageScreen(desc), anim(169, 100, mDesc->text.GetChars(), false, true)
{
}
void Drawer() override
{
anim.Draw();
}
};
class DBloodImageScrollerMenu : public DImageScrollerMenu
{
ImageScreen* newImageScreen(FImageScrollerDescriptor::ScrollerItem* desc) override
{
if (desc->type >= 0) return DImageScrollerMenu::newImageScreen(desc);
return new BloodImageScreen(desc);
}
};
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// Menu related game interface functions // Menu related game interface functions
// //
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void GameInterface::DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags)
{
if (!text) return;
int shade = (state != NIT_InactiveState) ? 32 : 48;
int pal = (state != NIT_InactiveState) ? 5 : 5;
if (state == NIT_SelectedState) shade = 32 - (I_GetBuildTime() & 63);
auto gamefont = fontnum == NIT_BigFont ? BigFont : SmallFont;
if (flags & LMF_Centered)
{
int width = gamefont->StringWidth(text);
xpos -= width / 2;
}
DrawText(twod, gamefont, CR_UNDEFINED, xpos+1, ypos+1, text, DTA_Color, 0xff000000, //DTA_Alpha, 0.5,
DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
DrawText(twod, gamefont, CR_UNDEFINED, xpos, ypos, text, DTA_TranslationIndex, TRANSLATION(Translation_Remap, pal), DTA_Color, shadeToLight(shade),
DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
}
void GameInterface::MenuOpened() void GameInterface::MenuOpened()
{ {
itemBloodQAV.reset(new CGameMenuItemQAV(160, 100, "BDRIP.QAV", true)); itemBloodQAV.reset(new CGameMenuItemQAV(160, 100, "BDRIP.QAV", true));
@ -245,7 +170,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
{ {
if (g_gameType & GAMEFLAG_SHAREWARE) if (g_gameType & GAMEFLAG_SHAREWARE)
{ {
M_StartMessage(GStrings("BUYBLOOD"), 1, -1); // unreachable because we do not support Blood SW versions yet. M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet.
return false; return false;
} }
} }
@ -261,41 +186,6 @@ FSavegameInfo GameInterface::GetSaveSig()
return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD }; return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD };
} }
// This also gets used by the summary and the loading screen
void DrawMenuCaption(const char* text)
{
double scalex = 1.; // Expand the box if the text is longer
int width = BigFont->StringWidth(text);
int boxwidth = tileWidth(2038);
if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10);
DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE);
DrawText(twod, BigFont, CR_UNDEFINED, 160 - width/2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
}
void GameInterface::DrawMenuCaption(const DVector2& origin, const char* text)
{
Blood::DrawMenuCaption(text);
}
void GameInterface::DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool bg)
{
if (text)
{
int height = SmallFont->GetHeight();
auto lines = FString(text).Split("\n");
int y = 100 - (height * lines.Size() / 2);
for (auto& l : lines)
{
int width = SmallFont->StringWidth(l);
int x = 160 - width / 2;
DrawText(twod, SmallFont, CR_UNTRANSLATED, x, y, l, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
y += height;
}
}
}
void GameInterface::QuitToTitle() void GameInterface::QuitToTitle()
{ {
Mus_Stop(); Mus_Stop();
@ -304,20 +194,43 @@ void GameInterface::QuitToTitle()
END_BLD_NS END_BLD_NS
using namespace Blood;
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// Class registration //
// //
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(DListMenuItemBloodDripDrawer, Draw)
static TMenuClassDescriptor<Blood::BloodListMenu> _lm("Blood.ListMenu");
static TMenuClassDescriptor<Blood::BloodListMenu> _mm("Blood.MainMenu");
static TMenuClassDescriptor<Blood::DBloodImageScrollerMenu> _im("Blood.ImageScrollerMenu");
void RegisterBloodMenus()
{ {
menuClasses.Push(&_lm); // For narrow screens this would be mispositioned so skip drawing it there.
menuClasses.Push(&_mm); double ratio = screen->GetWidth() / double(screen->GetHeight());
menuClasses.Push(&_im); if (ratio > 1.32) itemBloodQAV->Draw();
return 0;
} }
DEFINE_ACTION_FUNCTION(_ImageScrollerPageQavDrawer, LoadQav)
{
PARAM_PROLOGUE;
PARAM_STRING(str);
auto qav = new CGameMenuItemQAV(160, 100, str, false, true);
ACTION_RETURN_POINTER(qav);
}
DEFINE_ACTION_FUNCTION(_ImageScrollerPageQavDrawer, DestroyQav)
{
PARAM_PROLOGUE;
PARAM_POINTER(qav, CGameMenuItemQAV);
if (qav) delete qav;
return 0;
}
DEFINE_ACTION_FUNCTION(_ImageScrollerPageQavDrawer, DrawQav)
{
PARAM_PROLOGUE;
PARAM_POINTER(qav, CGameMenuItemQAV);
qav->Draw();
return 0;
}

View file

@ -87,6 +87,18 @@ static void drawTextScreenBackground(void)
} }
} }
// One these screens get scriptified this should use the version in BloodMenuDelegate.
static void DrawCaption(const char* text)
{
double scalex = 1.; // Expand the box if the text is longer
int width = BigFont->StringWidth(text);
int boxwidth = tileWidth(2038);
if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10);
DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE);
DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
}
class DBloodSummaryScreen : public DScreenJob class DBloodSummaryScreen : public DScreenJob
{ {
@ -141,7 +153,7 @@ class DBloodSummaryScreen : public DScreenJob
drawTextScreenBackground(); drawTextScreenBackground();
if (gGameOptions.nGameType == 0) if (gGameOptions.nGameType == 0)
{ {
DrawMenuCaption(GStrings("TXTB_LEVELSTATS")); DrawCaption(GStrings("TXTB_LEVELSTATS"));
if (bPlayerCheated) if (bPlayerCheated)
{ {
auto text = GStrings("TXTB_CHEATED"); auto text = GStrings("TXTB_CHEATED");
@ -154,7 +166,7 @@ class DBloodSummaryScreen : public DScreenJob
} }
else else
{ {
DrawMenuCaption(GStrings("TXTB_FRAGSTATS")); DrawCaption(GStrings("TXTB_FRAGSTATS"));
DrawKills(); DrawKills();
} }
int myclock = int(clock * 120 / 1'000'000'000); int myclock = int(clock * 120 / 1'000'000'000);
@ -301,7 +313,7 @@ public:
{ {
twod->ClearScreen(); twod->ClearScreen();
drawTextScreenBackground(); drawTextScreenBackground();
DrawMenuCaption(pzLoadingScreenText1); DrawCaption(pzLoadingScreenText1);
viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1); viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1);
auto text = GStrings("TXTB_PLSWAIT"); auto text = GStrings("TXTB_PLSWAIT");

View file

@ -58,12 +58,9 @@ void _SetErrorLoc(const char *pzFile, int nLine)
// by NoOne: show warning msgs in game instead of throwing errors (in some cases) // by NoOne: show warning msgs in game instead of throwing errors (in some cases)
void _consoleSysMsg(const char* pzFormat, ...) { void _consoleSysMsg(const char* pzFormat, ...) {
char buffer[1024];
va_list args; va_list args;
va_start(args, pzFormat); va_start(args, pzFormat);
vsprintf(buffer, pzFormat, args); VPrintf(PRINT_LOW, TEXTCOLOR_RED "%s\n", args);
Printf(TEXTCOLOR_RED "%s(%i): %s\n", _module, _line, buffer);
} }
END_BLD_NS END_BLD_NS

View file

@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "nnexts.h" #include "nnexts.h"
#include "zstring.h" #include "zstring.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_video.h" #include "v_video.h"

View file

@ -38,7 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sound.h" #include "sound.h"
#include "view.h" #include "view.h"
#include "eventq.h" #include "eventq.h"
#include "menu.h" #include "razemenu.h"
BEGIN_BLD_NS BEGIN_BLD_NS

View file

@ -474,7 +474,7 @@ void LoadSave::Write(void *pData, int nSize)
ThrowError("File error #%d writing save file.", errno); ThrowError("File error #%d writing save file.", errno);
} }
bool GameInterface::LoadGame(FSaveGameNode* node) bool GameInterface::LoadGame()
{ {
sndKillAllSounds(); sndKillAllSounds();
sfxKillAllSounds(); sfxKillAllSounds();
@ -534,7 +534,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
return true; return true;
} }
bool GameInterface::SaveGame(FSaveGameNode* node) bool GameInterface::SaveGame()
{ {
LoadSave::hSFile = WriteSavegameChunk("snapshot.bld"); LoadSave::hSFile = WriteSavegameChunk("snapshot.bld");

View file

@ -41,8 +41,6 @@ public:
virtual void Load(void); virtual void Load(void);
void Read(void *, int); void Read(void *, int);
void Write(void *, int); void Write(void *, int);
static void LoadGame(FSavegameNode *);
static void SaveGame(FSavegameNode*);
}; };
void LoadSaveSetup(void); void LoadSaveSetup(void);

View file

@ -143,6 +143,5 @@ void tilePrecacheTile(int nTile, int nType, HitList& hits);
char tileGetSurfType(int hit); char tileGetSurfType(int hit);
void scrLoadPalette(void); void scrLoadPalette(void);
void DrawMenuCaption(const char* text);
END_BLD_NS END_BLD_NS

View file

@ -0,0 +1,2 @@
// names for everything that gets accessed by scripts.
x(MENUBAR, 2038)

View file

@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "nnexts.h" #include "nnexts.h"
#include "zstring.h" #include "zstring.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_video.h" #include "v_video.h"

View file

@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "nnexts.h" #include "nnexts.h"
#include "zstring.h" #include "zstring.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_video.h" #include "v_video.h"
@ -47,6 +47,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "glbackend/glbackend.h" #include "glbackend/glbackend.h"
#include "statusbar.h" #include "statusbar.h"
#include "automap.h" #include "automap.h"
#include "v_draw.h"
CVARD(Bool, hud_powerupduration, true, CVAR_ARCHIVE/*|CVAR_FRONTEND_BLOOD*/, "enable/disable displaying the remaining seconds for power-ups") CVARD(Bool, hud_powerupduration, true, CVAR_ARCHIVE/*|CVAR_FRONTEND_BLOOD*/, "enable/disable displaying the remaining seconds for power-ups")

View file

@ -39,13 +39,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h" #include "view.h"
#include "nnexts.h" #include "nnexts.h"
#include "zstring.h" #include "zstring.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_video.h" #include "v_video.h"
#include "v_font.h" #include "v_font.h"
#include "statusbar.h" #include "statusbar.h"
#include "automap.h" #include "automap.h"
#include "v_draw.h"
#include "glbackend/glbackend.h" #include "glbackend/glbackend.h"
BEGIN_BLD_NS BEGIN_BLD_NS

View file

@ -23,7 +23,7 @@
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "v_draw.h" #include "v_draw.h"
#include "stats.h" #include "stats.h"
#include "menu.h" #include "razemenu.h"
#include "version.h" #include "version.h"
#include "earcut.hpp" #include "earcut.hpp"
#include "gamestate.h" #include "gamestate.h"

View file

@ -35,6 +35,7 @@ bool hw_int_useindexedcolortextures;
CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable indexed color texture rendering") CUSTOM_CVARD(Bool, hw_useindexedcolortextures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable indexed color texture rendering")
{ {
hw_int_useindexedcolortextures = self; hw_int_useindexedcolortextures = self;
if (screen) screen->SetTextureFilterMode();
} }

View file

@ -439,6 +439,9 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms)
std::swap(v1, v2); std::swap(v1, v2);
} }
auto osave = offset;
if (parms.nooffset) offset = { 0,0 };
if (parms.rotateangle == 0) if (parms.rotateangle == 0)
{ {
double x = parms.x - parms.left * xscale; double x = parms.x - parms.left * xscale;
@ -461,10 +464,10 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms)
if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.rclip || y + h >(double)parms.dclip) if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.rclip || y + h >(double)parms.dclip)
{ {
dg.mScissor[0] = parms.lclip; dg.mScissor[0] = parms.lclip + int(offset.X);
dg.mScissor[1] = parms.uclip; dg.mScissor[1] = parms.uclip + int(offset.Y);
dg.mScissor[2] = parms.rclip; dg.mScissor[2] = parms.rclip + int(offset.X);
dg.mScissor[3] = parms.dclip; dg.mScissor[3] = parms.dclip + int(offset.Y);
dg.mFlags |= DTF_Scissor; dg.mFlags |= DTF_Scissor;
} }
else else
@ -475,10 +478,10 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms)
dg.mVertCount = 4; dg.mVertCount = 4;
dg.mVertIndex = (int)mVertices.Reserve(4); dg.mVertIndex = (int)mVertices.Reserve(4);
TwoDVertex* ptr = &mVertices[dg.mVertIndex]; TwoDVertex* ptr = &mVertices[dg.mVertIndex];
ptr->Set(x, y, 0, u1, v1, vertexcolor); ptr++; Set(ptr, x, y, 0, u1, v1, vertexcolor); ptr++;
ptr->Set(x, y + h, 0, u1, v2, vertexcolor); ptr++; Set(ptr, x, y + h, 0, u1, v2, vertexcolor); ptr++;
ptr->Set(x + w, y, 0, u2, v1, vertexcolor); ptr++; Set(ptr, x + w, y, 0, u2, v1, vertexcolor); ptr++;
ptr->Set(x + w, y + h, 0, u2, v2, vertexcolor); ptr++; Set(ptr, x + w, y + h, 0, u2, v2, vertexcolor); ptr++;
} }
else else
{ {
@ -502,25 +505,26 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms)
double x4 = parms.x + xscale * (xd2 * cosang + yd2 * sinang); double x4 = parms.x + xscale * (xd2 * cosang + yd2 * sinang);
double y4 = parms.y - yscale * (xd2 * sinang - yd2 * cosang); double y4 = parms.y - yscale * (xd2 * sinang - yd2 * cosang);
dg.mScissor[0] = parms.lclip; dg.mScissor[0] = parms.lclip + int(offset.X);
dg.mScissor[1] = parms.uclip; dg.mScissor[1] = parms.uclip + int(offset.Y);
dg.mScissor[2] = parms.rclip; dg.mScissor[2] = parms.rclip + int(offset.X);
dg.mScissor[3] = parms.dclip; dg.mScissor[3] = parms.dclip + int(offset.Y);
dg.mFlags |= DTF_Scissor; dg.mFlags |= DTF_Scissor;
dg.mVertCount = 4; dg.mVertCount = 4;
dg.mVertIndex = (int)mVertices.Reserve(4); dg.mVertIndex = (int)mVertices.Reserve(4);
TwoDVertex* ptr = &mVertices[dg.mVertIndex]; TwoDVertex* ptr = &mVertices[dg.mVertIndex];
ptr->Set(x1, y1, 0, u1, v1, vertexcolor); ptr++; Set(ptr, x1, y1, 0, u1, v1, vertexcolor); ptr++;
ptr->Set(x2, y2, 0, u1, v2, vertexcolor); ptr++; Set(ptr, x2, y2, 0, u1, v2, vertexcolor); ptr++;
ptr->Set(x3, y3, 0, u2, v1, vertexcolor); ptr++; Set(ptr, x3, y3, 0, u2, v1, vertexcolor); ptr++;
ptr->Set(x4, y4, 0, u2, v2, vertexcolor); ptr++; Set(ptr, x4, y4, 0, u2, v2, vertexcolor); ptr++;
} }
dg.mIndexIndex = mIndices.Size(); dg.mIndexIndex = mIndices.Size();
dg.mIndexCount += 6; dg.mIndexCount += 6;
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
AddCommand(&dg); AddCommand(&dg);
offset = osave;
} }
//========================================================================== //==========================================================================
@ -561,6 +565,9 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
shape->dirty = false; shape->dirty = false;
} }
auto osave = offset;
if (parms.nooffset) offset = { 0,0 };
double minx = 16383, miny = 16383, maxx = -16384, maxy = -16384; double minx = 16383, miny = 16383, maxx = -16384, maxy = -16384;
for ( int i=0; i<dg.mVertCount; i++ ) for ( int i=0; i<dg.mVertCount; i++ )
{ {
@ -571,10 +578,10 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
} }
if (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip) if (minx < (double)parms.lclip || miny < (double)parms.uclip || maxx >(double)parms.rclip || maxy >(double)parms.dclip)
{ {
dg.mScissor[0] = parms.lclip; dg.mScissor[0] = parms.lclip + int(offset.X);
dg.mScissor[1] = parms.uclip; dg.mScissor[1] = parms.uclip + int(offset.Y);
dg.mScissor[2] = parms.rclip; dg.mScissor[2] = parms.rclip + int(offset.X);
dg.mScissor[3] = parms.dclip; dg.mScissor[3] = parms.dclip + int(offset.Y);
dg.mFlags |= DTF_Scissor; dg.mFlags |= DTF_Scissor;
} }
else else
@ -583,7 +590,7 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
dg.mVertIndex = (int)mVertices.Reserve(dg.mVertCount); dg.mVertIndex = (int)mVertices.Reserve(dg.mVertCount);
TwoDVertex *ptr = &mVertices[dg.mVertIndex]; TwoDVertex *ptr = &mVertices[dg.mVertIndex];
for ( int i=0; i<dg.mVertCount; i++ ) for ( int i=0; i<dg.mVertCount; i++ )
ptr[i].Set(shape->mTransformedVertices[i].X, shape->mTransformedVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor); Set(&ptr[i], shape->mTransformedVertices[i].X, shape->mTransformedVertices[i].Y, 0, shape->mCoords[i].X, shape->mCoords[i].Y, vertexcolor);
dg.mIndexIndex = mIndices.Size(); dg.mIndexIndex = mIndices.Size();
dg.mIndexCount += shape->mIndices.Size(); dg.mIndexCount += shape->mIndices.Size();
for ( int i=0; i<int(shape->mIndices.Size()); i+=3 ) for ( int i=0; i<int(shape->mIndices.Size()); i+=3 )
@ -599,6 +606,7 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
AddIndices(dg.mVertIndex, 3, shape->mIndices[i], shape->mIndices[i+1], shape->mIndices[i+2]); AddIndices(dg.mVertIndex, 3, shape->mIndices[i], shape->mIndices[i+1], shape->mIndices[i+2]);
} }
AddCommand(&dg); AddCommand(&dg);
offset = osave;
} }
//========================================================================== //==========================================================================
@ -655,7 +663,7 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints,
u = t * cosrot - v * sinrot; u = t * cosrot - v * sinrot;
v = v * cosrot + t * sinrot; v = v * cosrot + t * sinrot;
} }
mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0); Set(&mVertices[poly.mVertIndex+i], points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0);
} }
poly.mIndexIndex = mIndices.Size(); poly.mIndexIndex = mIndices.Size();
@ -694,10 +702,10 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigne
dg.mType = DrawTypeTriangles; dg.mType = DrawTypeTriangles;
if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth() - 1 || clipy2 < GetHeight() - 1) if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth() - 1 || clipy2 < GetHeight() - 1)
{ {
dg.mScissor[0] = clipx1; dg.mScissor[0] = clipx1 + int(offset.X);
dg.mScissor[1] = clipy1; dg.mScissor[1] = clipy1 + int(offset.Y);
dg.mScissor[2] = clipx2 + 1; dg.mScissor[2] = clipx2 + 1 + int(offset.X);
dg.mScissor[3] = clipy2 + 1; dg.mScissor[3] = clipy2 + 1 + int(offset.Y);
dg.mFlags |= DTF_Scissor; dg.mFlags |= DTF_Scissor;
} }
@ -712,7 +720,7 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigne
for (size_t i=0;i<vtcount;i++) for (size_t i=0;i<vtcount;i++)
{ {
ptr->Set(vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color); Set(ptr, vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color);
ptr++; ptr++;
} }
@ -841,18 +849,18 @@ void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FGameTextu
dg.mVertIndex = (int)mVertices.Reserve(4); dg.mVertIndex = (int)mVertices.Reserve(4);
auto ptr = &mVertices[dg.mVertIndex]; auto ptr = &mVertices[dg.mVertIndex];
ptr->Set(left, top, 0, fU1, fV1, color); ptr++; Set(ptr, left, top, 0, fU1, fV1, color); ptr++;
if (local_origin < 4) if (local_origin < 4)
{ {
ptr->Set(left, bottom, 0, fU1, fV2, color); ptr++; Set(ptr, left, bottom, 0, fU1, fV2, color); ptr++;
ptr->Set(right, top, 0, fU2, fV1, color); ptr++; Set(ptr, right, top, 0, fU2, fV1, color); ptr++;
} }
else else
{ {
ptr->Set(left, bottom, 0, fU2, fV1, color); ptr++; Set(ptr, left, bottom, 0, fU2, fV1, color); ptr++;
ptr->Set(right, top, 0, fU1, fV2, color); ptr++; Set(ptr, right, top, 0, fU1, fV2, color); ptr++;
} }
ptr->Set(right, bottom, 0, fU2, fV2, color); ptr++; Set(ptr, right, bottom, 0, fU2, fV2, color); ptr++;
dg.mIndexIndex = mIndices.Size(); dg.mIndexIndex = mIndices.Size();
dg.mIndexCount += 6; dg.mIndexCount += 6;
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
@ -875,10 +883,10 @@ void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color, F
dg.mVertIndex = (int)mVertices.Reserve(4); dg.mVertIndex = (int)mVertices.Reserve(4);
dg.mRenderStyle = style? *style : LegacyRenderStyles[STYLE_Translucent]; dg.mRenderStyle = style? *style : LegacyRenderStyles[STYLE_Translucent];
auto ptr = &mVertices[dg.mVertIndex]; auto ptr = &mVertices[dg.mVertIndex];
ptr->Set(x1, y1, 0, 0, 0, color); ptr++; Set(ptr, x1, y1, 0, 0, 0, color); ptr++;
ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++; Set(ptr, x1, y1 + h, 0, 0, 0, color); ptr++;
ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++; Set(ptr, x1 + w, y1, 0, 0, 0, color); ptr++;
ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++; Set(ptr, x1 + w, y1 + h, 0, 0, 0, color); ptr++;
dg.mIndexIndex = mIndices.Size(); dg.mIndexIndex = mIndices.Size();
dg.mIndexCount += 6; dg.mIndexCount += 6;
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
@ -911,10 +919,10 @@ void F2DDrawer::AddLine(double x1, double y1, double x2, double y2, int clipx1,
if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth()- 1 || clipy2 < GetHeight() - 1) if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth()- 1 || clipy2 < GetHeight() - 1)
{ {
dg.mScissor[0] = clipx1; dg.mScissor[0] = clipx1 + int(offset.X);
dg.mScissor[1] = clipy1; dg.mScissor[1] = clipy1 + int(offset.Y);
dg.mScissor[2] = clipx2 + 1; dg.mScissor[2] = clipx2 + 1 + int(offset.X);
dg.mScissor[3] = clipy2 + 1; dg.mScissor[3] = clipy2 + 1 + int(offset.Y);
dg.mFlags |= DTF_Scissor; dg.mFlags |= DTF_Scissor;
} }
@ -922,8 +930,8 @@ void F2DDrawer::AddLine(double x1, double y1, double x2, double y2, int clipx1,
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
dg.mVertCount = 2; dg.mVertCount = 2;
dg.mVertIndex = (int)mVertices.Reserve(2); dg.mVertIndex = (int)mVertices.Reserve(2);
mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); Set(&mVertices[dg.mVertIndex], x1, y1, 0, 0, 0, p);
mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p); Set(&mVertices[dg.mVertIndex+1], x2, y2, 0, 0, 0, p);
AddCommand(&dg); AddCommand(&dg);
} }
@ -958,10 +966,10 @@ void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, u
dg.mVertIndex = (int)mVertices.Reserve(4); dg.mVertIndex = (int)mVertices.Reserve(4);
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
auto ptr = &mVertices[dg.mVertIndex]; auto ptr = &mVertices[dg.mVertIndex];
ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++; Set(ptr, corner0.X, corner0.Y, 0, 0, 0, p); ptr++;
ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++; Set(ptr, corner1.X, corner1.Y, 0, 0, 0, p); ptr++;
ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++; Set(ptr, corner2.X, corner2.Y, 0, 0, 0, p); ptr++;
ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++; Set(ptr, corner3.X, corner3.Y, 0, 0, 0, p); ptr++;
dg.mIndexIndex = mIndices.Size(); dg.mIndexIndex = mIndices.Size();
dg.mIndexCount += 6; dg.mIndexCount += 6;
AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2);
@ -985,7 +993,7 @@ void F2DDrawer::AddPixel(int x1, int y1, uint32_t color)
dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent];
dg.mVertCount = 1; dg.mVertCount = 1;
dg.mVertIndex = (int)mVertices.Reserve(1); dg.mVertIndex = (int)mVertices.Reserve(1);
mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); Set(&mVertices[dg.mVertIndex], x1, y1, 0, 0, 0, p);
AddCommand(&dg); AddCommand(&dg);
} }

View file

@ -167,6 +167,7 @@ public:
bool isIn2D; bool isIn2D;
bool locked; // prevents clearing of the data so it can be reused multiple times (useful for screen fades) bool locked; // prevents clearing of the data so it can be reused multiple times (useful for screen fades)
float screenFade = 1.f; float screenFade = 1.f;
DVector2 offset;
public: public:
int fullscreenautoaspect = 0; int fullscreenautoaspect = 0;
int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1; int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1;
@ -216,6 +217,19 @@ public:
void SetClipRect(int x, int y, int w, int h); void SetClipRect(int x, int y, int w, int h);
void GetClipRect(int* x, int* y, int* w, int* h); void GetClipRect(int* x, int* y, int* w, int* h);
DVector2 SetOffset(const DVector2& vec)
{
auto v = offset;
offset = vec;
return v;
}
void Set(TwoDVertex* v, double xx, double yy, double zz, double uu, double vv, PalEntry col)
{
v->Set(xx + offset.X, yy + offset.Y, zz, uu, vv, col);
}
int DrawCount() const int DrawCount() const
{ {
return mData.Size(); return mData.Size();

View file

@ -417,8 +417,6 @@ DEFINE_ACTION_FUNCTION(_Screen, GetFullscreenRect)
PARAM_FLOAT(virth); PARAM_FLOAT(virth);
PARAM_INT(fsmode); PARAM_INT(fsmode);
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
DrawParms parms; DrawParms parms;
DoubleRect rect; DoubleRect rect;
parms.viewport.width = twod->GetWidth(); parms.viewport.width = twod->GetWidth();
@ -705,6 +703,7 @@ bool ParseDrawTextureTags(F2DDrawer *drawer, FGameTexture *img, double x, double
parms->rotateangle = 0; parms->rotateangle = 0;
parms->flipoffsets = false; parms->flipoffsets = false;
parms->indexed = false; parms->indexed = false;
parms->nooffset = false;
// Parse the tag list for attributes. (For floating point attributes, // Parse the tag list for attributes. (For floating point attributes,
// consider that the C ABI dictates that all floats be promoted to // consider that the C ABI dictates that all floats be promoted to
@ -916,6 +915,10 @@ bool ParseDrawTextureTags(F2DDrawer *drawer, FGameTexture *img, double x, double
parms->flipoffsets = ListGetInt(tags); parms->flipoffsets = ListGetInt(tags);
break; break;
case DTA_NoOffset:
parms->nooffset = ListGetInt(tags);
break;
case DTA_SrcX: case DTA_SrcX:
parms->srcx = ListGetDouble(tags) / img->GetDisplayWidth(); parms->srcx = ListGetDouble(tags) / img->GetDisplayWidth();
break; break;
@ -1531,9 +1534,30 @@ void DrawFrame(F2DDrawer* twod, PalEntry color, int left, int top, int width, in
twod->AddColorOnlyQuad(right, top - offset, offset, height + 2 * offset, color); twod->AddColorOnlyQuad(right, top - offset, offset, height + 2 * offset, color);
} }
DEFINE_ACTION_FUNCTION(_Screen, DrawLineFrame)
{
PARAM_PROLOGUE;
PARAM_COLOR(color);
PARAM_INT(left);
PARAM_INT(top);
PARAM_INT(width);
PARAM_INT(height);
PARAM_INT(thickness);
DrawFrame(twod, color, left, top, width, height, thickness);
return 0;
}
void V_CalcCleanFacs(int designwidth, int designheight, int realwidth, int realheight, int* cleanx, int* cleany, int* _cx1, int* _cx2) void V_CalcCleanFacs(int designwidth, int designheight, int realwidth, int realheight, int* cleanx, int* cleany, int* _cx1, int* _cx2)
{ {
if (designheight < 240 && realheight >= 480) designheight = 240; if (designheight < 240 && realheight >= 480) designheight = 240;
*cleanx = *cleany = std::min(realwidth / designwidth, realheight / designheight); *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)));
}

View file

@ -131,6 +131,7 @@ enum
DTA_FlipOffsets, // Flips offsets when using DTA_FlipX and DTA_FlipY, this cannot be automatic due to unexpected behavior with unoffsetted graphics. 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_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. DTA_CleanTop, // Like DTA_Clean but aligns to the top of the screen instead of the center.
DTA_NoOffset, // Ignore 2D drawer's offset.
}; };
@ -198,6 +199,7 @@ struct DrawParms
bool burn; bool burn;
bool flipoffsets; bool flipoffsets;
bool indexed; bool indexed;
bool nooffset;
int8_t fsscalemode; int8_t fsscalemode;
double srcx, srcy; double srcx, srcy;
double srcwidth, srcheight; double srcwidth, srcheight;

View file

@ -99,7 +99,7 @@ SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels,
{ {
int flags = 0; int flags = 0;
if (numchannels < 2) flags |= SoundStream::Mono; if (numchannels < 2) flags |= SoundStream::Mono;
auto stream = GSnd->CreateStream(cb, size, flags, samplerate, userdata); auto stream = GSnd->CreateStream(cb, int(size), flags, samplerate, userdata);
if (stream) stream->Play(true, 1); if (stream) stream->Play(true, 1);
return stream; return stream;
} }

View file

@ -50,7 +50,6 @@ enum
}; };
static FRandom pr_soundpitch ("SoundPitch"); static FRandom pr_soundpitch ("SoundPitch");
SoundEngine* soundEngine; SoundEngine* soundEngine;
int sfx_empty = -1;
//========================================================================== //==========================================================================
// //
@ -713,10 +712,9 @@ sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx)
{ {
unsigned int i; unsigned int i;
// If the sound doesn't exist, replace it with the empty sound. if (sfx->lumpnum == sfx_empty)
if (sfx->lumpnum == -1)
{ {
sfx->lumpnum = sfx_empty; return sfx;
} }
// See if there is another sound already initialized with this lump. If so, // See if there is another sound already initialized with this lump. If so,
@ -1532,31 +1530,12 @@ int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitc
S_sfx.Reserve(1); S_sfx.Reserve(1);
sfxinfo_t &newsfx = S_sfx.Last(); sfxinfo_t &newsfx = S_sfx.Last();
newsfx.data.Clear();
newsfx.name = logicalname; newsfx.name = logicalname;
newsfx.lumpnum = lump; newsfx.lumpnum = lump;
newsfx.next = 0; newsfx.next = 0;
newsfx.index = 0;
newsfx.Volume = 1;
newsfx.Attenuation = 1;
newsfx.PitchMask = CurrentPitchMask; newsfx.PitchMask = CurrentPitchMask;
newsfx.DefPitch = 0.0;
newsfx.DefPitchMax = 0.0;
newsfx.NearLimit = nearlimit; newsfx.NearLimit = nearlimit;
newsfx.LimitRange = 256 * 256;
newsfx.bRandomHeader = false;
newsfx.bLoadRAW = false;
newsfx.b16bit = false;
newsfx.bUsed = false;
newsfx.bSingular = false;
newsfx.bTentative = false;
newsfx.ResourceId = resid; newsfx.ResourceId = resid;
newsfx.RawRate = 0;
newsfx.link = sfxinfo_t::NO_LINK;
newsfx.Rolloff.RolloffType = ROLLOFF_Doom;
newsfx.Rolloff.MinDistance = 0;
newsfx.Rolloff.MaxDistance = 0;
newsfx.LoopStart = -1;
if (resid >= 0) ResIdMap[resid] = S_sfx.Size() - 1; if (resid >= 0) ResIdMap[resid] = S_sfx.Size() - 1;
return (int)S_sfx.Size()-1; return (int)S_sfx.Size()-1;

View file

@ -8,7 +8,12 @@ struct FRandomSoundList
uint32_t Owner = 0; uint32_t Owner = 0;
}; };
extern int sfx_empty; enum
{
sfx_empty = -1
};
// //
// SoundFX struct. // SoundFX struct.
@ -17,71 +22,38 @@ struct sfxinfo_t
{ {
// Next field is for use by the system sound interface. // Next field is for use by the system sound interface.
// A non-null data means the sound has been loaded. // A non-null data means the sound has been loaded.
SoundHandle data; SoundHandle data{};
FString name; // [RH] Sound name defined in SNDINFO FString name; // [RH] Sound name defined in SNDINFO
int lumpnum; // lump number of sfx int lumpnum = sfx_empty; // lump number of sfx
unsigned int next, index; // [RH] For hashing unsigned int next = -1, index = 0; // [RH] For hashing
float Volume; float Volume = 1.f;
int ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. int ResourceId = -1; // Resource ID as implemented by Blood. Not used by Doom but added for completeness.
uint8_t PitchMask; float LimitRange = 256*256; // Range for sound limiting (squared for faster computations)
int16_t NearLimit; // 0 means unlimited float DefPitch = 0.f; // A defined pitch instead of a random one the sound plays at, similar to A_StartSound.
float LimitRange; // Range for sound limiting (squared for faster computations) float DefPitchMax = 0.f; // Randomized range with stronger control over pitch itself.
float DefPitch; // A defined pitch instead of a random one the sound plays at, similar to A_StartSound.
float DefPitchMax; // Randomized range with stronger control over pitch itself.
unsigned bRandomHeader:1; int16_t NearLimit = 4; // 0 means unlimited.
unsigned bLoadRAW:1; uint8_t PitchMask = 0;
unsigned b16bit:1; bool bRandomHeader = false;
unsigned bUsed:1; bool bLoadRAW = false;
unsigned bSingular:1; bool b16bit = false;
bool bUsed = false;
bool bSingular = false;
bool bTentative = true;
unsigned bTentative:1;
TArray<int> UserData; TArray<int> UserData;
int RawRate; // Sample rate to use when bLoadRAW is true int RawRate = 0; // Sample rate to use when bLoadRAW is true
int LoopStart = -1; // -1 means no specific loop defined
int LoopStart; // -1 means no specific loop defined unsigned int link = NO_LINK;;
unsigned int link;
enum { NO_LINK = 0xffffffff }; enum { NO_LINK = 0xffffffff };
FRolloffInfo Rolloff; FRolloffInfo Rolloff{};
float Attenuation; // Multiplies the attenuation passed to S_Sound. float Attenuation = 1.f; // Multiplies the attenuation passed to S_Sound.
void MarkUsed(); // Marks this sound as used.
void Clear()
{
data.Clear();
lumpnum = -1; // lump number of sfx
next = -1;
index = 0; // [RH] For hashing
Volume = 1.f;
ResourceId = -1;
PitchMask = 0;
NearLimit = 4; // 0 means unlimited
LimitRange = 256*256;
bRandomHeader = false;
bLoadRAW = false;
b16bit= false;
bUsed = false;
bSingular = false;
bTentative = true;
RawRate = 0; // Sample rate to use when bLoadRAW is true
LoopStart = 0; // -1 means no specific loop defined
link = NO_LINK;
Rolloff = {};
Attenuation = 1.f;
}
}; };
// Rolloff types // Rolloff types

View file

@ -0,0 +1,248 @@
/*
** date.cpp
**
** VM exports for engine backend classes
**
**---------------------------------------------------------------------------
**
** 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 <time.h>
#include "c_dispatch.h"
#include "vm.h"
#include "zstring.h"
#include "printf.h"
time_t epochoffset = 0; // epoch start in seconds (0 = January 1st, 1970)
//==========================================================================
//
// CCMD setdate
//
// Set the time to a specific value
//
//==========================================================================
UNSAFE_CCMD(setdate)
{
if (argv.argc() != 3)
{
Printf("setdate HH:MM:SS DD-MM-YYYY: Set the current date\n");
return;
}
time_t today;
time(&today);
struct tm* timeinfo = localtime(&today);
if (timeinfo != nullptr)
{
auto clock = FString(argv[1]).Split(":");
auto date = FString(argv[2]).Split("-");
if(clock.Size() != 3 || date.Size() != 3)
{
Printf("setdate HH:MM:SS DD-MM-YYYY: Set the current date\n");
return;
}
if(!clock[0].IsInt())
{
Printf("Invalid hour\n");
return;
}
if (!clock[1].IsInt())
{
Printf("Invalid minutes\n");
return;
}
if (!clock[2].IsInt())
{
Printf("Invalid seconds\n");
return;
}
if (!date[0].IsInt())
{
Printf("Invalid day\n");
return;
}
if (!date[1].IsInt())
{
Printf("Invalid month\n");
return;
}
if (!date[2].IsInt())
{
Printf("Invalid year\n");
return;
}
//Set Date
timeinfo->tm_hour = int( clock[0].ToLong() );
timeinfo->tm_min = int( clock[1].ToLong() );
timeinfo->tm_sec = int( clock[2].ToLong() );
timeinfo->tm_mday = int( date[0].ToLong() );
timeinfo->tm_mon = int( date[1].ToLong() - 1); // Month interally is 0 - 11
timeinfo->tm_year = int( date[2].ToLong() - 1900 ); // Year interally is 00 - 138
time_t newTime = mktime(timeinfo);
tm* t_old = localtime(&today);
time_t oldTime = mktime(t_old);
if (newTime == -1 || oldTime == -1)
{
Printf("Unable to set the date\n");
return;
}
epochoffset = newTime - oldTime;
// This deals with some inconsistent display behaviour for DST
// In this case, we want to emulate GCC's behaviour
today += epochoffset;
struct tm* t_new = localtime(&today);
if (t_new != nullptr)
{
char timeString[1024];
if (strftime(timeString, sizeof(timeString), "%H", t_new))
{
auto hour = FString(timeString).ToLong();
if (hour - clock[0].ToLong() == -1 || hour - clock[0].ToLong() == 23)
epochoffset += 3600;
else if (hour - clock[0].ToLong() == 1 || hour - clock[0].ToLong() == -23)
epochoffset -= 3600;
}
}
return;
}
else
{
Printf("Unable to set the date\n");
return;
}
}
CCMD(getdate)
{
time_t now;
time(&now);
now += epochoffset;
struct tm* timeinfo = localtime(&now);
if (timeinfo != nullptr)
{
char timeString[1024];
if (strftime(timeString, sizeof(timeString), "%H:%M:%S %d-%m-%Y%n", timeinfo))
Printf("%s\n", timeString);
else
Printf("Error Retrieving Current Date\n");
}
else
{
Printf("Error Retrieving Current Date\n");
}
}
//=====================================================================================
//
//
//
//=====================================================================================
extern time_t epochoffset;
static int GetEpochTime()
{
time_t now;
time(&now);
return now != (time_t)(-1) ? int(now + epochoffset) : -1;
}
//Returns an empty string if the Strf tokens are valid, otherwise returns the problematic token
static FString CheckStrfString(FString timeForm)
{
// Valid Characters after %
const char validSingles[] = { 'a','A','b','B','c','C','d','D','e','F','g','G','h','H','I','j','m','M','n','p','r','R','S','t','T','u','U','V','w','W','x','X','y','Y','z','Z' };
timeForm.Substitute("%%", "%a"); //Prevent %% from causing tokenizing problems
timeForm = "a" + timeForm; //Prevent %* at the beginning from causing a false error from tokenizing
auto tokens = timeForm.Split("%");
for (auto t : tokens)
{
bool found = false;
// % at end
if (t.Len() == 0) return FString("%");
// Single Character
for (size_t i = 0; i < sizeof(validSingles) / sizeof(validSingles[0]); i++)
{
if (t[0] == validSingles[i])
{
found = true;
break;
}
}
if (found) continue;
return FString("%") + t[0];
}
return "";
}
static void FormatTime(const FString& timeForm, int timeVal, FString* result)
{
FString error = CheckStrfString(timeForm);
if (!error.IsEmpty())
ThrowAbortException(X_FORMAT_ERROR, "'%s' is not a valid format specifier of SystemTime.Format()", error.GetChars());
time_t val = timeVal;
struct tm* timeinfo = localtime(&val);
if (timeinfo != nullptr)
{
char timeString[1024];
if (strftime(timeString, sizeof(timeString), timeForm, timeinfo))
*result = timeString;
}
}
DEFINE_ACTION_FUNCTION_NATIVE(_SystemTime, Now, GetEpochTime)
{
PARAM_PROLOGUE;
ACTION_RETURN_INT(GetEpochTime());
}
DEFINE_ACTION_FUNCTION_NATIVE(_SystemTime, Format, FormatTime)
{
PARAM_PROLOGUE;
PARAM_STRING(timeForm);
PARAM_INT(timeVal);
FString result;
FormatTime(timeForm, timeVal, &result);
ACTION_RETURN_STRING(result);
}

View file

@ -22,6 +22,10 @@ struct SystemCallbacks
void (*MenuDim)(); void (*MenuDim)();
FString(*GetPlayerName)(int i); FString(*GetPlayerName)(int i);
bool (*DispatchEvent)(event_t* ev); bool (*DispatchEvent)(event_t* ev);
bool (*CheckGame)(const char* nm);
int (*GetGender)();
void (*MenuClosed)();
bool (*CheckMenudefOption)(const char* opt);
}; };
extern SystemCallbacks sysCallbacks; extern SystemCallbacks sysCallbacks;
@ -36,3 +40,6 @@ struct WadStuff
extern FString endoomName; extern FString endoomName;
extern bool batchrun; extern bool batchrun;
extern float menuBlurAmount; extern float menuBlurAmount;
extern bool generic_ui;
void UpdateGenericUI(bool cvar);

View file

@ -81,5 +81,6 @@ struct doomcom_t
extern doomcom_t doomcom; extern doomcom_t doomcom;
extern bool netgame, multiplayer; extern bool netgame, multiplayer;
extern int consoleplayer;
#endif #endif

View file

@ -715,8 +715,10 @@ xx(MainmenuTextOnly)
xx(Episodemenu) xx(Episodemenu)
xx(Playerclassmenu) xx(Playerclassmenu)
xx(HexenDefaultPlayerclassmenu) xx(HexenDefaultPlayerclassmenu)
xx(ListMenuItemBloodDripDrawer)
xx(Skillmenu) xx(Skillmenu)
xx(Startgame) xx(Startgame)
xx(StartgameNoSkill)
xx(StartgameConfirm) xx(StartgameConfirm)
xx(StartgameConfirmed) xx(StartgameConfirmed)
xx(Loadgamemenu) xx(Loadgamemenu)
@ -1078,6 +1080,7 @@ xx(PlayerSkin)
xx(NewPlayerMenu) xx(NewPlayerMenu)
xx(AltHud) xx(AltHud)
xx(GameScreen) xx(GameScreen)
xx(ListM)
// summary // summary
xx(cwidth) xx(cwidth)
@ -1086,3 +1089,14 @@ xx(wrapwidth)
xx(scalefactorx) xx(scalefactorx)
xx(scalefactory) xx(scalefactory)
xx(scalemode) xx(scalemode)
xy(menu_cursor, "menu/cursor")
xy(menu_choose, "menu/choose")
xy(menu_backup, "menu/backup")
xy(menu_clear, "menu/clear")
xy(menu_dismiss, "menu/dismiss")
xy(menu_change, "menu/change")
xy(menu_advance, "menu/advance")
xx(zoomsize)

View file

@ -1044,8 +1044,8 @@ bool FScanner::ScanValue(bool allowfloat, bool evaluate)
auto d = constants.CheckKey(String); auto d = constants.CheckKey(String);
if (!d) return false; if (!d) return false;
if (!allowfloat && int64_t(*d) != *d) return false; if (!allowfloat && int64_t(*d) != *d) return false;
BigNumber = *d; BigNumber = int64_t(*d);
Number = *d; Number = int(*d);
Float = *d; Float = *d;
} }
if (neg) if (neg)
@ -1231,8 +1231,8 @@ void FScanner::AddSymbol(const char *name, int64_t value)
{ {
Symbol sym; Symbol sym;
sym.tokenType = TK_IntConst; sym.tokenType = TK_IntConst;
sym.Number = value; sym.Number = int(value);
sym.Float = value; sym.Float = double(value);
symbols.Insert(name, sym); symbols.Insert(name, sym);
} }

View file

@ -39,6 +39,7 @@
#include "filesystem.h" #include "filesystem.h"
#include "sc_man.h" #include "sc_man.h"
#include "printf.h" #include "printf.h"
#include "i_interface.h"
//========================================================================== //==========================================================================
// //
@ -237,12 +238,12 @@ bool FStringTable::ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer)
if (filterstr.IsNotEmpty()) if (filterstr.IsNotEmpty())
{ {
bool ok = false; bool ok = false;
if (callbacks && callbacks->ValidFilter) if (sysCallbacks.CheckGame)
{ {
auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY); auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY);
for (auto& entry : filter) for (auto& entry : filter)
{ {
if (callbacks->ValidFilter(entry)) if (sysCallbacks.CheckGame(entry))
{ {
ok = true; ok = true;
break; break;
@ -356,7 +357,7 @@ void FStringTable::LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer)
sc.MustGetStringName("ifgame"); sc.MustGetStringName("ifgame");
sc.MustGetStringName("("); sc.MustGetStringName("(");
sc.MustGetString(); sc.MustGetString();
skip |= (!callbacks || !callbacks->ValidFilter || !callbacks->ValidFilter(sc.String)); skip |= (!sysCallbacks.CheckGame || !sysCallbacks.CheckGame(sc.String));
sc.MustGetStringName(")"); sc.MustGetStringName(")");
sc.MustGetString(); sc.MustGetString();
@ -564,7 +565,7 @@ const char *FStringTable::GetString(const char *name, uint32_t *langtable, int g
{ {
return nullptr; return nullptr;
} }
if (gender == -1 && callbacks && callbacks->GetPlayerGender) gender = callbacks->GetPlayerGender(); if (gender == -1 && sysCallbacks.GetGender) gender = sysCallbacks.GetGender();
if (gender < 0 || gender > 3) gender = 0; if (gender < 0 || gender > 3) gender = 0;
FName nm(name, true); FName nm(name, true);
if (nm != NAME_None) if (nm != NAME_None)
@ -597,7 +598,7 @@ const char *FStringTable::GetLanguageString(const char *name, uint32_t langtable
{ {
return nullptr; return nullptr;
} }
if (gender == -1 && callbacks && callbacks->GetPlayerGender) gender = callbacks->GetPlayerGender(); if (gender == -1 && sysCallbacks.GetGender) gender = sysCallbacks.GetGender();
if (gender < 0 || gender > 3) gender = 0; if (gender < 0 || gender > 3) gender = 0;
FName nm(name, true); FName nm(name, true);
if (nm != NAME_None) if (nm != NAME_None)
@ -665,3 +666,5 @@ const char *StringMap::MatchString (const char *string) const
} }
FStringTable GStrings; FStringTable GStrings;

View file

@ -50,13 +50,6 @@
#include "name.h" #include "name.h"
struct StringtableCallbacks
{
// These two functions would create a dependency on the game code so they are installed as callbacks.
bool (*ValidFilter)(const char* str);
int (*GetPlayerGender)();
};
struct TableElement struct TableElement
{ {
int filenum; int filenum;
@ -108,7 +101,6 @@ public:
return GetString(name, nullptr); return GetString(name, nullptr);
} }
bool exists(const char *name); bool exists(const char *name);
void SetCallbacks(StringtableCallbacks* cb) { callbacks = cb; }
void InsertString(int lumpnum, int langid, FName label, const FString& string); void InsertString(int lumpnum, int langid, FName label, const FString& string);
@ -118,8 +110,7 @@ private:
StringMacroMap allMacros; StringMacroMap allMacros;
LangMap allStrings; LangMap allStrings;
TArray<std::pair<uint32_t, StringMap*>> currentLanguageSet; TArray<std::pair<uint32_t, StringMap*>> currentLanguageSet;
StringtableCallbacks* callbacks = nullptr;
void LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer); void LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer);
TArray<TArray<FString>> parseCSV(const TArray<uint8_t> &buffer); TArray<TArray<FString>> parseCSV(const TArray<uint8_t> &buffer);
bool ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer); bool ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer);

View file

@ -46,6 +46,7 @@
#include "gstrings.h" #include "gstrings.h"
#include "vm.h" #include "vm.h"
#include "serializer.h" #include "serializer.h"
#include "c_cvars.h"
//========================================================================== //==========================================================================
// //
@ -240,3 +241,46 @@ DEFINE_ACTION_FUNCTION(FFont, BreakLines)
auto broken = V_BreakLines(self, maxwidth, text, true); auto broken = V_BreakLines(self, maxwidth, text, true);
ACTION_RETURN_OBJECT(Create<DBrokenLines>(broken)); ACTION_RETURN_OBJECT(Create<DBrokenLines>(broken));
} }
bool generic_ui;
EXTERN_CVAR(String, language)
bool CheckFontComplete(FFont* font)
{
// Also check if the SmallFont contains all characters this language needs.
// If not, switch back to the original one.
return font->CanPrint(GStrings["REQUIRED_CHARACTERS"]);
}
void UpdateGenericUI(bool cvar)
{
auto switchstr = GStrings["USE_GENERIC_FONT"];
generic_ui = (cvar || (switchstr && strtoll(switchstr, nullptr, 0)));
if (!generic_ui)
{
// Use the mod's SmallFont if it is complete.
// Otherwise use the stock Smallfont if it is complete.
// If none is complete, fall back to the VGA font.
// The font being set here will be used in 3 places: Notifications, centered messages and menu confirmations.
if (CheckFontComplete(SmallFont))
{
AlternativeSmallFont = SmallFont;
}
else if (OriginalSmallFont && CheckFontComplete(OriginalSmallFont))
{
AlternativeSmallFont = OriginalSmallFont;
}
else
{
AlternativeSmallFont = NewSmallFont;
}
// Todo: Do the same for the BigFont
}
}
CUSTOM_CVAR(Bool, ui_generic, false, CVAR_NOINITCALL) // This is for allowing to test the generic font system with all languages
{
UpdateGenericUI(self);
}

View file

@ -0,0 +1,198 @@
/*
** joystickmenu.cpp
** The joystick configuration menus
**
**---------------------------------------------------------------------------
** Copyright 2010 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 "menu.h"
#include "m_joy.h"
#include "vm.h"
static TArray<IJoystickConfig *> Joysticks;
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetSensitivity)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
ACTION_RETURN_FLOAT(self->GetSensitivity());
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetSensitivity)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_FLOAT(sens);
self->SetSensitivity((float)sens);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisScale)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_FLOAT(self->GetAxisScale(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisScale)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_FLOAT(sens);
self->SetAxisScale(axis, (float)sens);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisDeadZone)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_FLOAT(self->GetAxisDeadZone(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDeadZone)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_FLOAT(dz);
self->SetAxisDeadZone(axis, (float)dz);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisMap)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_INT(self->GetAxisMap(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisMap)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
PARAM_INT(map);
self->SetAxisMap(axis, (EJoyAxis)map);
return 0;
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetName)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
ACTION_RETURN_STRING(self->GetName());
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisName)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
PARAM_INT(axis);
ACTION_RETURN_STRING(self->GetAxisName(axis));
}
DEFINE_ACTION_FUNCTION(IJoystickConfig, GetNumAxes)
{
PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig);
ACTION_RETURN_INT(self->GetNumAxes());
}
void UpdateJoystickMenu(IJoystickConfig *selected)
{
DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions);
DMenuDescriptor **ddesc = MenuDescriptors.CheckKey("JoystickOptionsDefaults");
if (ddesc == nullptr) return; // without any data the menu cannot be set up and must remain empty.
if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor)))
{
DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc;
DOptionMenuDescriptor *dopt = (DOptionMenuDescriptor *)*ddesc;
if (dopt == nullptr) return;
DMenuItemBase *it;
int i;
int itemnum = -1;
I_GetJoysticks(Joysticks);
if ((unsigned)itemnum >= Joysticks.Size())
{
itemnum = Joysticks.Size() - 1;
}
if (selected != NULL)
{
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == selected)
{
itemnum = i;
break;
}
}
}
opt->mItems = dopt->mItems;
it = opt->GetItem("ConfigureMessage");
if (it != nullptr) it->SetValue(0, !!Joysticks.Size());
it = opt->GetItem("ConnectMessage1");
if (it != nullptr) it->SetValue(0, !use_joystick);
it = opt->GetItem("ConnectMessage2");
if (it != nullptr) it->SetValue(0, !use_joystick);
for (int i = 0; i < (int)Joysticks.Size(); ++i)
{
it = CreateOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]);
GC::WriteBarrier(opt, it);
opt->mItems.Push(it);
if (i == itemnum) opt->mSelectedItem = opt->mItems.Size();
}
if (opt->mSelectedItem >= (int)opt->mItems.Size())
{
opt->mSelectedItem = opt->mItems.Size() - 1;
}
//opt->CalcIndent();
// If the joystick config menu is open, close it if the device it's open for is gone.
if (CurrentMenu != nullptr && (CurrentMenu->IsKindOf("JoystickConfigMenu")))
{
auto p = CurrentMenu->PointerVar<IJoystickConfig>("mJoy");
if (p != nullptr)
{
unsigned i;
for (i = 0; i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == p)
{
break;
}
}
if (i == Joysticks.Size())
{
CurrentMenu->Close();
}
}
}
}
}

1235
source/common/menu/menu.cpp Normal file

File diff suppressed because it is too large Load diff

327
source/common/menu/menu.h Normal file
View file

@ -0,0 +1,327 @@
#ifndef __M_MENU_MENU_H__
#define __M_MENU_MENU_H__
#include "dobject.h"
#include "c_cvars.h"
#include "v_font.h"
#include "textures.h"
EXTERN_CVAR(Float, snd_menuvolume)
EXTERN_CVAR(Int, m_use_mouse);
struct event_t;
class FTexture;
class FFont;
enum EColorRange : int;
class FKeyBindings;
struct FBrokenLines;
enum EMenuKey
{
MKEY_Up,
MKEY_Down,
MKEY_Left,
MKEY_Right,
MKEY_PageUp,
MKEY_PageDown,
//----------------- Keys past here do not repeat.
MKEY_Enter,
MKEY_Back, // Back to previous menu
MKEY_Clear, // Clear keybinding/flip player sprite preview
NUM_MKEYS,
// These are not buttons but events sent from other menus
MKEY_Input, // Sent when input is confirmed
MKEY_Abort, // Input aborted
MKEY_MBYes,
MKEY_MBNo,
};
class DMenu;
extern DMenu *CurrentMenu;
extern int MenuTime;
class DMenuItemBase;
extern DObject* menuDelegate;
//=============================================================================
//
// menu descriptor. This is created from the menu definition lump
// Items must be inserted in the order they are cycled through with the cursor
//
//=============================================================================
class DMenuDescriptor : public DObject
{
DECLARE_CLASS(DMenuDescriptor, DObject)
public:
FName mMenuName = NAME_None;
FString mNetgameMessage;
PClass *mClass = nullptr;
bool mProtected = false;
TArray<DMenuItemBase *> mItems;
size_t PropagateMark() override;
};
class DListMenuDescriptor : public DMenuDescriptor
{
DECLARE_CLASS(DListMenuDescriptor, DMenuDescriptor)
public:
int mSelectedItem;
double mSelectOfsX;
double mSelectOfsY;
FTextureID mSelector;
int mDisplayTop;
double mXpos, mYpos;
int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions
FFont *mFont;
EColorRange mFontColor;
EColorRange mFontColor2;
bool mCenter;
bool mFromEngine;
bool mAnimated;
bool mAnimatedTransition;
int mVirtWidth;
int mVirtHeight;
void Reset();
};
struct FOptionMenuSettings
{
EColorRange mTitleColor;
EColorRange mFontColor;
EColorRange mFontColorValue;
EColorRange mFontColorMore;
EColorRange mFontColorHeader;
EColorRange mFontColorHighlight;
EColorRange mFontColorSelection;
int mLinespacing;
};
class DOptionMenuDescriptor : public DMenuDescriptor
{
DECLARE_CLASS(DOptionMenuDescriptor, DMenuDescriptor)
public:
FString mTitle;
int mSelectedItem;
int mDrawTop;
int mScrollTop;
int mScrollPos;
int mIndent;
int mPosition;
bool mDontDim;
FFont *mFont;
void CalcIndent();
DMenuItemBase *GetItem(FName name);
void Reset();
~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<FName, DMenuDescriptor *> MenuDescriptorList;
extern FOptionMenuSettings OptionSettings;
extern MenuDescriptorList MenuDescriptors;
#define CURSORSPACE (14 * CleanXfac_1)
//=============================================================================
//
//
//
//=============================================================================
struct FMenuRect
{
int x, y;
int width, height;
void set(int _x, int _y, int _w, int _h)
{
x = _x;
y = _y;
width = _w;
height = _h;
}
bool inside(int _x, int _y)
{
return _x >= x && _x < x+width && _y >= y && _y < y+height;
}
};
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;
double start;
int32_t length;
int8_t dir;
bool destroyprev;
bool StartTransition(DMenu* from, DMenu* to, MenuTransitionType animtype);
bool Draw();
};
class DMenu : public DObject
{
DECLARE_CLASS (DMenu, DObject)
HAS_OBJECT_POINTERS
public:
enum
{
MOUSE_Click,
MOUSE_Move,
MOUSE_Release
};
TObjPtr<DMenu*> mParentMenu;
bool mMouseCapture;
bool mBackbuttonSelected;
bool DontDim;
bool DontBlur;
bool Animated;
bool AnimatedTransition;
static int InMenu;
DMenu(DMenu *parent = NULL);
bool TranslateKeyboardEvents();
virtual void Close();
bool CallResponder(event_t *ev);
bool CallMenuEvent(int mkey, bool fromcontroller);
void CallTicker();
void CallDrawer();
bool canAnimate() { return AnimatedTransition; }
};
//=============================================================================
//
// base class for menu items
//
//=============================================================================
class DMenuItemBase : public DObject
{
DECLARE_CLASS(DMenuItemBase, DObject)
public:
double mXpos, mYpos;
FName mAction;
int mEnabled;
bool Activate();
bool SetString(int i, const char *s);
bool GetString(int i, char *s, int len);
bool SetValue(int i, int value);
bool GetValue(int i, int *pvalue);
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
double GetY() { return mYpos; }
};
//=============================================================================
//
//
//
//=============================================================================
struct FOptionValues
{
struct Pair
{
double Value;
FString TextValue;
FString Text;
};
TArray<Pair> mValues;
};
typedef TMap< FName, FOptionValues* > FOptionMap;
extern FOptionMap OptionValues;
//=============================================================================
//
//
//
//=============================================================================
struct event_t;
void M_EnableMenu (bool on) ;
bool M_Responder (event_t *ev);
void M_Ticker (void);
void M_Drawer (void);
void M_Init (void);
void M_CreateMenus();
void M_ActivateMenu(DMenu *menu);
void M_ClearMenus ();
void M_PreviousMenu ();
void M_ParseMenuDefs();
void M_DoStartControlPanel(bool scaleoverride);
void M_SetMenu(FName menu, int param = -1);
void M_StartMessage(const char *message, int messagemode, FName action = NAME_None);
DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar);
void M_MarkMenus();
FTextureID GetMenuTexture(const char* const name);
void DeinitMenus();
bool M_Active();
bool M_IsAnimated();
struct IJoystickConfig;
DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, int v = -1);
DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center);
DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings);
DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy);
DMenuItemBase * CreateListMenuItemPatch(double x, double y, int height, int hotkey, FTextureID tex, FName command, int param);
DMenuItemBase * CreateListMenuItemText(double x, double y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param);
DMenuItemBase * CreateOptionMenuItemCommand(const char *label, FName cmd, bool centered = false);
DMenuItemBase* CreateListMenuItemStaticText(double x, double y, const char* text, FFont* font, PalEntry color, bool centered = false);
void UpdateVRModes(bool considerQuadBuffered=true);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
/*
** messagebox.cpp
** Confirmation, notification screns
**
**---------------------------------------------------------------------------
** Copyright 2010 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 <ctype.h>
#include "menu.h"
#include "gstrings.h"
#include "i_video.h"
#include "c_dispatch.h"
#include "vm.h"
#include "menustate.h"
void M_StartControlPanel(bool makeSound, bool scaleoverride = false);
FName MessageBoxClass = NAME_MessageBoxMenu;
CVAR(Bool, m_quickexit, false, CVAR_ARCHIVE)
typedef void(*hfunc)();
DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler)
{
PARAM_PROLOGUE;
PARAM_POINTERTYPE(Handler, hfunc);
Handler();
return 0;
}
//=============================================================================
//
//
//
//=============================================================================
DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action = NAME_None, hfunc handler = nullptr)
{
auto c = PClass::FindClass(MessageBoxClass);
if (!c->IsDescendantOf(NAME_MessageBoxMenu)) c = PClass::FindClass(NAME_MessageBoxMenu);
auto p = c->CreateNew();
FString namestr = message;
IFVIRTUALPTRNAME(p, NAME_MessageBoxMenu, Init)
{
VMValue params[] = { p, parent, &namestr, messagemode, playsound, action.GetIndex(), reinterpret_cast<void*>(handler) };
VMCall(func, params, countof(params), nullptr, 0);
return (DMenu*)p;
}
return nullptr;
}
//=============================================================================
//
//
//
//=============================================================================
void M_StartMessage(const char *message, int messagemode, FName action)
{
if (CurrentMenu == NULL)
{
// only play a sound if no menu was active before
M_StartControlPanel(menuactive == MENU_Off);
}
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = CurrentMenu;
M_ActivateMenu(newmenu);
}
DEFINE_ACTION_FUNCTION(DMenu, StartMessage)
{
PARAM_PROLOGUE;
PARAM_STRING(msg);
PARAM_INT(mode);
PARAM_NAME(action);
M_StartMessage(msg, mode, action);
return 0;
}

View file

@ -0,0 +1,68 @@
/*
** optionmenu.cpp
** Handler class for the option menus and associated items
**
**---------------------------------------------------------------------------
** Copyright 2010-2017 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_video.h"
#include "menu.h"
#include "vm.h"
//=============================================================================
//
//
//
//=============================================================================
DMenuItemBase *DOptionMenuDescriptor::GetItem(FName name)
{
for(unsigned i=0;i<mItems.Size(); i++)
{
FName nm = mItems[i]->mAction;
if (nm == name) return mItems[i];
}
return NULL;
}
void SetCVarDescription(FBaseCVar* cvar, const FString* label)
{
cvar->AddDescription(*label);
}
DEFINE_ACTION_FUNCTION_NATIVE(_OptionMenuItemOption, SetCVarDescription, SetCVarDescription)
{
PARAM_PROLOGUE;
PARAM_POINTER(cv, FBaseCVar);
PARAM_STRING(label);
SetCVarDescription(cv, &label);
return 0;
}

View file

@ -4,7 +4,7 @@
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit ** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers ** Copyright 2010-2020 Christoph Oelckers
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
@ -38,68 +38,16 @@
#include "m_png.h" #include "m_png.h"
#include "filesystem.h" #include "filesystem.h"
#include "v_text.h" #include "v_text.h"
#include "d_event.h"
#include "gstrings.h" #include "gstrings.h"
#include "d_gui.h"
#include "v_draw.h"
#include "files.h"
#include "resourcefile.h"
#include "cmdlib.h"
#include "files.h"
#include "savegamehelp.h"
#include "i_specialpaths.h"
#include "c_dispatch.h"
#include "build.h"
#include "serializer.h" #include "serializer.h"
#include "vm.h"
#include "i_system.h"
#include "v_video.h"
#include "findfile.h" #include "findfile.h"
#include "inputstate.h" #include "v_draw.h"
#include "gamestate.h" #include "savegamemanager.h"
FSavegameManager savegameManager;
FString BackupSaveGame;
void DoLoadGame(const char* name)
{
if (OpenSaveGameForRead(name))
{
if (gi->LoadGame(nullptr))
{
gameaction = ga_level;
}
else
{
I_Error("%s: Failed to load savegame", name);
}
}
else
{
I_Error("%s: Failed to open savegame", name);
}
}
void FSavegameManager::LoadGame(FSaveGameNode* node)
{
inputState.ClearAllInput();
gi->FreeLevelData();
DoLoadGame(node->Filename);
BackupSaveGame = node->Filename;
}
void FSavegameManager::SaveGame(FSaveGameNode* node, bool ok4q, bool forceq)
{
if (OpenSaveGameForWrite(node->Filename, node->SaveTitle))
{
if (gi->SaveGame(node) && FinishSavegameWrite())
{
FString fn = node->Filename;
FString desc = node->SaveTitle;
NotifyNewSave(fn, desc, ok4q, forceq);
Printf(PRINT_NOTIFY, "%s\n", GStrings("GAME SAVED"));
BackupSaveGame = node->Filename;
}
}
}
//============================================================================= //=============================================================================
// //
@ -107,7 +55,7 @@ void FSavegameManager::SaveGame(FSaveGameNode* node, bool ok4q, bool forceq)
// //
//============================================================================= //=============================================================================
void FSavegameManager::ClearSaveGames() void FSavegameManagerBase::ClearSaveGames()
{ {
for (unsigned i = 0; i<SaveGames.Size(); i++) for (unsigned i = 0; i<SaveGames.Size(); i++)
{ {
@ -117,7 +65,7 @@ void FSavegameManager::ClearSaveGames()
SaveGames.Clear(); SaveGames.Clear();
} }
FSavegameManager::~FSavegameManager() FSavegameManagerBase::~FSavegameManagerBase()
{ {
ClearSaveGames(); ClearSaveGames();
} }
@ -128,7 +76,7 @@ FSavegameManager::~FSavegameManager()
// //
//============================================================================= //=============================================================================
int FSavegameManager::RemoveSaveSlot(int index) int FSavegameManagerBase::RemoveSaveSlot(int index)
{ {
int listindex = SaveGames[0]->bNoDelete ? index - 1 : index; int listindex = SaveGames[0]->bNoDelete ? index - 1 : index;
if (listindex < 0) return index; if (listindex < 0) return index;
@ -155,13 +103,20 @@ int FSavegameManager::RemoveSaveSlot(int index)
return index; return index;
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(sel);
ACTION_RETURN_INT(self->RemoveSaveSlot(sel));
}
//============================================================================= //=============================================================================
// //
// //
// //
//============================================================================= //=============================================================================
int FSavegameManager::InsertSaveNode(FSaveGameNode *node) int FSavegameManagerBase::InsertSaveNode(FSaveGameNode *node)
{ {
if (SaveGames.Size() == 0) if (SaveGames.Size() == 0)
{ {
@ -175,7 +130,7 @@ int FSavegameManager::InsertSaveNode(FSaveGameNode *node)
else else
{ // Add node at top of list { // Add node at top of list
unsigned int i = 0; unsigned int i = 0;
if (SaveGames[0] == &NewSaveNode) i++; // To not insert above the "new savegame" dummy entry. //if (SaveGames[0] == &NewSaveNode) i++; // To not insert above the "new savegame" dummy entry.
for (; i < SaveGames.Size(); i++) for (; i < SaveGames.Size(); i++)
{ {
if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0) if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0)
@ -188,72 +143,13 @@ int FSavegameManager::InsertSaveNode(FSaveGameNode *node)
} }
} }
//=============================================================================
//
// M_ReadSaveStrings
//
// Find savegames and read their titles
//
//=============================================================================
void FSavegameManager::ReadSaveStrings()
{
if (SaveGames.Size() == 0)
{
void *filefirst;
findstate_t c_file;
FString filter;
LastSaved = LastAccessed = -1;
quickSaveSlot = nullptr;
filter = G_BuildSaveName("*");
filefirst = I_FindFirst(filter.GetChars(), &c_file);
if (filefirst != ((void *)(-1)))
{
do
{
// I_FindName only returns the file's name and not its full path
FString filepath = G_BuildSaveName(I_FindName(&c_file));
FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true);
if (savegame != nullptr)
{
FResourceLump *info = savegame->FindLump("info.json");
if (info == nullptr)
{
// savegame info not found. This is not a savegame so leave it alone.
delete savegame;
continue;
}
auto fr = info->NewReader();
FString title;
int check = G_ValidateSavegame(fr, &title, true);
fr.Close();
delete savegame;
if (check != 0)
{
FSaveGameNode *node = new FSaveGameNode;
node->Filename = filepath;
node->bOldVersion = check == -1;
node->bMissingWads = check == -2;
node->SaveTitle = title;
InsertSaveNode(node);
}
}
} while (I_FindNext (filefirst, &c_file) == 0);
I_FindClose (filefirst);
}
}
}
//============================================================================= //=============================================================================
// //
// //
// //
//============================================================================= //=============================================================================
void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave) void FSavegameManagerBase::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave)
{ {
FSaveGameNode *node; FSaveGameNode *node;
@ -278,7 +174,7 @@ void FSavegameManager::NotifyNewSave(const FString &file, const FString &title,
if (okForQuicksave) if (okForQuicksave)
{ {
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = i - 1; // without <new save> item LastAccessed = LastSaved = i;
} }
return; return;
} }
@ -294,7 +190,7 @@ void FSavegameManager::NotifyNewSave(const FString &file, const FString &title,
if (okForQuicksave) if (okForQuicksave)
{ {
if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node; if (quickSaveSlot == nullptr || quickSaveSlot == (FSaveGameNode*)1 || forceQuicksave) quickSaveSlot = node;
LastAccessed = LastSaved = index - 1; // without <new save> item LastAccessed = LastSaved = index;
} }
else else
{ {
@ -302,27 +198,31 @@ void FSavegameManager::NotifyNewSave(const FString &file, const FString &title,
} }
} }
//============================================================================= //=============================================================================
// //
// Loads the savegame // Loads the savegame
// //
//============================================================================= //=============================================================================
void FSavegameManager::LoadSavegame(int Selected) void FSavegameManagerBase::LoadSavegame(int Selected)
{ {
auto sel = savegameManager.GetSavegame(Selected); PerformLoadGame(SaveGames[Selected]->Filename.GetChars(), true);
if (sel && !sel->bOldVersion && !sel->bMissingWads) if (quickSaveSlot == (FSaveGameNode*)1)
{ {
savegameManager.LoadGame(SaveGames[Selected]); quickSaveSlot = SaveGames[Selected];
if (quickSaveSlot == (FSaveGameNode*)1)
{
quickSaveSlot = SaveGames[Selected];
}
M_ClearMenus();
LastAccessed = Selected;
} }
M_ClearMenus();
LastAccessed = Selected;
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(sel);
self->LoadSavegame(sel);
return 0;
}
//============================================================================= //=============================================================================
// //
@ -330,13 +230,12 @@ void FSavegameManager::LoadSavegame(int Selected)
// //
//============================================================================= //=============================================================================
void FSavegameManager::DoSave(int Selected, const char *savegamestring) void FSavegameManagerBase::DoSave(int Selected, const char *savegamestring)
{ {
if (Selected != 0) if (Selected != 0)
{ {
auto node = *SaveGames[Selected]; auto node = SaveGames[Selected];
node.SaveTitle = savegamestring; PerformSaveGame(node->Filename.GetChars(), savegamestring);
savegameManager.SaveGame(&node, true, false);
} }
else else
{ {
@ -346,18 +245,25 @@ void FSavegameManager::DoSave(int Selected, const char *savegamestring)
for (i = 0;; ++i) for (i = 0;; ++i)
{ {
filename = G_BuildSaveName(FStringf("save%04d", i)); filename = BuildSaveName("save", i);
if (!FileExists(filename)) if (!FileExists(filename))
{ {
break; break;
} }
} }
FSaveGameNode sg{ savegamestring, filename }; PerformSaveGame(filename, savegamestring);
savegameManager.SaveGame(&sg, true, false);
} }
M_ClearMenus(); M_ClearMenus();
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(sel);
PARAM_STRING(name);
self->DoSave(sel, name);
return 0;
}
//============================================================================= //=============================================================================
// //
@ -365,7 +271,7 @@ void FSavegameManager::DoSave(int Selected, const char *savegamestring)
// //
//============================================================================= //=============================================================================
unsigned FSavegameManager::ExtractSaveData(int index) unsigned FSavegameManagerBase::ExtractSaveData(int index)
{ {
FResourceFile *resf; FResourceFile *resf;
FSaveGameNode *node; FSaveGameNode *node;
@ -406,15 +312,7 @@ unsigned FSavegameManager::ExtractSaveData(int index)
} }
info->Unlock(); info->Unlock();
FString comment, fcomment, ncomment, mtime; SaveCommentString = ExtractSaveComment(arc);
arc("Creation Time", comment)
("Map Label", fcomment)
("Map Name", ncomment)
("Map Time", mtime);
comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars());
SaveCommentString = comment;
FResourceLump *pic = resf->FindLump("savepic.png"); FResourceLump *pic = resf->FindLump("savepic.png");
if (pic != nullptr) if (pic != nullptr)
@ -438,7 +336,6 @@ unsigned FSavegameManager::ExtractSaveData(int index)
{ {
delete SavePic; delete SavePic;
SavePic = nullptr; SavePic = nullptr;
SavePicData.Clear();
} }
} }
} }
@ -453,7 +350,7 @@ unsigned FSavegameManager::ExtractSaveData(int index)
// //
//============================================================================= //=============================================================================
void FSavegameManager::UnloadSaveData() void FSavegameManagerBase::UnloadSaveData()
{ {
if (SavePic != nullptr) if (SavePic != nullptr)
{ {
@ -462,7 +359,13 @@ void FSavegameManager::UnloadSaveData()
SaveCommentString = ""; SaveCommentString = "";
SavePic = nullptr; SavePic = nullptr;
SavePicData.Clear(); }
DEFINE_ACTION_FUNCTION(FSavegameManager, UnloadSaveData)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
self->UnloadSaveData();
return 0;
} }
//============================================================================= //=============================================================================
@ -471,7 +374,7 @@ void FSavegameManager::UnloadSaveData()
// //
//============================================================================= //=============================================================================
void FSavegameManager::ClearSaveStuff() void FSavegameManagerBase::ClearSaveStuff()
{ {
UnloadSaveData(); UnloadSaveData();
if (quickSaveSlot == (FSaveGameNode*)1) if (quickSaveSlot == (FSaveGameNode*)1)
@ -480,6 +383,12 @@ void FSavegameManager::ClearSaveStuff()
} }
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, ClearSaveStuff)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
self->ClearSaveStuff();
return 0;
}
//============================================================================= //=============================================================================
// //
@ -487,13 +396,22 @@ void FSavegameManager::ClearSaveStuff()
// //
//============================================================================= //=============================================================================
bool FSavegameManager::DrawSavePic(int x, int y, int w, int h) bool FSavegameManagerBase::DrawSavePic(int x, int y, int w, int h)
{ {
if (SavePic == nullptr) return false; if (SavePic == nullptr) return false;
DrawTexture(twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); DrawTexture(twod, SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE);
return true; return true;
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSavePic)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(x);
PARAM_INT(y);
PARAM_INT(w);
PARAM_INT(h);
ACTION_RETURN_BOOL(self->DrawSavePic(x, y, w, h));
}
//============================================================================= //=============================================================================
// //
@ -501,7 +419,7 @@ bool FSavegameManager::DrawSavePic(int x, int y, int w, int h)
// //
//============================================================================= //=============================================================================
void FSavegameManager::SetFileInfo(int Selected) void FSavegameManagerBase::SetFileInfo(int Selected)
{ {
if (!SaveGames[Selected]->Filename.IsEmpty()) if (!SaveGames[Selected]->Filename.IsEmpty())
{ {
@ -509,6 +427,14 @@ void FSavegameManager::SetFileInfo(int Selected)
} }
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, SetFileInfo)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(i);
self->SetFileInfo(i);
return 0;
}
//============================================================================= //=============================================================================
// //
@ -516,11 +442,16 @@ void FSavegameManager::SetFileInfo(int Selected)
// //
//============================================================================= //=============================================================================
unsigned FSavegameManager::SavegameCount() unsigned FSavegameManagerBase::SavegameCount()
{ {
return SaveGames.Size(); return SaveGames.Size();
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, SavegameCount)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
ACTION_RETURN_INT(self->SavegameCount());
}
//============================================================================= //=============================================================================
// //
@ -528,11 +459,17 @@ unsigned FSavegameManager::SavegameCount()
// //
//============================================================================= //=============================================================================
FSaveGameNode *FSavegameManager::GetSavegame(int i) FSaveGameNode *FSavegameManagerBase::GetSavegame(int i)
{ {
return SaveGames[i]; return SaveGames[i];
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, GetSavegame)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
PARAM_INT(i);
ACTION_RETURN_POINTER(self->GetSavegame(i));
}
//============================================================================= //=============================================================================
// //
@ -540,13 +477,19 @@ FSaveGameNode *FSavegameManager::GetSavegame(int i)
// //
//============================================================================= //=============================================================================
void FSavegameManager::InsertNewSaveNode() void FSavegameManagerBase::InsertNewSaveNode()
{ {
NewSaveNode.SaveTitle = GStrings("NEWSAVE"); NewSaveNode.SaveTitle = GStrings("NEWSAVE");
NewSaveNode.bNoDelete = true; NewSaveNode.bNoDelete = true;
SaveGames.Insert(0, &NewSaveNode); SaveGames.Insert(0, &NewSaveNode);
} }
DEFINE_ACTION_FUNCTION(FSavegameManager, InsertNewSaveNode)
{
PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
self->InsertNewSaveNode();
return 0;
}
//============================================================================= //=============================================================================
// //
@ -554,7 +497,7 @@ void FSavegameManager::InsertNewSaveNode()
// //
//============================================================================= //=============================================================================
bool FSavegameManager::RemoveNewSaveNode() bool FSavegameManagerBase::RemoveNewSaveNode()
{ {
if (SaveGames[0] == &NewSaveNode) if (SaveGames[0] == &NewSaveNode)
{ {
@ -564,176 +507,35 @@ bool FSavegameManager::RemoveNewSaveNode()
return false; return false;
} }
//============================================================================= DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode)
//
//
//
//=============================================================================
CVAR(Bool, saveloadconfirmation, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, autosavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextautosave = -1;
CVAR(Int, disableautosave, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Int, autosavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{ {
if (self < 1) PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
self = 1; ACTION_RETURN_INT(self->RemoveNewSaveNode());
} }
CVAR(Int, quicksavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextquicksave = -1; DEFINE_ACTION_FUNCTION(FSavegameManager, ReadSaveStrings)
CUSTOM_CVAR(Int, quicksavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{ {
if (self < 1) PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
self = 1; self->ReadSaveStrings();
return 0;
} }
void M_Autosave() DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData)
{ {
if (disableautosave) return; PARAM_SELF_STRUCT_PROLOGUE(FSavegameManagerBase);
if (!gi->CanSave()) return; PARAM_INT(sel);
FString description; ACTION_RETURN_INT(self->ExtractSaveData(sel));
FString file;
// Keep a rotating sets of autosaves
UCVarValue num;
const char* readableTime;
int count = autosavecount != 0 ? autosavecount : 1;
if (nextautosave == -1)
{
nextautosave = (autosavenum + 1) % count;
}
num.Int = nextautosave;
autosavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg;
sg.Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave));
readableTime = myasctime();
sg.SaveTitle.Format("Autosave %s", readableTime);
nextautosave = (nextautosave + 1) % count;
savegameManager.SaveGame(&sg, false, false);
}
CCMD(autosave)
{
gameaction = ga_autosave;
}
CCMD(rotatingquicksave)
{
if (!gi->CanSave()) return;
FString description;
FString file;
// Keep a rotating sets of quicksaves
UCVarValue num;
const char* readableTime;
int count = quicksavecount != 0 ? quicksavecount : 1;
if (nextquicksave == -1)
{
nextquicksave = (quicksavenum + 1) % count;
}
num.Int = nextquicksave;
quicksavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg;
sg.Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave));
readableTime = myasctime();
sg.SaveTitle.Format("Quicksave %s", readableTime);
nextquicksave = (nextquicksave + 1) % count;
savegameManager.SaveGame(&sg, false, false);
} }
//============================================================================= DEFINE_FIELD(FSaveGameNode, SaveTitle);
// DEFINE_FIELD(FSaveGameNode, Filename);
// DEFINE_FIELD(FSaveGameNode, bOldVersion);
// DEFINE_FIELD(FSaveGameNode, bMissingWads);
//============================================================================= DEFINE_FIELD(FSaveGameNode, bNoDelete);
CCMD(quicksave) DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, WindowSize);
{ // F6 DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, quickSaveSlot);
if (!gi->CanSave()) return; DEFINE_FIELD_X(SavegameManager, FSavegameManagerBase, SaveCommentString);
if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Savegamemenu);
return;
}
auto slot = savegameManager.quickSaveSlot;
// [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation)
{
savegameManager.SaveGame(savegameManager.quickSaveSlot, true, true);
return;
}
FString tempstring = GStrings("QSPROMPT");
tempstring.Substitute("%s", slot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, INT_MAX, false, NAME_None, [](bool res)
{
if (res)
{
savegameManager.SaveGame(savegameManager.quickSaveSlot, true, true);
}
return true;
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(quickload)
{ // F9
#if 0
if (netgame)
{
M_StartControlPanel(true);
M_StartMessage(GStrings("QLOADNET"), 1);
return;
}
#endif
if (savegameManager.quickSaveSlot == nullptr || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
// signal that whatever gets loaded should be the new quicksave
savegameManager.quickSaveSlot = (FSaveGameNode*)1;
M_SetMenu(NAME_Loadgamemenu);
return;
}
// [mxd]. Just load the game, no questions asked.
if (!saveloadconfirmation)
{
savegameManager.LoadGame(savegameManager.quickSaveSlot);
return;
}
FString tempstring = GStrings("QLPROMPT");
tempstring.Substitute("%s", savegameManager.quickSaveSlot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, INT_MAX, false, NAME_None, [](bool res)
{
if (res)
{
savegameManager.LoadGame(savegameManager.quickSaveSlot);
}
return true;
});
M_ActivateMenu(newmenu);
}

View file

@ -0,0 +1,61 @@
#pragma once
#include "zstring.h"
#include "tarray.h"
class FGameTexture;
class FSerializer;
// The savegame manager contains too much code that is game specific. Parts are shareable but need more work first.
struct FSaveGameNode
{
FString SaveTitle;
FString Filename;
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;
};
struct FSavegameManagerBase
{
protected:
TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1;
int LastAccessed = -1;
FGameTexture *SavePic = nullptr;
public:
int WindowSize = 0;
FString SaveCommentString;
FSaveGameNode *quickSaveSlot = nullptr;
virtual ~FSavegameManagerBase();
protected:
int InsertSaveNode(FSaveGameNode *node);
virtual void PerformSaveGame(const char *fn, const char *sgdesc) = 0;
virtual void PerformLoadGame(const char *fn, bool) = 0;
virtual FString ExtractSaveComment(FSerializer &arc) = 0;
virtual FString BuildSaveName(const char* prefix, int slot) = 0;
public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave);
void ClearSaveGames();
virtual void ReadSaveStrings() = 0;
void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring);
unsigned ExtractSaveData(int index);
void ClearSaveStuff();
bool DrawSavePic(int x, int y, int w, int h);
void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor);
void SetFileInfo(int Selected);
unsigned SavegameCount();
FSaveGameNode *GetSavegame(int i);
void InsertNewSaveNode();
bool RemoveNewSaveNode();
};

View file

@ -125,7 +125,7 @@ void FSamplerManager::SetTextureFilterMode()
{ {
glSamplerParameteri(mSamplers[i], GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter); glSamplerParameteri(mSamplers[i], GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
glSamplerParameteri(mSamplers[i], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter); glSamplerParameteri(mSamplers[i], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
glSamplerParameterf(mSamplers[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_filter_anisotropic); glSamplerParameterf(mSamplers[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, filter > 0? gl_texture_filter_anisotropic : 1.0);
} }
glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter); glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter); glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);

View file

@ -234,6 +234,7 @@ class_head(X) ::= EXTEND CLASS(T) IDENTIFIER(A).
X = head; X = head;
} }
class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C). class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C).
{ {
NEW_AST_NODE(Class,head,T); NEW_AST_NODE(Class,head,T);
@ -394,6 +395,17 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body
X = def; X = def;
} }
struct_def(X) ::= EXTEND STRUCT(T) IDENTIFIER(A) LBRACE opt_struct_body(B) RBRACE opt_semicolon.
{
NEW_AST_NODE(Struct,def,T);
def->NodeName = A.Name();
def->Body = B;
def->Type = nullptr;
def->Symbol = nullptr;
def->Flags = ZCC_Extension;
X = def;
}
%type struct_flags{ClassFlagsBlock} %type struct_flags{ClassFlagsBlock}
struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; } struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; }
struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; } struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; }

View file

@ -377,8 +377,30 @@ void ZCCCompiler::ProcessMixin(ZCC_MixinDef *cnode, PSymbolTreeNode *treenode)
void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZCC_Class *outer) void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZCC_Class *outer)
{ {
Structs.Push(new ZCC_StructWork(static_cast<ZCC_Struct *>(cnode), treenode, outer)); ZCC_StructWork* cls = nullptr;
ZCC_StructWork *cls = Structs.Last();
// If this is a struct extension, put the new node directly into the existing class.
if (cnode->Flags == ZCC_Extension)
{
for (auto strct : Structs)
{
if (strct->NodeName() == cnode->NodeName)
{
cls = strct;
break;
}
}
if (cls == nullptr)
{
Error(cnode, "Struct %s cannot be found in the current translation unit.", FName(cnode->NodeName).GetChars());
return;
}
}
else
{
Structs.Push(new ZCC_StructWork(static_cast<ZCC_Struct*>(cnode), treenode, outer));
cls = Structs.Last();
}
auto node = cnode->Body; auto node = cnode->Body;
PSymbolTreeNode *childnode; PSymbolTreeNode *childnode;
@ -494,7 +516,15 @@ ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols,
ProcessClass(static_cast<ZCC_Class *>(node), tnode); ProcessClass(static_cast<ZCC_Class *>(node), tnode);
break; break;
} }
goto common;
case AST_Struct: case AST_Struct:
if (static_cast<ZCC_Class*>(node)->Flags == ZCC_Extension)
{
ProcessStruct(static_cast<ZCC_Struct*>(node), tnode, nullptr);
break;
}
common:
case AST_ConstantDef: case AST_ConstantDef:
case AST_Enum: case AST_Enum:
if ((tnode = AddTreeNode(static_cast<ZCC_NamedNode *>(node)->NodeName, node, GlobalTreeNodes))) if ((tnode = AddTreeNode(static_cast<ZCC_NamedNode *>(node)->NodeName, node, GlobalTreeNodes)))

View file

@ -47,6 +47,7 @@
#include "gstrings.h" #include "gstrings.h"
#include "printf.h" #include "printf.h"
#include "s_music.h" #include "s_music.h"
#include "i_interface.h"
//========================================================================== //==========================================================================
@ -354,6 +355,18 @@ DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetCursor, GetCursor)
ACTION_RETURN_STRING(FString(self->GetCursor())); ACTION_RETURN_STRING(FString(self->GetCursor()));
} }
static int GetGlyphHeight(FFont* fnt, int code)
{
auto glyph = fnt->GetChar(code, CR_UNTRANSLATED, nullptr);
return glyph ? (int)glyph->GetDisplayHeight() : 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(FFont, GetGlyphHeight, GetGlyphHeight)
{
PARAM_SELF_STRUCT_PROLOGUE(FFont);
PARAM_INT(code);
ACTION_RETURN_INT(GetGlyphHeight(self, code));
}
//========================================================================== //==========================================================================
// //
// file system // file system
@ -541,7 +554,7 @@ DEFINE_ACTION_FUNCTION(_CVar, FindCVar)
// //
// //
//============================================================================= //=============================================================================
#if 0
DEFINE_ACTION_FUNCTION(FKeyBindings, SetBind) DEFINE_ACTION_FUNCTION(FKeyBindings, SetBind)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
@ -623,7 +636,7 @@ DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand)
C_DoCommand(cmd); C_DoCommand(cmd);
return 0; return 0;
} }
#endif
DEFINE_ACTION_FUNCTION(_Console, MidPrint) DEFINE_ACTION_FUNCTION(_Console, MidPrint)
{ {
PARAM_PROLOGUE; PARAM_PROLOGUE;
@ -659,3 +672,5 @@ DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop);
DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses) DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses)
DEFINE_GLOBAL(Bindings) DEFINE_GLOBAL(Bindings)
DEFINE_GLOBAL(AutomapBindings)
DEFINE_GLOBAL(generic_ui)

View file

@ -265,6 +265,11 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset
} }
} }
} }
if (!(flags & TEXMAN_NoAlias))
{
int* alias = aliases.CheckKey(name);
if (alias) return FTextureID(*alias);
}
return FTextureID(-1); return FTextureID(-1);
} }
@ -1539,6 +1544,19 @@ void FTextureManager::SetTranslation(FTextureID fromtexnum, FTextureID totexnum)
} }
//-----------------------------------------------------------------------------
//
// Adds an alias name to the texture manager.
// Aliases are only checked if no real texture with the given name exists.
//
//-----------------------------------------------------------------------------
void FTextureManager::AddAlias(const char* name, FGameTexture* tex)
{
FTextureID id = tex->GetID();
if (tex != Textures[id.GetIndex()].Texture || !tex->isValid()) return; // Whatever got passed in here was not valid, so ignore the alias.
aliases.Insert(name, id.GetIndex());
}
//========================================================================== //==========================================================================
// //

View file

@ -89,7 +89,8 @@ public:
TEXMAN_ShortNameOnly = 16, TEXMAN_ShortNameOnly = 16,
TEXMAN_DontCreate = 32, TEXMAN_DontCreate = 32,
TEXMAN_Localize = 64, TEXMAN_Localize = 64,
TEXMAN_ForceLookup = 128 TEXMAN_ForceLookup = 128,
TEXMAN_NoAlias = 256,
}; };
enum enum
@ -144,6 +145,8 @@ public:
tmanips.Remove(cname); tmanips.Remove(cname);
} }
void AddAlias(const char* name, FGameTexture* tex);
private: private:
// texture counting // texture counting
@ -190,6 +193,7 @@ private:
TArray<int> Translation; TArray<int> Translation;
TMap<FName, TextureManipulation> tmanips; TMap<FName, TextureManipulation> tmanips;
TMap<FName, int> aliases;
public: public:

View file

@ -73,8 +73,10 @@ bool FName::NameManager::Inited;
static const char *PredefinedNames[] = static const char *PredefinedNames[] =
{ {
#define xx(n) #n, #define xx(n) #n,
#define xy(n, s) s,
#include "namedef.h" #include "namedef.h"
#undef xx #undef xx
#undef xy
}; };
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------

View file

@ -40,8 +40,10 @@
enum ENamedName enum ENamedName
{ {
#define xx(n) NAME_##n, #define xx(n) NAME_##n,
#define xy(n, s) NAME_##n,
#include "namedef.h" #include "namedef.h"
#undef xx #undef xx
#undef xy
}; };
class FString; class FString;

View file

@ -252,11 +252,11 @@ void ChangeLevel(MapRecord* map, int skill)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void DeferedStartGame(MapRecord* map, int skill) void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
{ {
g_nextmap = map; g_nextmap = map;
g_nextskill = skill; g_nextskill = skill;
gameaction = ga_newgame; gameaction = nostopsound? ga_newgamenostopsound : ga_newgame;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View file

@ -62,9 +62,12 @@
#include "v_video.h" #include "v_video.h"
#include "v_draw.h" #include "v_draw.h"
#include "g_input.h" #include "g_input.h"
#include "menu.h" #include "razemenu.h"
#include "raze_music.h" #include "raze_music.h"
#include "gstrings.h" #include "gstrings.h"
#include "menustate.h"
#include "i_interface.h"
#include "vm.h"
#define LEFTMARGIN 8 #define LEFTMARGIN 8
#define RIGHTMARGIN 8 #define RIGHTMARGIN 8
@ -108,6 +111,8 @@ constate_e ConsoleState = c_up;
double NotifyFontScale = 1; double NotifyFontScale = 1;
DEFINE_GLOBAL(NotifyFontScale)
void C_SetNotifyFontScale(double scale) void C_SetNotifyFontScale(double scale)
{ {
NotifyFontScale = scale; NotifyFontScale = scale;
@ -165,9 +170,6 @@ CVAR(Int, developer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
EXTERN_CVAR(Int, uiscale); EXTERN_CVAR(Int, uiscale);
CVAR(Bool, generic_ui, false, CVAR_ARCHIVE)
struct History struct History
{ {
struct History *Older; struct History *Older;

View file

@ -38,7 +38,7 @@
#include "c_console.h" #include "c_console.h"
#include "d_gui.h" #include "d_gui.h"
#include "inputstate.h" #include "inputstate.h"
#include "menu.h" #include "razemenu.h"
#include "gamestate.h" #include "gamestate.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "uiinput.h" #include "uiinput.h"

View file

@ -36,9 +36,10 @@
#include "vm.h" #include "vm.h"
#include "c_buttons.h" #include "c_buttons.h"
#include "v_draw.h" #include "v_draw.h"
#include "menu.h" #include "razemenu.h"
#include "gamestruct.h" #include "gamestruct.h"
#include "gamecvars.h" #include "gamecvars.h"
#include "menustate.h"
enum enum
{ {

View file

@ -46,7 +46,7 @@
#include <inttypes.h> #include <inttypes.h>
#include "version.h" #include "version.h"
#include "menu.h" #include "razemenu.h"
#include "i_video.h" #include "i_video.h"
#include "c_console.h" #include "c_console.h"
#include "d_net.h" #include "d_net.h"

View file

@ -41,7 +41,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "i_specialpaths.h" #include "i_specialpaths.h"
#include "raze_music.h" #include "raze_music.h"
#include "statistics.h" #include "statistics.h"
#include "menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "quotemgr.h" #include "quotemgr.h"
#include "mapinfo.h" #include "mapinfo.h"
@ -71,15 +71,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "uiinput.h" #include "uiinput.h"
#include "d_net.h" #include "d_net.h"
#include "automap.h" #include "automap.h"
#include "v_draw.h"
#include "gi.h"
CVAR(Bool, autoloadlights, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, autoloadlights, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, autoloadbrightmaps, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, autoloadbrightmaps, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVARD(Bool, invertmousex, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert horizontal mouse movement") CVARD(Bool, invertmousex, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert horizontal mouse movement")
CVARD(Bool, invertmouse, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert vertical mouse movement") CVARD(Bool, invertmouse, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert vertical mouse movement")
EXTERN_CVAR(Bool, ui_generic)
CUSTOM_CVAR(String, language, "auto", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) CUSTOM_CVAR(String, language, "auto", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
{ {
GStrings.UpdateLanguage(self); GStrings.UpdateLanguage(self);
UpdateGenericUI(ui_generic);
} }
CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
@ -132,8 +137,6 @@ bool AppActive = true;
FString currentGame; FString currentGame;
FString LumpFilter; FString LumpFilter;
TMap<FName, int32_t> 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(Bool, queryiwad, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(String, defaultiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR(String, defaultiwad, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
@ -158,12 +161,6 @@ int StrTable_GetGender()
bool validFilter(const char* str); bool validFilter(const char* str);
static StringtableCallbacks stblcb =
{
validFilter,
StrTable_GetGender
};
extern int chatmodeon; extern int chatmodeon;
bool System_WantGuiCapture() bool System_WantGuiCapture()
@ -465,7 +462,7 @@ namespace ShadowWarrior
{ {
::GameInterface* CreateInterface(); ::GameInterface* CreateInterface();
} }
namespace Powerslave namespace Exhumed
{ {
::GameInterface* CreateInterface(); ::GameInterface* CreateInterface();
} }
@ -482,7 +479,7 @@ void CheckFrontend(int flags)
} }
else if (flags & GAMEFLAG_PSEXHUMED) else if (flags & GAMEFLAG_PSEXHUMED)
{ {
gi = Powerslave::CreateInterface(); gi = Exhumed::CreateInterface();
} }
else else
{ {
@ -493,6 +490,8 @@ void CheckFrontend(int flags)
void I_StartupJoysticks(); void I_StartupJoysticks();
void I_ShutdownInput(); void I_ShutdownInput();
int RunGame(); int RunGame();
void System_MenuClosed();
void System_MenuDim();
int GameMain() int GameMain()
{ {
@ -512,9 +511,13 @@ int GameMain()
nullptr, nullptr,
System_GetSceneRect, System_GetSceneRect,
nullptr, nullptr,
nullptr, System_MenuDim,
nullptr, nullptr,
System_DispatchEvent, System_DispatchEvent,
validFilter,
StrTable_GetGender,
System_MenuClosed,
nullptr
}; };
try try
@ -533,7 +536,7 @@ int GameMain()
r = -1; r = -1;
} }
DeleteScreenJob(); DeleteScreenJob();
M_ClearMenus(true); DeinitMenus();
if (gi) if (gi)
{ {
gi->FreeGameData(); // Must be done before taking down any subsystems. gi->FreeGameData(); // Must be done before taking down any subsystems.
@ -551,7 +554,6 @@ int GameMain()
TileFiles.CloseAll(); // delete the texture data before shutting down graphics. TileFiles.CloseAll(); // delete the texture data before shutting down graphics.
GLInterface.Deinit(); GLInterface.Deinit();
I_ShutdownGraphics(); I_ShutdownGraphics();
M_DeinitMenus();
engineUnInit(); engineUnInit();
if (gi) if (gi)
{ {
@ -745,6 +747,7 @@ static TArray<GrpEntry> SetupGame()
currentGame = LumpFilter; currentGame = LumpFilter;
currentGame.Truncate(currentGame.IndexOf(".")); currentGame.Truncate(currentGame.IndexOf("."));
CheckFrontend(g_gameType); CheckFrontend(g_gameType);
gameinfo.gametype = g_gameType;
return usedgroups; return usedgroups;
} }
@ -851,10 +854,10 @@ int RunGame()
TexMan.Init([]() {}, [](BuildInfo &) {}); TexMan.Init([]() {}, [](BuildInfo &) {});
V_InitFonts(); V_InitFonts();
TileFiles.Init(); TileFiles.Init();
sfx_empty = fileSystem.FindFile("engine/dsempty.lmp"); // this must be done outside the sound code because it's initialized late.
I_InitSound(); I_InitSound();
Mus_InitMusic(); Mus_InitMusic();
S_ParseSndInfo(); S_ParseSndInfo();
S_ParseReverbDef();
InitStatistics(); InitStatistics();
LoadScripts(); LoadScripts();
SetDefaultStrings(); SetDefaultStrings();
@ -870,8 +873,11 @@ int RunGame()
if (exec) exec->ExecCommands(); if (exec) exec->ExecCommands();
SetupGameButtons(); SetupGameButtons();
gameinfo.mBackButton = "engine/graphics/m_back.png";
gi->app_init(); gi->app_init();
SetDefaultMenuColors();
M_Init(); M_Init();
BuildGameMenus();
if (!(paletteloaded & PALETTE_MAIN)) if (!(paletteloaded & PALETTE_MAIN))
I_FatalError("No palette found."); I_FatalError("No palette found.");
@ -1791,3 +1797,70 @@ void playerProcessHelpers(fixed_t* q16ang, double* angAdjust, fixed_t* angTarget
*q16horiz += FloatToFixed(scaleAdjust * *horizAdjust); *q16horiz += FloatToFixed(scaleAdjust * *horizAdjust);
} }
} }
bool M_Active()
{
return CurrentMenu != nullptr || ConsoleState == c_down || ConsoleState == c_falling;
}
struct gamefilter
{
const char* gamename;
int gameflag;
};
static const gamefilter games[] = {
{ "Duke", GAMEFLAG_DUKE},
{ "Nam", GAMEFLAG_NAM | GAMEFLAG_NAPALM},
{ "NamOnly", GAMEFLAG_NAM}, // for cases where the difference matters.
{ "Napalm", GAMEFLAG_NAPALM},
{ "WW2GI", GAMEFLAG_WW2GI},
{ "Redneck", GAMEFLAG_RR},
{ "RedneckRides", GAMEFLAG_RRRA},
{ "Deer", GAMEFLAG_DEER},
{ "Blood", GAMEFLAG_BLOOD},
{ "ShadowWarrior", GAMEFLAG_SW},
{ "Exhumed", GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED},
{ "Plutopak", GAMEFLAG_PLUTOPAK},
{ "Worldtour", GAMEFLAG_WORLDTOUR},
{ "Shareware", GAMEFLAG_SHAREWARE},
};
bool validFilter(const char* str)
{
for (auto& gf : games)
{
if (g_gameType & gf.gameflag)
{
if (!stricmp(str, gf.gamename)) return true;
}
}
return false;
}
#include "vm.h"
DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow)
{
PARAM_PROLOGUE;
if (numret > 0) ret[0].SetInt(windowxy1.x);
if (numret > 1) ret[1].SetInt(windowxy1.y);
if (numret > 2) ret[2].SetInt(windowxy2.x - windowxy1.x + 1);
if (numret > 3) ret[3].SetInt(windowxy2.y - windowxy1.y + 1);
return MIN(numret, 4);
}
DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight)
{
PARAM_PROLOGUE;
PARAM_INT(shade);
ACTION_RETURN_INT(shadeToLight(shade));
}
extern bool demoplayback;
DEFINE_GLOBAL(multiplayer)
DEFINE_GLOBAL(netgame)
DEFINE_GLOBAL(gameaction)
DEFINE_GLOBAL(gamestate)
DEFINE_GLOBAL(demoplayback)
DEFINE_GLOBAL(consoleplayer)

View file

@ -22,15 +22,11 @@ extern cycle_t drawtime, actortime, thinktime, gameupdatetime;
extern bool r_NoInterpolate; extern bool r_NoInterpolate;
struct MapRecord; struct MapRecord;
struct FSaveGameNode;
extern MapRecord* g_nextmap; extern MapRecord* g_nextmap;
extern int g_nextskill; extern int g_nextskill;
extern FSaveGameNode* g_savenode;
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 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<FName, int32_t> NameToTileIndex;
int CONFIG_Init(); int CONFIG_Init();
// I am not sure if anything below will survive for long... // I am not sure if anything below will survive for long...
@ -60,7 +56,7 @@ int GetAutomapZoom(int gZoom);
void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double scale, PalEntry color = 0xffffffff); void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double scale, PalEntry color = 0xffffffff);
void updatePauseStatus(); void updatePauseStatus();
void DeferedStartGame(MapRecord* map, int skill); void DeferedStartGame(MapRecord* map, int skill, bool nostopsound = false);
void ChangeLevel(MapRecord* map, int skill); void ChangeLevel(MapRecord* map, int skill);
void CompleteLevel(MapRecord* map); void CompleteLevel(MapRecord* map);

View file

@ -40,7 +40,9 @@ enum gameaction_t : int
ga_savegame, // save the game ga_savegame, // save the game
ga_autosave, // autosave the game (for triggering a save from within the game.) ga_autosave, // autosave the game (for triggering a save from within the game.)
ga_completed, // Level was exited. ga_completed, // Level was exited.
ga_nextlevel // Actually start the next level. ga_nextlevel, // Actually start the next level.
ga_loadgamehidecon,
ga_newgamenostopsound, // start a new game
}; };
extern gamestate_t gamestate; extern gamestate_t gamestate;
extern gameaction_t gameaction; extern gameaction_t gameaction;

View file

@ -33,21 +33,6 @@ struct FSavegameInfo
int currentsavever; int currentsavever;
}; };
struct FSaveGameNode
{
FString SaveTitle;
FString Filename;
bool bOldVersion = false;
bool bMissingWads = false;
bool bNoDelete = false;
bool bIsExt = false;
bool isValid() const
{
return Filename.IsNotEmpty() && !bOldVersion && !bMissingWads;
}
};
struct ReservedSpace struct ReservedSpace
{ {
int top; int top;
@ -71,7 +56,6 @@ struct GameInterface
virtual void FreeGameData() {} virtual void FreeGameData() {}
virtual void PlayHudSound() {} virtual void PlayHudSound() {}
virtual GameStats getStats() { return {}; } virtual GameStats getStats() { return {}; }
virtual void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) {}
virtual void MainMenuOpened() {} virtual void MainMenuOpened() {}
virtual void MenuOpened() {} virtual void MenuOpened() {}
virtual void MenuClosed() {} virtual void MenuClosed() {}
@ -80,12 +64,9 @@ struct GameInterface
virtual void CustomMenuSelection(int menu, int item) {} virtual void CustomMenuSelection(int menu, int item) {}
virtual bool StartGame(FNewGameStartup& gs) { return false; } virtual bool StartGame(FNewGameStartup& gs) { return false; }
virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; } virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
virtual bool DrawSpecialScreen(const DVector2 &origin, int tilenum) { return false; }
virtual void DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool withbg = true);
virtual double SmallFontScale() { return 1; } virtual double SmallFontScale() { return 1; }
virtual void DrawMenuCaption(const DVector2& origin, const char* text) {} virtual bool SaveGame() { return true; }
virtual bool SaveGame(FSaveGameNode*) { return true; } virtual bool LoadGame() { return true; }
virtual bool LoadGame(FSaveGameNode*) { return true; }
virtual void SerializeGameState(FSerializer& arc) {} virtual void SerializeGameState(FSerializer& arc) {}
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {} virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
virtual void QuitToTitle() {} virtual void QuitToTitle() {}

52
source/core/gi.cpp Normal file
View file

@ -0,0 +1,52 @@
/*
** gi.cpp
** Holds same game-dependant info
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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 <stdlib.h>
#include "gi.h"
#include "sc_man.h"
#include "filesystem.h"
#include "v_video.h"
#include "vm.h"
#include "c_cvars.h"
gameinfo_t gameinfo;
EXTERN_CVAR(Float, turbo)
DEFINE_GLOBAL(gameinfo)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gametype)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mBackButton)
DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, mSliderColor)

55
source/core/gi.h Normal file
View file

@ -0,0 +1,55 @@
/*
** gi.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#ifndef __GI_H__
#define __GI_H__
#include "basics.h"
#include "zstring.h"
#include "name.h"
struct gameinfo_t
{
int gametype;
FName mSliderColor;
FString mBackButton;
};
extern gameinfo_t gameinfo;
bool CheckGame(const char *string, bool chexisdoom);
#endif //__GI_H__

View file

@ -376,6 +376,9 @@ void InitFileSystem(TArray<GrpEntry>& groups)
LumpFilterInfo lfi; LumpFilterInfo lfi;
lfi.dotFilter = LumpFilter; lfi.dotFilter = LumpFilter;
if (g_gameType & (GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL)) lfi.gameTypeFilter.Push("DukeEngine");
lfi.postprocessFunc = [&]() lfi.postprocessFunc = [&]()
{ {
DeleteStuff(fileSystem, todelete, groups.Size()); DeleteStuff(fileSystem, todelete, groups.Size());

View file

@ -67,7 +67,7 @@
#include "d_net.h" #include "d_net.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "c_console.h" #include "c_console.h"
#include "menu.h" #include "razemenu.h"
#include "i_system.h" #include "i_system.h"
#include "raze_sound.h" #include "raze_sound.h"
#include "raze_music.h" #include "raze_music.h"
@ -76,7 +76,6 @@
#include "screenjob.h" #include "screenjob.h"
#include "mmulti.h" #include "mmulti.h"
#include "c_console.h" #include "c_console.h"
#include "menu.h"
#include "uiinput.h" #include "uiinput.h"
#include "v_video.h" #include "v_video.h"
#include "glbackend/glbackend.h" #include "glbackend/glbackend.h"
@ -86,6 +85,8 @@
#include "mapinfo.h" #include "mapinfo.h"
#include "automap.h" #include "automap.h"
#include "statusbar.h" #include "statusbar.h"
#include "gamestruct.h"
#include "savegamehelp.h"
CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, r_ticstability, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, r_ticstability, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -103,7 +104,7 @@ int entertic;
int oldentertics; int oldentertics;
int gametic; int gametic;
extern FString BackupSaveGame; FString BackupSaveGame;
void DoLoadGame(const char* name); void DoLoadGame(const char* name);
@ -146,7 +147,9 @@ static void GameTicker()
C_ClearMessages(); C_ClearMessages();
if (BackupSaveGame.IsNotEmpty() && cl_resumesavegame) if (BackupSaveGame.IsNotEmpty() && cl_resumesavegame)
{ {
#if 0
DoLoadGame(BackupSaveGame); DoLoadGame(BackupSaveGame);
#endif
} }
else else
{ {
@ -184,8 +187,9 @@ static void GameTicker()
break; break;
case ga_newgame: case ga_newgame:
newGameStarted = true;
FX_StopAllSounds(); FX_StopAllSounds();
case ga_newgamenostopsound:
newGameStarted = true;
FX_SetReverb(0); FX_SetReverb(0);
gi->FreeLevelData(); gi->FreeLevelData();
C_ClearMessages(); C_ClearMessages();
@ -206,7 +210,7 @@ static void GameTicker()
case ga_mainmenunostopsound: case ga_mainmenunostopsound:
gi->FreeLevelData(); gi->FreeLevelData();
gamestate = GS_MENUSCREEN; gamestate = GS_MENUSCREEN;
M_StartControlPanel(false); M_StartControlPanel(ga == ga_mainmenu);
M_SetMenu(NAME_Mainmenu); M_SetMenu(NAME_Mainmenu);
break; break;
@ -339,12 +343,14 @@ void Display()
} }
screen->FrameTime = I_msTimeFS(); screen->FrameTime = I_msTimeFS();
tileUpdateAnimations();
screen->BeginFrame(); screen->BeginFrame();
twodpsp.Clear(); twodpsp.Clear();
twodpsp.SetSize(screen->GetWidth(), screen->GetHeight()); twodpsp.SetSize(screen->GetWidth(), screen->GetHeight());
twodpsp.ClearClipRect(); twodpsp.ClearClipRect();
twod->Clear(); twod->Clear();
twod->SetSize(screen->GetWidth(), screen->GetHeight()); //twod->SetSize(screen->GetWidth(), screen->GetHeight());
twod->Begin(screen->GetWidth(), screen->GetHeight());
twod->ClearClipRect(); twod->ClearClipRect();
switch (gamestate) switch (gamestate)
{ {
@ -643,6 +649,7 @@ void MainLoop ()
Printf (PRINT_BOLD, "\n%s\n", error.GetMessage()); Printf (PRINT_BOLD, "\n%s\n", error.GetMessage());
} }
gi->ErrorCleanup(); gi->ErrorCleanup();
M_ClearMenus();
C_FullConsole(); C_FullConsole();
gameaction = ga_nothing; gameaction = ga_nothing;
} }
@ -651,6 +658,8 @@ void MainLoop ()
error.MaybePrintMessage(); error.MaybePrintMessage();
Printf("%s", error.stacktrace.GetChars()); Printf("%s", error.stacktrace.GetChars());
gi->ErrorCleanup(); gi->ErrorCleanup();
twod->SetOffset(DVector2(0, 0));
M_ClearMenus();
C_FullConsole(); C_FullConsole();
} }
} }

View file

@ -38,6 +38,12 @@
#include "filesystem.h" #include "filesystem.h"
#include "printf.h" #include "printf.h"
FString gSkillNames[MAXSKILLS];
FString gVolumeNames[MAXVOLUMES];
FString gVolumeSubtitles[MAXVOLUMES];
int32_t gVolumeFlags[MAXVOLUMES];
int gDefaultVolume = 0, gDefaultSkill = 1;
MapRecord mapList[512]; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts. MapRecord mapList[512]; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts.
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.) MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
MapRecord* lastLevel; // Same here, for the last level. MapRecord* lastLevel; // Same here, for the last level.

View file

@ -7,6 +7,27 @@
#undef GetMessage // Windows strikes... #undef GetMessage // Windows strikes...
#endif #endif
enum EMax
{
MAXSKILLS = 7,
MAXVOLUMES = 7,
MAXMENUGAMEPLAYENTRIES = 7,
};
enum EVolFlags
{
EF_HIDEFROMSP = 1,
};
// These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames[MAXSKILLS];
extern FString gVolumeNames[MAXVOLUMES];
extern FString gVolumeSubtitles[MAXVOLUMES];
extern int32_t gVolumeFlags[MAXVOLUMES];
extern int gDefaultVolume, gDefaultSkill;
// Localization capable replacement of the game specific solutions. // Localization capable replacement of the game specific solutions.
inline void MakeStringLocalizable(FString &quote) inline void MakeStringLocalizable(FString &quote)

View file

@ -1,182 +0,0 @@
/*
** 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 "gamecontrol.h"
#include "build.h"
#include "zstring.h"
//=============================================================================
//
// Fullscreen image drawer (move to its own source file!)
//
//=============================================================================
void ImageScreen::Drawer()
{
if (mDesc == nullptr)
{
// don't let bogus definitions crash this.
}
else if (mDesc->type == 0)
{
auto tileindexp = NameToTileIndex.CheckKey(FName(mDesc->text, true));
int tileindex = 0;
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.
{
DrawTexture(twod, tileGetTexture(tileindex), origin.X, origin.Y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
}
}
else if (mDesc->type > 0)
{
gi->DrawCenteredTextScreen(origin, mDesc->text, mDesc->type);
}
// QAVs are handled in the Blood frontend. Maybe they should be moved out? Stuff for later, but this is a feature where it is feasible.
}
ImageScreen* DImageScrollerMenu::newImageScreen(FImageScrollerDescriptor::ScrollerItem* desc)
{
return new ImageScreen(desc);
}
void DImageScrollerMenu::Init(DMenu* parent, FImageScrollerDescriptor* desc)
{
mParentMenu = parent;
index = 0;
mDesc = desc;
canAnimate = !!(mDesc->mFlags & LMF_Animate);
mCurrent = newImageScreen(&mDesc->mItems[0]);
mCurrent->canAnimate = canAnimate;
isAnimated = true;
}
bool DImageScrollerMenu::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.
pageTransition.previous = nullptr;
if (pageTransition.current) 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 = newImageScreen(&mDesc->mItems[index]);
next->canAnimate = canAnimate;
if (!pageTransition.StartTransition(mCurrent, next, MA_Return))
{
delete mCurrent;
}
mCurrent = next;
gi->MenuSound(ChooseSound);
}
return true;
case MKEY_Right:
case MKEY_Enter:
if (pageTransition.previous == nullptr)
{
int oldindex = index;
if (++index >= (int)mDesc->mItems.Size()) index = 0;
auto next = newImageScreen(&mDesc->mItems[index]);
next->canAnimate = canAnimate;
if (!pageTransition.StartTransition(mCurrent, next, MA_Advance))
{
delete mCurrent;
}
mCurrent = next;
gi->MenuSound(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.
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, false);
}
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

@ -1,434 +0,0 @@
/*
** joystickmenu.cpp
** The joystick configuration menus
**
**---------------------------------------------------------------------------
** Copyright 2010 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 <float.h>
#include "menu.h"
#include "c_dispatch.h"
#include "filesystem.h"
#include "sc_man.h"
#include "v_font.h"
#include "c_bind.h"
#include "d_event.h"
#include "d_gui.h"
#include "m_joy.h"
#include "v_video.h"
#define NO_IMP
#include "optionmenuitems.h"
static TArray<IJoystickConfig *> Joysticks;
IJoystickConfig *SELECTED_JOYSTICK;
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy);
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoySensitivity : public FOptionMenuSliderBase
{
public:
FOptionMenuSliderJoySensitivity(const char *label, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
}
double GetSliderValue()
{
return SELECTED_JOYSTICK->GetSensitivity();
}
void SetSliderValue(double val)
{
SELECTED_JOYSTICK->SetSensitivity(float(val));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoyScale : public FOptionMenuSliderBase
{
int mAxis;
int mNeg;
public:
FOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mAxis = axis;
mNeg = 1;
}
double GetSliderValue()
{
double d = SELECTED_JOYSTICK->GetAxisScale(mAxis);
mNeg = d < 0? -1:1;
return d;
}
void SetSliderValue(double val)
{
SELECTED_JOYSTICK->SetAxisScale(mAxis, float(val * mNeg));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuSliderJoyDeadZone : public FOptionMenuSliderBase
{
int mAxis;
int mNeg;
public:
FOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval)
: FOptionMenuSliderBase(label, min, max, step, showval)
{
mAxis = axis;
mNeg = 1;
}
double GetSliderValue()
{
double d = SELECTED_JOYSTICK->GetAxisDeadZone(mAxis);
mNeg = d < 0? -1:1;
return d;
}
void SetSliderValue(double val)
{
SELECTED_JOYSTICK->SetAxisDeadZone(mAxis, float(val * mNeg));
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemJoyMap : public FOptionMenuItemOptionBase
{
int mAxis;
public:
FOptionMenuItemJoyMap(const char *label, int axis, const char *values, int center)
: FOptionMenuItemOptionBase(label, "none", values, NULL, center)
{
mAxis = axis;
}
int GetSelection()
{
double f = SELECTED_JOYSTICK->GetAxisMap(mAxis);
FOptionValues **opt = OptionValues.CheckKey(mValues);
if (opt != NULL && *opt != NULL)
{
// Map from joystick axis to menu selection.
for(unsigned i = 0; i < (*opt)->mValues.Size(); i++)
{
if (fabs(f - (*opt)->mValues[i].Value) < FLT_EPSILON)
{
return i;
}
}
}
return -1;
}
void SetSelection(int selection)
{
FOptionValues **opt = OptionValues.CheckKey(mValues);
// Map from menu selection to joystick axis.
if (opt == NULL || *opt == NULL || (unsigned)selection >= (*opt)->mValues.Size())
{
selection = JOYAXIS_None;
}
else
{
selection = (int)(*opt)->mValues[selection].Value;
}
SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)selection);
}
};
//=============================================================================
//
//
//
//=============================================================================
class FOptionMenuItemInverter : public FOptionMenuItemOptionBase
{
int mAxis;
public:
FOptionMenuItemInverter(const char *label, int axis, int center)
: FOptionMenuItemOptionBase(label, "none", "YesNo", NULL, center)
{
mAxis = axis;
}
int GetSelection()
{
float f = SELECTED_JOYSTICK->GetAxisScale(mAxis);
return f > 0? 0:1;
}
void SetSelection(int Selection)
{
float f = fabs(SELECTED_JOYSTICK->GetAxisScale(mAxis));
if (Selection) f*=-1;
SELECTED_JOYSTICK->SetAxisScale(mAxis, f);
}
};
class DJoystickConfigMenu : public DOptionMenu
{
};
//=============================================================================
//
// Executes a CCMD, action is a CCMD name
//
//=============================================================================
class FOptionMenuItemJoyConfigMenu : public FOptionMenuItemSubmenu
{
IJoystickConfig *mJoy;
public:
FOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy)
: FOptionMenuItemSubmenu(label, "JoystickConfigMenu")
{
mJoy = joy;
}
bool Activate(FName caller) override
{
UpdateJoystickConfigMenu(mJoy);
return FOptionMenuItemSubmenu::Activate(caller);
}
};
/*=======================================
*
* Joystick Menu
*
*=======================================*/
FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy)
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickConfigMenu);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
FOptionMenuItem *it;
for(unsigned i=0;i<opt->mItems.Size();i++)
{
delete opt->mItems[i];
opt->mItems.Clear();
}
if (joy == NULL)
{
opt->mTitle = "$JOYMNU_TITLE";
it = new FOptionMenuItemStaticText("$JOYMNU_INVALID");
opt->mItems.Push(it);
}
else
{
opt->mTitle.Format("%s", joy->GetName().GetChars());
SELECTED_JOYSTICK = joy;
it = new FOptionMenuSliderJoySensitivity("$JOYMNU_OVRSENS", 0, 2, 0.1, 3);
opt->mItems.Push(it);
it = new FOptionMenuItemStaticText(" ");
opt->mItems.Push(it);
if (joy->GetNumAxes() > 0)
{
it = new FOptionMenuItemStaticText("$JOYMNU_AXIS");
opt->mItems.Push(it);
for (int i = 0; i < joy->GetNumAxes(); ++i)
{
it = new FOptionMenuItemStaticText(" ");
opt->mItems.Push(it);
it = new FOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false);
opt->mItems.Push(it);
it = new FOptionMenuSliderJoyScale("$JOYMNU_OVRSENS", i, 0, 4, 0.1, 3);
opt->mItems.Push(it);
it = new FOptionMenuItemInverter("$JOYMNU_INVERT", i, false);
opt->mItems.Push(it);
it = new FOptionMenuSliderJoyDeadZone("$JOYMNU_DEADZONE", i, 0, 0.9, 0.05, 3);
opt->mItems.Push(it);
}
}
else
{
it = new FOptionMenuItemStaticText("$JOYMNU_NOAXES");
opt->mItems.Push(it);
}
}
opt->mScrollPos = 0;
opt->mSelectedItem = -1;
opt->mIndent = 0;
opt->CalcIndent();
return opt;
}
return NULL;
}
void UpdateJoystickMenu(IJoystickConfig *selected)
{
FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions);
if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc;
FOptionMenuItem *it;
for(unsigned i=0;i<opt->mItems.Size();i++)
{
delete opt->mItems[i];
}
opt->mItems.Clear();
int i;
int itemnum = -1;
I_GetJoysticks(Joysticks);
if ((unsigned)itemnum >= Joysticks.Size())
{
itemnum = Joysticks.Size() - 1;
}
if (selected != NULL)
{
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == selected)
{
itemnum = i;
break;
}
}
}
// Todo: Block joystick for changing this one.
it = new FOptionMenuItemOption("$JOYMNU_ENABLE", "use_joystick", "YesNo", NULL, false);
opt->mItems.Push(it);
#if 0//def _WIN32
it = new FOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false);
opt->mItems.Push(it);
it = new FOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false);
opt->mItems.Push(it);
it = new FOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false);
opt->mItems.Push(it);
#endif
it = new FOptionMenuItemStaticText(" ");
opt->mItems.Push(it);
if (Joysticks.Size() == 0)
{
it = new FOptionMenuItemStaticText("$JOYMNU_NOCON");
opt->mItems.Push(it);
if (!use_joystick)
{
it = new FOptionMenuItemStaticText("$JOYMNU_DISABLED1");
opt->mItems.Push(it);
it = new FOptionMenuItemStaticText("$JOYMNU_DISABLED2");
opt->mItems.Push(it);
}
}
else
{
it = new FOptionMenuItemStaticText("$JOYMNU_CONFIG");
opt->mItems.Push(it);
for (int i = 0; i < (int)Joysticks.Size(); ++i)
{
it = new FOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]);
opt->mItems.Push(it);
if (i == itemnum) opt->mSelectedItem = opt->mItems.Size();
}
}
if (opt->mSelectedItem >= (int)opt->mItems.Size())
{
opt->mSelectedItem = opt->mItems.Size() - 1;
}
opt->CalcIndent();
// If the joystick config menu is open, close it if the device it's
// open for is gone.
for (i = 0; (unsigned)i < Joysticks.Size(); ++i)
{
if (Joysticks[i] == SELECTED_JOYSTICK)
{
break;
}
}
if (i == (int)Joysticks.Size())
{
SELECTED_JOYSTICK = NULL;
if (CurrentMenu != NULL && dynamic_cast<DJoystickConfigMenu*>(CurrentMenu))
{
CurrentMenu->Close();
}
}
}
}
static TMenuClassDescriptor<DJoystickConfigMenu> _im("JoystickConfigMenu");
void RegisterJoystickMenus()
{
menuClasses.Push(&_im);
}

View file

@ -1,634 +0,0 @@
/*
** listmenu.cpp
** A simple menu consisting of a list of items
**
**---------------------------------------------------------------------------
** Copyright 2010 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 "gamecontrol.h"
#include "build.h"
#include "v_video.h"
//=============================================================================
//
//
//
//=============================================================================
DListMenu::DListMenu(DMenu *parent, FListMenuDescriptor *desc)
: DMenu(parent)
{
mDesc = NULL;
if (desc != NULL) Init(parent, desc);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Init(DMenu *parent, FListMenuDescriptor *desc)
{
mParentMenu = parent;
mDesc = desc;
canAnimate = !!(mDesc->mFlags & LMF_Animate);
if (mDesc->mScriptId >= 0) scriptID = mDesc->mScriptId;
#if 0
if (desc->mCenter)
{
int center = 160;
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int xpos = mDesc->mItems[i]->GetX();
int width = mDesc->mItems[i]->GetWidth();
int curx = mDesc->mSelectOfsX;
if (width > 0 && mDesc->mItems[i]->Selectable())
{
int left = 160 - (width - curx) / 2 - curx;
if (left < center) center = left;
}
}
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
int width = mDesc->mItems[i]->GetWidth();
if (width > 0)
{
mDesc->mItems[i]->SetX(center);
}
}
}
#endif
}
//=============================================================================
//
//
//
//=============================================================================
FListMenuItem *DListMenu::GetItem(FName name)
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
FName nm = mDesc->mItems[i]->GetAction(NULL);
if (nm == name) return mDesc->mItems[i];
}
return NULL;
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_KeyDown)
{
int ch = tolower (ev->data1);
for(unsigned i = mDesc->mSelectedItem + 1; i < mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
SelectionChanged();
M_MenuSound(CursorSound);
return true;
}
}
for(int i = 0; i < mDesc->mSelectedItem; i++)
{
if (mDesc->mItems[i]->CheckHotkey(ch))
{
mDesc->mSelectedItem = i;
SelectionChanged();
M_MenuSound(CursorSound);
return true;
}
}
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MenuEvent (int mkey, bool fromcontroller)
{
int startedAt = mDesc->mSelectedItem;
switch (mkey)
{
case MKEY_Up:
do
{
if (--mDesc->mSelectedItem < 0) mDesc->mSelectedItem = mDesc->mItems.Size()-1;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
SelectionChanged();
M_MenuSound(CursorSound);
return true;
case MKEY_Down:
do
{
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) mDesc->mSelectedItem = 0;
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
SelectionChanged();
M_MenuSound(CursorSound);
return true;
case MKEY_Enter:
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate(mDesc->mMenuName))
{
M_MenuSound(AdvanceSound);
}
return true;
default:
return Super::MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DListMenu::MouseEvent(int type, int xx, int yy)
{
int sel = -1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
//x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
//y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
int width43 = (screen->GetHeight() * 4 / 3);
int x = (xx - (screen->GetWidth() - width43) / 2) * 320 / width43;
int y = yy * 200 / screen->GetHeight();
if (mFocusControl != NULL)
{
mFocusControl->MouseEvent(type, x, y);
return true;
}
else
{
if ((mDesc->mWLeft <= 0 || x > mDesc->mWLeft) &&
(mDesc->mWRight <= 0 || x < mDesc->mWRight))
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
if (mDesc->mItems[i]->CheckCoordinate(x, y))
{
if ((int)i != mDesc->mSelectedItem)
{
// no sound. This is too noisy.
}
mDesc->mSelectedItem = i;
SelectionChanged();
mDesc->mItems[i]->MouseEvent(type, x, y);
return true;
}
}
}
}
mDesc->mSelectedItem = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::Ticker ()
{
Super::Ticker();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DListMenu::PreDraw()
{
if (mDesc->mCaption.IsNotEmpty())
{
gi->DrawMenuCaption(origin, GStrings.localize(mDesc->mCaption));
}
}
void DListMenu::Drawer ()
{
PreDraw();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Drawer(this, origin, mDesc->mSelectedItem == (int)i);
}
if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size())
mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector);
PostDraw();
Super::Drawer();
}
//=============================================================================
//
// base class for menu items
//
//=============================================================================
FListMenuItem::~FListMenuItem()
{
}
bool FListMenuItem::CheckCoordinate(int x, int y)
{
return false;
}
void FListMenuItem::Ticker()
{
}
void FListMenuItem::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
}
bool FListMenuItem::Selectable()
{
return false;
}
void FListMenuItem::DrawSelector(int xofs, int yofs, FGameTexture *tex)
{
if (tex)
{
DrawTexture (twod, tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE);
}
}
bool FListMenuItem::Activate(FName)
{
return false; // cannot be activated
}
FName FListMenuItem::GetAction(int *pparam)
{
return mAction;
}
bool FListMenuItem::SetString(int i, const char *s)
{
return false;
}
bool FListMenuItem::GetString(int i, char *s, int len)
{
return false;
}
bool FListMenuItem::SetValue(int i, int value)
{
return false;
}
bool FListMenuItem::GetValue(int i, int *pvalue)
{
return false;
}
void FListMenuItem::Enable(bool on)
{
mEnabled = on;
}
bool FListMenuItem::MenuEvent(int mkey, bool fromcontroller)
{
return false;
}
bool FListMenuItem::MouseEvent(int type, int x, int y)
{
return false;
}
bool FListMenuItem::CheckHotkey(int c)
{
return false;
}
int FListMenuItem::GetWidth()
{
return 0;
}
//=============================================================================
//
// static patch
//
//=============================================================================
FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FGameTexture *patch, bool centered)
: FListMenuItem(x, y)
{
mTexture = patch;
mCentered = centered;
}
void FListMenuItemStaticPatch::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
if (!mTexture)
{
return;
}
int x = mXpos;
FGameTexture *tex = mTexture;
if (mYpos >= 0)
{
if (mCentered) x -= tex->GetDisplayWidth()/2;
DrawTexture (twod, tex, x, mYpos, DTA_Clean, true, TAG_DONE);
}
else
{
int x = (mXpos - 160) * CleanXfac + (screen->GetWidth()>>1);
if (mCentered) x -= (tex->GetDisplayWidth()*CleanXfac)/2;
DrawTexture (twod, tex, x, -mYpos*CleanYfac, DTA_CleanNoMove, true, TAG_DONE);
}
}
//=============================================================================
//
// static text
//
//=============================================================================
FListMenuItemStaticText::FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered)
: FListMenuItem(x, y)
{
mText = text;
mFont = font;
mColor = color;
mCentered = centered;
}
void FListMenuItemStaticText::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
const char *text = mText;
if (text != NULL)
{
if (mYpos >= 0)
{
int x = mXpos;
if (mCentered) x -= mFont->StringWidth(text)/2;
DrawText(twod, mFont, mColor, x, mYpos, text, DTA_Clean, true, TAG_DONE);
}
else
{
int x = (mXpos - 160) * CleanXfac + (screen->GetWidth()>>1);
if (mCentered) x -= (mFont->StringWidth(text)*CleanXfac)/2;
DrawText (twod, mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true, TAG_DONE);
}
}
}
FListMenuItemStaticText::~FListMenuItemStaticText()
{
if (mText != NULL) delete [] mText;
}
//=============================================================================
//
// native static text item
//
//=============================================================================
FListMenuItemNativeStaticText::FListMenuItemNativeStaticText(int x, int y, const FString& text, int fontnum, int palnum, bool centered)
: FListMenuItem(x, y)
{
mText = text;
mFontnum = fontnum;
mPalnum = palnum;
mCentered = centered;
}
void FListMenuItemNativeStaticText::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
const char* text = mText;
if (mText.Len() && !mHidden)
{
gi->DrawNativeMenuText(mFontnum, mPalnum, origin.X + mXpos, origin.Y + mYpos, 1.f, GStrings.localize(text), menu->Descriptor()->mFlags);
}
}
//=============================================================================
//
// base class for selectable items
//
//=============================================================================
FListMenuItemSelectable::FListMenuItemSelectable(int x, int y, int height, FName action, int param)
: FListMenuItem(x, y, action)
{
mHeight = height;
mParam = param;
mHotkey = 0;
}
bool FListMenuItemSelectable::CheckCoordinate(int x, int y)
{
return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here
}
bool FListMenuItemSelectable::Selectable()
{
return mEnabled && !mHidden;
}
bool FListMenuItemSelectable::Activate(FName caller)
{
return M_SetMenu(mAction, mParam, caller);
}
FName FListMenuItemSelectable::GetAction(int *pparam)
{
if (pparam != NULL) *pparam = mParam;
return mAction;
}
bool FListMenuItemSelectable::CheckHotkey(int c)
{
return c == tolower(mHotkey);
}
bool FListMenuItemSelectable::MouseEvent(int type, int x, int y)
{
if (type == DMenu::MOUSE_Release)
{
if (NULL != CurrentMenu && CurrentMenu->MenuEvent(MKEY_Enter, true))
{
return true;
}
}
return false;
}
//=============================================================================
//
// text item
//
//=============================================================================
FListMenuItemText::FListMenuItemText(int x, int y, int height, int hotkey, const FString &text, FFont *font, EColorRange color, EColorRange color2, FName child, int param)
: FListMenuItemSelectable(x, y, height, child, param)
{
mText = text;
mFont = font;
mColor = color;
mColorSelected = color2;
mFont = NewSmallFont;
mHotkey = hotkey;
}
FListMenuItemText::~FListMenuItemText()
{
}
void FListMenuItemText::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
const char *text = GStrings(mText);
if (mText.Len())
{
DrawText(twod, mFont, selected ? mColorSelected : mColor, mXpos - mFont->StringWidth(text)/2, mYpos, text, DTA_Clean, true, TAG_DONE);
}
}
int FListMenuItemText::GetWidth()
{
const char *text = mText;
if (mText.Len())
{
return mFont->StringWidth(GStrings.localize(text));
}
return 1;
}
//=============================================================================
//
// native text item
//
//=============================================================================
FListMenuItemNativeText::FListMenuItemNativeText(int x, int y, int height, int hotkey, const FString& text, int fontnum, int palnum, float fontscale, FName child, int param)
: FListMenuItemSelectable(x, y, height, child, param)
{
mText = text;
mFontnum = NIT_BigFont;
mPalnum = NIT_ActiveColor;
mFontscale = fontscale;
mHotkey = hotkey;
}
FListMenuItemNativeText::~FListMenuItemNativeText()
{
}
void FListMenuItemNativeText::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
const char* text = mText;
if (mText.Len() && !mHidden)
{
auto state = selected ? NIT_SelectedState : mEnabled ? NIT_ActiveState : NIT_InactiveState;
gi->DrawNativeMenuText(mFontnum, state, origin.X + mXpos, origin.Y + mYpos, 1.f, GStrings.localize(text), menu->Descriptor()->mFlags);
}
}
int FListMenuItemNativeText::GetWidth()
{
return 1;
}
//=============================================================================
//
// patch item
//
//=============================================================================
FListMenuItemPatch::FListMenuItemPatch(int x, int y, int height, int hotkey, FGameTexture *patch, FName child, int param)
: FListMenuItemSelectable(x, y, height, child, param)
{
mHotkey = hotkey;
mTexture = patch;
}
void FListMenuItemPatch::Drawer(DListMenu* menu, const DVector2& origin, bool selected)
{
DrawTexture (twod, mTexture, mXpos, mYpos, DTA_Clean, true, TAG_DONE);
}
int FListMenuItemPatch::GetWidth()
{
return mTexture
? mTexture->GetDisplayWidth()
: 0;
}

View file

@ -4,7 +4,7 @@
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit ** Copyright 2001-2010 Randy Heit
** Copyright 2010 Christoph Oelckers ** Copyright 2010-2020 Christoph Oelckers
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
@ -33,612 +33,124 @@
** **
*/ */
#include "menu.h" #include "razemenu.h"
#include "version.h" #include "version.h"
#include "m_png.h" #include "m_png.h"
#include "filesystem.h" #include "filesystem.h"
#include "v_text.h" #include "v_text.h"
#include "d_event.h"
#include "gstrings.h" #include "gstrings.h"
#include "d_gui.h" #include "serializer.h"
#include "v_draw.h" #include "vm.h"
#include "files.h" #include "i_system.h"
#include "resourcefile.h"
#include "savegamehelp.h"
#include "i_specialpaths.h"
#include "findfile.h"
#include "v_video.h" #include "v_video.h"
#include "findfile.h"
#include "v_draw.h"
#include "savegamehelp.h"
//=============================================================================
//
// M_ReadSaveStrings
//
// Find savegames and read their titles
//
//=============================================================================
void FSavegameManager::ReadSaveStrings()
class DLoadSaveMenu : public DListMenu
{ {
using Super = DListMenu; if (SaveGames.Size() == 0)
protected:
int Selected = 0;
int TopItem = 0;
int savepicLeft;
int savepicTop;
int savepicWidth;
int savepicHeight;
int rowHeight;
int listboxLeft;
int listboxTop;
int listboxWidth;
int listboxRows;
int listboxHeight;
int listboxRight;
int listboxBottom;
int commentLeft;
int commentTop;
int commentWidth;
int commentHeight;
int commentRight;
int commentBottom;
int commentRows;
double FontScale;
DTextEnterMenu *mInput = nullptr;
TArray<FBrokenLines> BrokenSaveComment;
bool mEntering = false;
//=============================================================================
//
// End of static savegame maintenance code
//
//=============================================================================
DLoadSaveMenu()
{ {
savegameManager.ReadSaveStrings(); void *filefirst;
} findstate_t c_file;
FString filter;
void Init(DMenu* parent, FListMenuDescriptor* desc) override LastSaved = LastAccessed = -1;
{ quickSaveSlot = nullptr;
Super::Init(parent, desc); filter = G_BuildSaveName("*");
int Width43 = screen->GetHeight() * 4 / 3; filefirst = I_FindFirst(filter.GetChars(), &c_file);
int Left43 = (screen->GetWidth() - Width43) / 2; if (filefirst != ((void *)(-1)))
float wScale = Width43 / 640.;
savepicLeft = Left43 + int(20 * wScale);
savepicTop = mDesc->mYpos * screen->GetHeight() / 200 ;
savepicWidth = int(240 * wScale);
savepicHeight = int(180 * wScale);
FontScale = std::max(screen->GetHeight() / 480, 1);
rowHeight = std::max(int((NewConsoleFont->GetHeight() + 1) * FontScale), 1);
listboxLeft = savepicLeft + savepicWidth + int(20 * wScale);
listboxTop = savepicTop;
listboxWidth = Width43 + Left43 - listboxLeft - int(30 * wScale);
int listboxHeight1 = screen->GetHeight() - listboxTop - int(20*wScale);
listboxRows = (listboxHeight1 - 1) / rowHeight;
listboxHeight = listboxRows * rowHeight + 1;
listboxRight = listboxLeft + listboxWidth;
listboxBottom = listboxTop + listboxHeight;
commentLeft = savepicLeft;
commentTop = savepicTop + savepicHeight + int(16 * wScale);
commentWidth = savepicWidth;
commentHeight = listboxHeight - savepicHeight - (16 * wScale);
commentRight = commentLeft + commentWidth;
commentBottom = commentTop + commentHeight;
commentRows = commentHeight / rowHeight;
UpdateSaveComment();
}
//=============================================================================
//
//
//
//=============================================================================
void Drawer() override
{
Super::Drawer();
int i;
unsigned j;
bool didSeeSelected = false;
// Draw picture area
/*
if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame)
{ {
return; do
}
*/
PalEntry frameColor(255, 80, 80, 80); // todo: pick a proper color per game.
PalEntry fillColor(160, 0, 0, 0);
DrawFrame(twod, frameColor, savepicLeft, savepicTop, savepicWidth, savepicHeight, -1);
if (!savegameManager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight))
{
twod->AddColorOnlyQuad(savepicLeft, savepicTop, savepicWidth, savepicHeight, fillColor);
if (savegameManager.SavegameCount() > 0)
{ {
if (Selected >= savegameManager.SavegameCount()) Selected = 0; // I_FindName only returns the file's name and not its full path
FString text = (Selected == -1 || !savegameManager.GetSavegame(Selected)->bOldVersion) ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); FString filepath = G_BuildSaveName(I_FindName(&c_file));
int textlen = NewSmallFont->StringWidth(text) * CleanXfac;
DrawText(twod, NewSmallFont, CR_GOLD, savepicLeft + (savepicWidth - textlen) / 2, FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true);
savepicTop + (savepicHeight - rowHeight) / 2, text, DTA_CleanNoMove, true, TAG_DONE); if (savegame != nullptr)
}
}
// Draw comment area
DrawFrame(twod, frameColor, commentLeft, commentTop, commentWidth, commentHeight, -1);
twod->AddColorOnlyQuad(commentLeft, commentTop, commentWidth, commentHeight, fillColor);
int numlinestoprint = std::min(commentRows, (int)BrokenSaveComment.Size());
for (int i = 0; i < numlinestoprint; i++)
{
DrawText(twod, NewConsoleFont, CR_ORANGE, commentLeft / FontScale, (commentTop + rowHeight * i) / FontScale, BrokenSaveComment[i].Text,
DTA_VirtualWidthF, screen->GetWidth() / FontScale, DTA_VirtualHeightF, screen->GetHeight() / FontScale, DTA_KeepRatio, true, TAG_DONE);
}
// Draw file area
DrawFrame(twod, frameColor, listboxLeft, listboxTop, listboxWidth, listboxHeight, -1);
twod->AddColorOnlyQuad(listboxLeft, listboxTop, listboxWidth, listboxHeight, fillColor);
if (savegameManager.SavegameCount() == 0)
{
FString text = GStrings("MNU_NOFILES");
int textlen = int(NewConsoleFont->StringWidth(text) * FontScale);
DrawText(twod, NewConsoleFont, CR_GOLD, (listboxLeft + (listboxWidth - textlen) / 2) / FontScale, (listboxTop + (listboxHeight - rowHeight) / 2) / FontScale, text,
DTA_VirtualWidthF, screen->GetWidth() / FontScale, DTA_VirtualHeightF, screen->GetHeight() / FontScale, DTA_KeepRatio, true, TAG_DONE);
return;
}
j = TopItem;
for (i = 0; i < listboxRows && j < savegameManager.SavegameCount(); i++)
{
int colr;
auto& node = *savegameManager.GetSavegame(j);
if (node.bOldVersion)
{
colr = CR_RED;
}
else if (node.bMissingWads)
{
colr = CR_YELLOW;
}
else if (j == Selected)
{
colr = CR_WHITE;
}
else
{
colr = CR_TAN;
}
//screen->SetClipRect(listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1));
if ((int)j == Selected)
{
twod->AddColorOnlyQuad(listboxLeft, listboxTop + rowHeight * i, listboxWidth, rowHeight, mEntering ? PalEntry(255, 255, 0, 0) : PalEntry(255, 0, 0, 255));
didSeeSelected = true;
if (!mEntering)
{ {
DrawText(twod, NewConsoleFont, colr, (listboxLeft + 1) / FontScale, (listboxTop + rowHeight * i + FontScale) / FontScale, node.SaveTitle, FResourceLump *info = savegame->FindLump("info.json");
DTA_VirtualWidthF, screen->GetWidth() / FontScale, DTA_VirtualHeightF, screen->GetHeight() / FontScale, DTA_KeepRatio, true, TAG_DONE); if (info == nullptr)
}
else
{
FStringf s("%s%c", mInput->GetText(), NewConsoleFont->GetCursor());
int length = int(NewConsoleFont->StringWidth(s) * FontScale);
int displacement = std::min(0, listboxWidth - 2 - length);
DrawText(twod, NewConsoleFont, CR_WHITE, (listboxLeft + 1 + displacement) / FontScale, (listboxTop + rowHeight * i + FontScale) / FontScale, s,
DTA_VirtualWidthF, screen->GetWidth() / FontScale, DTA_VirtualHeightF, screen->GetHeight() / FontScale, DTA_KeepRatio, true, TAG_DONE);
}
}
else
{
DrawText(twod, NewConsoleFont, colr, (listboxLeft + 1) / FontScale, (listboxTop + rowHeight * i + FontScale) / FontScale, node.SaveTitle,
DTA_VirtualWidthF, screen->GetWidth() / FontScale, DTA_VirtualHeightF, screen->GetHeight() / FontScale, DTA_KeepRatio, true, TAG_DONE);
}
//screen->ClearClipRect();
j++;
}
}
void UpdateSaveComment()
{
BrokenSaveComment = V_BreakLines(NewConsoleFont, int(commentWidth / FontScale), savegameManager.SaveCommentString);
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent(int mkey, bool fromcontroller) override
{
auto& manager = savegameManager;
switch (mkey)
{
case MKEY_Up:
if (manager.SavegameCount() > 1)
{
if (Selected == -1) Selected = TopItem;
else
{
if (--Selected < 0) Selected = manager.SavegameCount() - 1;
if (Selected < TopItem) TopItem = Selected;
else if (Selected >= TopItem + listboxRows) TopItem = std::max(0, Selected - listboxRows + 1);
}
manager.UnloadSaveData();
manager.ExtractSaveData(Selected);
UpdateSaveComment();
}
return true;
case MKEY_Down:
if (manager.SavegameCount() > 1)
{
if (Selected == -1) Selected = TopItem;
else
{
if (++Selected >= manager.SavegameCount()) Selected = 0;
if (Selected < TopItem) TopItem = Selected;
else if (Selected >= TopItem + listboxRows) TopItem = std::max(0, Selected - listboxRows + 1);
}
manager.UnloadSaveData();
manager.ExtractSaveData(Selected);
UpdateSaveComment();
}
return true;
case MKEY_PageDown:
if (manager.SavegameCount() > 1)
{
if (TopItem >= manager.SavegameCount() - listboxRows)
{
TopItem = 0;
if (Selected != -1) Selected = 0;
}
else
{
TopItem = std::min(TopItem + listboxRows, int(manager.SavegameCount()) - listboxRows);
if (TopItem > Selected&& Selected != -1) Selected = TopItem;
}
manager.UnloadSaveData();
manager.ExtractSaveData(Selected);
UpdateSaveComment();
}
return true;
case MKEY_PageUp:
if (manager.SavegameCount() > 1)
{
if (TopItem == 0)
{
TopItem = std::max(0, int(manager.SavegameCount()) - listboxRows);
if (Selected != -1) Selected = TopItem;
}
else
{
TopItem = std::max(int(TopItem - listboxRows), 0);
if (Selected >= TopItem + listboxRows) Selected = TopItem;
}
manager.UnloadSaveData();
manager.ExtractSaveData(Selected);
UpdateSaveComment();
}
return true;
case MKEY_Enter:
return false; // This event will be handled by the subclasses
case MKEY_MBYes:
{
if (Selected < manager.SavegameCount())
{
Selected = manager.RemoveSaveSlot(Selected);
UpdateSaveComment();
}
return true;
}
default:
return Super::MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool MouseEvent(int type, int x, int y) override
{
auto& manager = savegameManager;
if (x >= listboxLeft && x < listboxLeft + listboxWidth &&
y >= listboxTop && y < listboxTop + listboxHeight)
{
int lineno = (y - listboxTop) / rowHeight;
if (TopItem + lineno < manager.SavegameCount())
{
Selected = TopItem + lineno;
manager.UnloadSaveData();
manager.ExtractSaveData(Selected);
UpdateSaveComment();
if (type == MOUSE_Release)
{
if (MenuEvent(MKEY_Enter, true))
{ {
return true; // savegame info not found. This is not a savegame so leave it alone.
delete savegame;
continue;
}
auto fr = info->NewReader();
FString title;
int check = G_ValidateSavegame(fr, &title, true);
fr.Close();
delete savegame;
if (check != 0)
{
FSaveGameNode *node = new FSaveGameNode;
node->Filename = filepath;
node->bOldVersion = check == -1;
node->bMissingWads = check == -2;
node->SaveTitle = title;
InsertSaveNode(node);
} }
} }
} } while (I_FindNext (filefirst, &c_file) == 0);
else Selected = -1; I_FindClose (filefirst);
}
else Selected = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool Responder(event_t * ev) override
{
auto& manager = savegameManager;
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_KeyDown)
{
if ((unsigned)Selected < manager.SavegameCount())
{
switch (ev->data1)
{
case GK_F1:
manager.SetFileInfo(Selected);
UpdateSaveComment();
return true;
case GK_DEL:
case '\b':
{
FString EndString;
EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s",
GStrings("MNU_DELETESG"), manager.GetSavegame(Selected)->SaveTitle.GetChars(), GStrings("PRESSYN"));
M_StartMessage(EndString, 0, -1);
}
return true;
}
}
}
else if (ev->subtype == EV_GUI_WheelUp)
{
if (TopItem > 0) TopItem--;
return true;
}
else if (ev->subtype == EV_GUI_WheelDown)
{
if (TopItem < manager.SavegameCount() - listboxRows) TopItem++;
return true;
}
}
return Super::Responder(ev);
}
};
//=============================================================================
//
//
//
//=============================================================================
class DSaveMenu : public DLoadSaveMenu
{
using Super = DLoadSaveMenu;
FString mSaveName;
public:
//=============================================================================
//
//
//
//=============================================================================
DSaveMenu()
{
savegameManager.InsertNewSaveNode();
TopItem = 0;
Selected = savegameManager.ExtractSaveData (-1);
UpdateSaveComment();
}
//=============================================================================
//
//
//
//=============================================================================
void Destroy() override
{
if (savegameManager.RemoveNewSaveNode())
{
Selected--;
}
Super::Destroy();
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent (int mkey, bool fromcontroller) override
{
if (Super::MenuEvent(mkey, fromcontroller))
{
return true;
}
if (Selected == -1)
{
return false;
}
if (mkey == MKEY_Enter)
{
FString SavegameString = (Selected != 0)? savegameManager.GetSavegame(Selected)->SaveTitle : FString();
mInput = new DTextEnterMenu(this, NewConsoleFont, SavegameString, listboxWidth, false, false);
M_ActivateMenu(mInput);
mEntering = true;
}
else if (mkey == MKEY_Input)
{
mEntering = false;
mSaveName = mInput->GetText();
mInput = nullptr;
}
else if (mkey == MKEY_Abort)
{
mEntering = false;
mInput = nullptr;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool MouseEvent(int type, int x, int y) override
{
if (mSaveName.Len() > 0)
{
// Do not process events when saving is in progress to avoid update of the current index,
// i.e. Selected member variable must remain unchanged
return true;
}
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
bool Responder (event_t *ev) override
{
if (ev->subtype == EV_GUI_KeyDown)
{
if (Selected != -1)
{
switch (ev->data1)
{
case GK_DEL:
case '\b':
// cannot delete 'new save game' item
if (Selected == 0) return true;
break;
case 'N':
Selected = TopItem = 0;
savegameManager.UnloadSaveData();
return true;
}
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
void Ticker() override
{
if (mSaveName.Len() > 0)
{
savegameManager.DoSave(Selected, mSaveName);
} }
} }
};
//=============================================================================
//
//
//
//=============================================================================
class DLoadMenu : public DLoadSaveMenu
{
using Super = DLoadSaveMenu;
public:
//=============================================================================
//
//
//
//=============================================================================
DLoadMenu()
{
TopItem = 0;
Selected = savegameManager.ExtractSaveData(-1);
UpdateSaveComment();
}
//=============================================================================
//
//
//
//=============================================================================
bool MenuEvent(int mkey, bool fromcontroller) override
{
if (Super::MenuEvent(mkey, fromcontroller))
{
return true;
}
if (Selected == -1 || savegameManager.SavegameCount() == 0)
{
return false;
}
if (mkey == MKEY_Enter)
{
savegameManager.LoadSavegame(Selected);
return true;
}
return false;
}
};
static TMenuClassDescriptor<DLoadMenu> _lm("LoadMenu");
static TMenuClassDescriptor<DSaveMenu> _sm("SaveMenu");
void RegisterLoadsaveMenus()
{
menuClasses.Push(&_sm);
menuClasses.Push(&_lm);
} }
//=============================================================================
//
//
//
//=============================================================================
void FSavegameManager::PerformLoadGame(const char *f, bool s)
{
G_LoadGame(f);
}
void FSavegameManager::PerformSaveGame(const char *f, const char *s)
{
G_SaveGame(f, s, false, false);
}
FString FSavegameManager::BuildSaveName(const char* fn, int slot)
{
return G_BuildSaveName(FStringf("%s%04d", fn, slot));
}
//=============================================================================
//
//
//
//=============================================================================
FString FSavegameManager::ExtractSaveComment(FSerializer& arc)
{
FString comment, fcomment, ncomment, mtime;
arc("Creation Time", comment)
("Map Label", fcomment)
("Map Name", ncomment)
("Map Time", mtime);
comment.AppendFormat("\n%s - %s\n%s", fcomment.GetChars(), ncomment.GetChars(), mtime.GetChars());
return comment;
}
FSavegameManager savegameManager;
DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager)
{
PARAM_PROLOGUE;
ACTION_RETURN_POINTER(&savegameManager);
}

File diff suppressed because it is too large Load diff

View file

@ -1,844 +0,0 @@
#ifndef __M_MENU_MENU_H__
#define __M_MENU_MENU_H__
#include "v_font.h"
#include "c_cvars.h"
#include "version.h"
#include "textures.h"
#include "zstring.h"
#include "v_draw.h"
#include "menustate.h"
#include "gamestruct.h"
EXTERN_CVAR(Float, snd_menuvolume)
EXTERN_CVAR(Int, m_use_mouse);
enum EMax
{
MAXSKILLS = 7,
MAXVOLUMES = 7,
MAXMENUGAMEPLAYENTRIES = 7,
};
// These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames[MAXSKILLS];
extern FString gVolumeNames[MAXVOLUMES];
extern FString gVolumeSubtitles[MAXVOLUMES];
extern int32_t gVolumeFlags[MAXVOLUMES];
extern int gDefaultVolume, gDefaultSkill;
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;
double start;
int32_t length;
int32_t dir;
bool StartTransition(DMenu* from, DMenu* to, MenuTransitionType animtype);
bool Draw();
};
enum
{
EF_HIDEFROMSP = 1 << 0,
};
enum MenuGameplayEntryFlags
{
MGE_Locked = 1u << 0u,
MGE_Hidden = 1u << 1u,
MGE_UserContent = 1u << 2u,
};
typedef struct MenuGameplayEntry
{
char name[64];
uint8_t flags;
bool isValid() const { return name[0] != '\0'; }
} MenuGameplayEntry;
typedef struct MenuGameplayStemEntry
{
MenuGameplayEntry entry;
MenuGameplayEntry subentries[MAXMENUGAMEPLAYENTRIES];
} MenuGameplayStemEntry;
extern MenuGameplayStemEntry g_MenuGameplayEntries[MAXMENUGAMEPLAYENTRIES];
enum EMenuSounds : int
{
ActivateSound,
CursorSound,
AdvanceSound,
BackSound,
CloseSound,
PageSound,
ChangeSound,
ChooseSound
};
EXTERN_CVAR(Bool, menu_sounds)
struct event_t;
class FGameTexture;
class FFont;
enum EColorRange : int;
class FPlayerClass;
class FKeyBindings;
enum EMenuKey
{
MKEY_Up,
MKEY_Down,
MKEY_Left,
MKEY_Right,
MKEY_PageUp,
MKEY_PageDown,
//----------------- Keys past here do not repeat.
MKEY_Enter,
MKEY_Back, // Back to previous menu
MKEY_Clear, // Clear keybinding/flip player sprite preview
NUM_MKEYS,
// These are not buttons but events sent from other menus
MKEY_Input, // Sent when input is confirmed
MKEY_Abort, // Input aborted
MKEY_MBYes,
MKEY_MBNo,
};
enum ENativeFontValues
{
NIT_BigFont,
NIT_SmallFont,
NIT_ActiveColor = -1,
NIT_InactiveColor = -2,
NIT_SelectedColor = -3,
NIT_ActiveState = 1,
NIT_InactiveState = 2,
NIT_SelectedState = 3
// positive values for color are direct palswap indices.
};
extern FNewGameStartup NewGameStartupInfo;
extern EMenuState menuactive;
//=============================================================================
//
// menu descriptor. This is created from the menu definition lump
// Items must be inserted in the order they are cycled through with the cursor
//
//=============================================================================
enum EMenuDescriptorType
{
MDESC_ListMenu,
MDESC_OptionsMenu,
MDESC_ImageScroller,
};
struct FMenuDescriptor
{
FName mMenuName;
FString mNetgameMessage;
int mType;
FName mClass;
virtual ~FMenuDescriptor() {}
};
class FListMenuItem;
class FOptionMenuItem;
enum ListMenuFlags
{
LMF_Centered = 1,
LMF_DontSpace = 2,
LMF_Animate = 4,
};
struct FListMenuDescriptor : public FMenuDescriptor
{
TDeletingArray<FListMenuItem *> mItems;
FString mCaption;
int mSelectedItem;
int mSelectOfsX;
int mSelectOfsY;
FGameTexture *mSelector;
int mDisplayTop;
int mXpos, mYpos, mYbotton;
int mWLeft, mWRight;
int mLinespacing; // needs to be stored for dynamically created menus
int mAutoselect; // this can only be set by internal menu creation functions
int mScriptId;
int mSecondaryId;
int mNativeFontNum, mNativePalNum;
float mNativeFontScale;
FFont *mFont;
EColorRange mFontColor;
EColorRange mFontColor2;
FMenuDescriptor *mRedirect; // used to redirect overlong skill and episode menus to option menu based alternatives
int mFlags;
int mSpacing;
FListMenuDescriptor()
{
Reset();
}
void Reset();
};
struct FOptionMenuSettings
{
EColorRange mTitleColor;
EColorRange mFontColor;
EColorRange mFontColorValue;
EColorRange mFontColorMore;
EColorRange mFontColorHeader;
EColorRange mFontColorHighlight;
EColorRange mFontColorSelection;
int mLinespacing;
};
struct FOptionMenuDescriptor : public FMenuDescriptor
{
TDeletingArray<FOptionMenuItem *> mItems;
FString mTitle;
int mSelectedItem;
int mDrawTop;
int mScrollTop;
int mScrollPos;
int mIndent;
int mPosition;
bool mDontDim;
void CalcIndent();
FOptionMenuItem *GetItem(FName name);
void Reset()
{
// Reset the default settings (ignore all other values in the struct)
mPosition = 0;
mScrollTop = 0;
mIndent = 0;
mDontDim = 0;
}
};
struct FImageScrollerDescriptor : public FMenuDescriptor
{
struct ScrollerItem
{
int type; // 0: fullscreen image; 1: centered text
int scriptID;
FString text;
};
int mFlags = 0;
TArray<ScrollerItem> mItems;
};
typedef TMap<FName, FMenuDescriptor *> MenuDescriptorList;
extern FOptionMenuSettings OptionSettings;
extern MenuDescriptorList MenuDescriptors;
#define CURSORSPACE (14 * CleanXfac_1)
//=============================================================================
//
//
//
//=============================================================================
struct FMenuRect
{
int x, y;
int width, height;
void set(int _x, int _y, int _w, int _h)
{
x = _x;
y = _y;
width = _w;
height = _h;
}
bool inside(int _x, int _y)
{
return _x >= x && _x < x+width && _y >= y && _y < y+height;
}
};
class DMenu
{
protected:
bool mMouseCapture;
bool mBackbuttonSelected;
public:
enum
{
MOUSE_Click,
MOUSE_Move,
MOUSE_Release
};
enum
{
BACKBUTTON_TIME = 4*MENU_TICRATE
};
static bool InMenu;
DMenu *mParentMenu;
DVector2 origin = { 0,0 };
int scriptID = INT_MAX;
bool canAnimate = false;
bool isAnimated = false; // set to true when uncapped frame rate is needed.
DMenu(DMenu *parent = NULL);
virtual ~DMenu() = default;
virtual bool Responder (event_t *ev);
virtual bool MenuEvent (int mkey, bool fromcontroller);
virtual void Ticker ();
virtual void PreDraw() {}
virtual void PostDraw() {}
virtual void Drawer ();
virtual bool DimAllowed ();
virtual bool TranslateKeyboardEvents();
virtual void Close();
virtual bool MouseEvent(int type, int x, int y);
virtual void Destroy() {}
bool IsAnimated() const { return isAnimated; }
bool MouseEventBack(int type, int x, int y);
void SetCapture();
void ReleaseCapture();
void SetOrigin();
bool HasCapture()
{
return mMouseCapture;
}
};
//=============================================================================
//
// base class for menu items
//
//=============================================================================
class DListMenu;
class FListMenuItem
{
protected:
int mXpos, mYpos;
int mHeight;
FName mAction;
public:
bool mEnabled, mHidden;
FListMenuItem(int xpos = 0, int ypos = 0, FName action = NAME_None)
{
mXpos = xpos;
mYpos = ypos;
mAction = action;
mEnabled = true;
mHidden = false;
}
virtual ~FListMenuItem();
virtual bool CheckCoordinate(int x, int y);
virtual void Ticker();
virtual void Drawer(DListMenu *menu, const DVector2& origin, bool selected);
virtual bool Selectable();
virtual bool Activate(FName caller);
virtual FName GetAction(int *pparam);
virtual bool SetString(int i, const char *s);
virtual bool GetString(int i, char *s, int len);
virtual bool SetValue(int i, int value);
virtual bool GetValue(int i, int *pvalue);
virtual void Enable(bool on);
virtual bool MenuEvent (int mkey, bool fromcontroller);
virtual bool MouseEvent(int type, int x, int y);
virtual bool CheckHotkey(int c);
virtual int GetWidth();
virtual void DrawSelector(int xofs, int yofs, FGameTexture *tex);
void OffsetPositionY(int ydelta) { mYpos += ydelta; }
int GetY() { return mYpos; }
int GetX() { return mXpos; }
void SetX(int x) { mXpos = x; }
void SetY(int x) { mYpos = x; }
void SetHeight(int x) { mHeight = x; }
void SetAction(FName action) { mAction = action; }
};
class FListMenuItemStaticPatch : public FListMenuItem
{
protected:
FGameTexture *mTexture;
bool mCentered;
public:
FListMenuItemStaticPatch(int x, int y, FGameTexture *patch, bool centered);
void Drawer(DListMenu* menu, const DVector2& origin, bool selected);
};
class FListMenuItemStaticText : public FListMenuItem
{
protected:
const char *mText;
FFont *mFont;
EColorRange mColor;
bool mCentered;
public:
FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered);
~FListMenuItemStaticText();
void Drawer(DListMenu* menu, const DVector2& origin, bool selected) override;
};
class FListMenuItemNativeStaticText : public FListMenuItem
{
protected:
FString mText;
int mFontnum;
int mPalnum;
bool mCentered;
public:
FListMenuItemNativeStaticText(int x, int y, const FString & text, int fontnum, int palnum, bool centered);
void Drawer(DListMenu* menu, const DVector2& origin, bool selected) override;
};
//=============================================================================
//
// selectable items
//
//=============================================================================
class FListMenuItemSelectable : public FListMenuItem
{
protected:
int mHotkey;
int mParam;
public:
FListMenuItemSelectable(int x, int y, int height, FName childmenu, int mParam = -1);
bool CheckCoordinate(int x, int y) override;
bool Selectable() override;
bool CheckHotkey(int c) override;
bool Activate(FName caller) override;
bool MouseEvent(int type, int x, int y) override;
FName GetAction(int *pparam) override;
};
class FListMenuItemText : public FListMenuItemSelectable
{
FString mText;
FFont *mFont;
EColorRange mColor;
EColorRange mColorSelected;
public:
FListMenuItemText(int x, int y, int height, int hotkey, const FString &text, FFont *font, EColorRange color, EColorRange color2, FName child, int param = 0);
~FListMenuItemText();
void Drawer(DListMenu* menu, const DVector2& origin, bool selected) override;
int GetWidth() override;
};
class FListMenuItemNativeText : public FListMenuItemSelectable
{
// This draws the item with the game frontend's native text drawer and uses a front end defined font, it takes only symbolic constants as parameters.
FString mText;
int mFontnum;
int mPalnum;
float mFontscale;
public:
FListMenuItemNativeText(int x, int y, int height, int hotkey, const FString& text, int fontnum, int palnum, float fontscale, FName child, int param = 0);
~FListMenuItemNativeText();
void Drawer(DListMenu* menu, const DVector2& origin, bool selected) override;
int GetWidth() override;
void DrawSelector(int xofs, int yofs, FGameTexture* tex) override { } // The text drawer handles this itself.
};
class FListMenuItemPatch : public FListMenuItemSelectable
{
FGameTexture* mTexture;
public:
FListMenuItemPatch(int x, int y, int height, int hotkey, FGameTexture* patch, FName child, int param = 0);
void Drawer(DListMenu* menu, const DVector2& origin, bool selected) override;
int GetWidth() override;
};
//=============================================================================
//
// list menu class runs a menu described by a FListMenuDescriptor
//
//=============================================================================
class DListMenu : public DMenu
{
typedef DMenu Super;
protected:
FListMenuDescriptor *mDesc = nullptr;
FListMenuItem *mFocusControl = nullptr;
public:
DListMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL);
FListMenuItem *GetItem(FName name);
bool Responder (event_t *ev) override;
bool MenuEvent (int mkey, bool fromcontroller) override;
bool MouseEvent(int type, int x, int y) override;
void Ticker () override;
void Drawer () override;
void PreDraw() override;
virtual void SelectionChanged() {}
void SetFocus(FListMenuItem *fc)
{
mFocusControl = fc;
}
bool CheckFocus(FListMenuItem *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
const FListMenuDescriptor* Descriptor() const
{
return mDesc;
}
};
//=============================================================================
//
// base class for menu items
//
//=============================================================================
class FOptionMenuItem : public FListMenuItem
{
protected:
FString mLabel;
bool mCentered = false;
void drawText(int x, int y, int color, const char * text, bool grayed = false);
int drawLabel(int indent, int y, EColorRange color, bool grayed = false);
void drawValue(int indent, int y, int color, const char *text, bool grayed = false);
int CursorSpace();
public:
FOptionMenuItem(const char *text, FName action = NAME_None, bool center = false)
: FListMenuItem(0, 0, action)
{
mLabel = text;
mCentered = center;
}
~FOptionMenuItem();
virtual int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected);
virtual bool Selectable();
virtual int GetIndent();
virtual bool MouseEvent(int type, int x, int y);
};
//=============================================================================
//
//
//
//=============================================================================
struct FOptionValues
{
struct Pair
{
double Value;
FString TextValue;
FString Text;
};
TArray<Pair> mValues;
};
typedef TMap< FName, FOptionValues* > FOptionMap;
extern FOptionMap OptionValues;
//=============================================================================
//
// Option menu class runs a menu described by a FOptionMenuDescriptor
//
//=============================================================================
class DOptionMenu : public DMenu
{
using Super = DMenu;
bool CanScrollUp;
bool CanScrollDown;
int VisBottom;
FOptionMenuItem *mFocusControl;
protected:
FOptionMenuDescriptor *mDesc;
int GetPosition();
public:
FOptionMenuItem *GetItem(FName name);
DOptionMenu(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
virtual void Init(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL);
int FirstSelectable();
bool Responder (event_t *ev);
bool MenuEvent (int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker ();
void Drawer ();
virtual int GetIndent();
const FOptionMenuDescriptor *GetDescriptor() const { return mDesc; }
void SetFocus(FOptionMenuItem *fc)
{
mFocusControl = fc;
}
bool CheckFocus(FOptionMenuItem *fc)
{
return mFocusControl == fc;
}
void ReleaseFocus()
{
mFocusControl = NULL;
}
};
FFont *OptionFont();
int OptionHeight();
int OptionWidth(const char * s);
void DrawOptionText(int x, int y, int color, const char *text, bool grayed = false);
//=============================================================================
//
// ImageScroller
//
//=============================================================================
class ImageScreen;
class DImageScrollerMenu : public DMenu
{
DMenu* mCurrent = nullptr;
FImageScrollerDescriptor* mDesc = nullptr;
int index = 0;
MenuTransition pageTransition = {};
protected:
virtual ImageScreen* newImageScreen(FImageScrollerDescriptor::ScrollerItem* desc);
public:
void Init(DMenu* parent = nullptr, FImageScrollerDescriptor* desc = nullptr);
bool MenuEvent(int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void Ticker();
void Drawer();
};
//=============================================================================
//
// Input some text
//
//=============================================================================
class DTextEnterMenu : public DMenu
{
using Super = DMenu;
FString mEnterString;
int mEnterSize;
bool mInputGridOkay;
int InputGridX;
int InputGridY;
int CursorSize;
bool AllowColors;
FFont *displayFont;
void AppendChar(int ch);
public:
// [TP] Added allowcolors
DTextEnterMenu(DMenu *parent, FFont *dpf, FString textbuffer, int maxlen, bool showgrid, bool allowcolors = false);
void Drawer ();
bool MenuEvent (int mkey, bool fromcontroller);
bool Responder(event_t *ev);
bool TranslateKeyboardEvents();
bool MouseEvent(int type, int x, int y);
const char* GetText() { return mEnterString.GetChars(); }
};
//=============================================================================
//
// Show a fullscreen image / centered text screen for an image scroller
//
//=============================================================================
class ImageScreen : public DMenu
{
protected:
const FImageScrollerDescriptor::ScrollerItem* mDesc;
public:
ImageScreen(const FImageScrollerDescriptor::ScrollerItem* it)
{
mDesc = it;
}
void Drawer() override;
};
struct event_t;
void M_EnableMenu (bool on) ;
bool M_Responder (event_t *ev);
void M_Ticker (void);
void M_Drawer (void);
void M_PreviousMenu();
void M_Init (void);
void M_CreateMenus();
void M_ActivateMenu(DMenu *menu);
void M_ClearMenus (bool final = false);
void M_ParseMenuDefs();
void M_StartupSkillMenu(FNewGameStartup *gs);
int M_GetDefaultSkill();
void M_StartControlPanel (bool makeSound);
bool M_SetMenu(FName menu, int param = -1, FName callingMenu = NAME_None);
void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave);
void M_StartMessage(const char *message, int messagemode, int scriptId, FName action = NAME_None);
void M_UnhideCustomMenu(int menu, int itemmask);
void M_MenuSound(EMenuSounds snd);
void M_Autosave();
bool M_Active();
void M_DeinitMenus();
void M_UnpauseSound();
void I_SetMouseCapture();
void I_ReleaseMouseCapture();
struct MenuClassDescriptor;
extern TArray<MenuClassDescriptor*> menuClasses;
using hFunc = std::function<bool(bool)>;
DMenu* CreateMessageBoxMenu(DMenu* parent, const char* message, int messagemode, int scriptID, bool playsound, FName action = NAME_None, hFunc handler = nullptr);
struct MenuClassDescriptor
{
FName mName;
MenuClassDescriptor(const char* name) : mName(name)
{
//menuClasses.Push(this);
}
virtual DMenu* CreateNew() = 0;
};
template<class Menu> struct TMenuClassDescriptor : public MenuClassDescriptor
{
TMenuClassDescriptor(const char* name) : MenuClassDescriptor(name)
{}
DMenu* CreateNew()
{
return new Menu;
}
};
struct FSavegameManager
{
private:
TArray<FSaveGameNode*> SaveGames;
FSaveGameNode NewSaveNode;
int LastSaved = -1;
int LastAccessed = -1;
TArray<char> SavePicData;
FGameTexture *SavePic = nullptr;
public:
int WindowSize = 0;
FString SaveCommentString;
FSaveGameNode *quickSaveSlot = nullptr;
~FSavegameManager();
private:
int InsertSaveNode(FSaveGameNode *node);
public:
void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave, bool forceQuicksave);
void ClearSaveGames();
void ReadSaveStrings();
void UnloadSaveData();
int RemoveSaveSlot(int index);
void LoadSavegame(int Selected);
void DoSave(int Selected, const char *savegamestring);
unsigned ExtractSaveData(int index);
void ClearSaveStuff();
bool DrawSavePic(int x, int y, int w, int h);
void DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor);
void SetFileInfo(int Selected);
unsigned SavegameCount();
FSaveGameNode *GetSavegame(int i);
void InsertNewSaveNode();
bool RemoveNewSaveNode();
void LoadGame(FSaveGameNode* node);
void SaveGame(FSaveGameNode* node, bool ok4q, bool forceq);
};
extern FSavegameManager savegameManager;
extern DMenu* CurrentMenu;
bool M_IsAnimated();
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,374 +0,0 @@
/*
** menuinput.cpp
** The string input code
**
**---------------------------------------------------------------------------
** Copyright 2001-2010 Randy Heit
** Copyright 2010 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 "menu.h"
#include "c_cvars.h"
#include "d_event.h"
#include "d_gui.h"
#include "v_font.h"
#include "v_text.h"
#include "v_draw.h"
#include "v_video.h"
#define INPUTGRID_WIDTH 13
#define INPUTGRID_HEIGHT 5
// Heretic and Hexen do not, by default, come with glyphs for all of these
// characters. Oh well. Doom and Strife do.
static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] =
"ABCDEFGHIJKLM"
"NOPQRSTUVWXYZ"
"0123456789+-="
".,!?@'\":;[]()"
"<>^#$%&*/_ \b";
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//=============================================================================
//
//
//
//=============================================================================
// [TP] Added allowcolors
DTextEnterMenu::DTextEnterMenu(DMenu *parent, FFont *dpf, FString textbuffer, int maxlen, bool showgrid, bool allowcolors)
: DMenu(parent)
{
mEnterString = textbuffer;
mEnterSize = maxlen;
mInputGridOkay = (showgrid && (m_showinputgrid == 0)) || (m_showinputgrid >= 1);
if (mEnterString.Len() > 0)
{
InputGridX = INPUTGRID_WIDTH - 1;
InputGridY = INPUTGRID_HEIGHT - 1;
}
else
{
// If we are naming a new save, don't start the cursor on "end".
InputGridX = 0;
InputGridY = 0;
}
AllowColors = allowcolors; // [TP]
displayFont = dpf;
CursorSize = displayFont->StringWidth(displayFont->GetCursor());
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::TranslateKeyboardEvents()
{
return mInputGridOkay;
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
// Save game and player name string input
if (ev->subtype == EV_GUI_Char)
{
mInputGridOkay = false;
AppendChar(ev->data1);
return true;
}
char ch = (char)ev->data1;
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
{
if (mEnterString.Len() > 0)
{
mEnterString.DeleteLastCharacter();
}
}
else if (ev->subtype == EV_GUI_KeyDown)
{
if (ch == GK_ESCAPE)
{
DMenu *parent = mParentMenu;
parent->MenuEvent(MKEY_Abort, false);
Close();
return true;
}
else if (ch == '\r')
{
if (mEnterString.Len() > 0)
{
// [TP] If we allow color codes, colorize the string now.
//if (AllowColors)
//mEnterString = mEnterString.Filter();
DMenu *parent = mParentMenu;
parent->MenuEvent(MKEY_Input, false);
Close();
return true;
}
}
}
if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat)
{
return true;
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
{
const int cell_width = 18 * CleanXfac_1;
const int cell_height = 16 * CleanYfac_1;
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
{
InputGridX = (x - screen_x) / cell_width;
InputGridY = (y - screen_y) / cell_height;
if (type == DMenu::MOUSE_Release)
{
if (MenuEvent(MKEY_Enter, true))
{
//M_MenuSound(CursorSound);
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
}
}
return true;
}
else
{
InputGridX = InputGridY = -1;
}
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::AppendChar(int ch)
{
FStringf newstring("%s%c%c", mEnterString.GetChars(), ch, displayFont->GetCursor());
if (mEnterSize < 0 || displayFont->StringWidth(newstring) < mEnterSize)
{
mEnterString.AppendCharacter(ch);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
{
if (key == MKEY_Back)
{
mParentMenu->MenuEvent(MKEY_Abort, false);
return Super::MenuEvent(key, fromcontroller);
}
if (fromcontroller)
{
mInputGridOkay = true;
}
if (mInputGridOkay)
{
int ch;
if (InputGridX == -1 || InputGridY == -1)
{
InputGridX = InputGridY = 0;
}
switch (key)
{
case MKEY_Down:
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Up:
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
return true;
case MKEY_Right:
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Left:
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
return true;
case MKEY_Clear:
if (mEnterString.Len() > 0)
{
mEnterString.DeleteLastCharacter();
}
return true;
case MKEY_Enter:
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
if (mInputGridOkay)
{
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
if (ch == 0) // end
{
if (mEnterString.Len() > 0)
{
DMenu *parent = mParentMenu;
parent->MenuEvent(MKEY_Input, false);
Close();
return true;
}
}
else if (ch == '\b') // bs
{
if (mEnterString.Len() > 0)
{
mEnterString.DeleteLastCharacter();
}
}
else
{
AppendChar(ch);
}
}
return true;
default:
break; // Keep GCC quiet
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DTextEnterMenu::Drawer ()
{
mParentMenu->Drawer();
if (mInputGridOkay)
{
const int cell_width = 18 * CleanXfac;
const int cell_height = 12 * CleanYfac;
const int top_padding = cell_height / 2 - displayFont->GetHeight() * CleanYfac / 2;
// Darken the background behind the character grid.
// Unless we frame it with a border, I think it looks better to extend the
// background across the full width of the screen.
twod->AddColorOnlyQuad(0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
screen->GetHeight() - INPUTGRID_HEIGHT * cell_height,
screen->GetWidth() /*13 * cell_width*/,
INPUTGRID_HEIGHT * cell_height, 0xc8000000);
if (InputGridX >= 0 && InputGridY >= 0)
{
// Highlight the background behind the selected character.
twod->AddColorOnlyQuad(
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2,
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(),
cell_width, cell_height, PalEntry(255, 255, 248, 220));
}
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
{
const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight();
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
{
int width;
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
auto pic = displayFont->GetChar(ch, CR_DARKGRAY, &width);
EColorRange color;
int remap;
// The highlighted character is yellow; the rest are dark gray.
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
remap = displayFont->GetColorTranslation(color);
if (pic != NULL)
{
// Draw a normal character.
DrawTexture(twod, pic, xx + cell_width/2 - width*CleanXfac_1/2, yy + top_padding,
DTA_TranslationIndex, remap,
DTA_CleanNoMove_1, true,
TAG_DONE);
}
else if (ch == ' ')
{
// Draw the space as a box outline. We also draw it 50% wider than it really is.
const int x1 = xx + cell_width/2 - width * CleanXfac_1 * 3 / 4;
const int x2 = x1 + width * 3 * CleanXfac_1 / 2;
const int y1 = yy + top_padding;
const int y2 = y1 + displayFont->GetHeight() * CleanYfac_1;
auto palcolor = PalEntry(255, 160, 160, 160);
twod->AddColorOnlyQuad(x1, y1, x2 - x1, CleanYfac_1, palcolor); // top
twod->AddColorOnlyQuad(x1, y2, x2 - x1, CleanYfac_1, palcolor); // bottom
twod->AddColorOnlyQuad(x1, y1+CleanYfac_1, CleanXfac_1, y2 - y1, palcolor); // left
twod->AddColorOnlyQuad(x2-CleanXfac_1, y1+CleanYfac_1, CleanXfac_1, CleanYfac_1, palcolor); // right
}
else if (ch == '\b' || ch == 0)
{
// Draw the backspace and end "characters".
const char *const str = ch == '\b' ? "BS" : "ED";
DrawText(twod, NewSmallFont, color,
xx + cell_width/2 - displayFont->StringWidth(str)*CleanXfac_1/2,
yy + top_padding, str, DTA_CleanNoMove_1, true, TAG_DONE);
}
}
}
}
Super::Drawer();
}

View file

@ -1,468 +0,0 @@
/*
** messagebox.cpp
** Confirmation, notification screns
**
**---------------------------------------------------------------------------
** Copyright 2010 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 "menu.h"
#include "d_event.h"
#include "d_gui.h"
#include "v_text.h"
#include "v_draw.h"
#include "gstrings.h"
#include "c_dispatch.h"
#include "statistics.h"
#include "v_2ddrawer.h"
#include "v_video.h"
#include "i_time.h"
#include "engineerrors.h"
extern FSaveGameNode *quickSaveSlot;
void GameInterface::DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool bg)
{
double scale = SmallFontScale();
int formatwidth = int(320 / scale);
auto lines = V_BreakLines(SmallFont, formatwidth, text, true);
auto fheight = bg? 10 : SmallFont->GetHeight()* scale; // Fixme: Get spacing for text pages from elsewhere.
if (!bg)
{
auto totaltextheight = lines.Size() * fheight;
position -= totaltextheight / 2;
}
double y = origin.Y + position;
for (auto& line : lines)
{
double x = origin.X + 160 - line.Width * scale * 0.5;
DrawText(twod, SmallFont, CR_UNTRANSLATED, x, y, line.Text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
y += fheight;
}
}
class DMessageBoxMenu : public DMenu
{
using Super = DMenu;
FString mFullMessage;
TArray<FBrokenLines> mMessage;
int mMessageMode;
int messageSelection;
int mMouseLeft, mMouseRight, mMouseY;
FName mAction;
std::function<bool(bool)> mActionFunc;
public:
DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None, hFunc handler = nullptr);
void Destroy();
void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false);
void Drawer();
bool Responder(event_t *ev);
bool MenuEvent(int mkey, bool fromcontroller);
bool MouseEvent(int type, int x, int y);
void CloseSound();
virtual void HandleResult(bool res);
};
//=============================================================================
//
//
//
//=============================================================================
DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action, hFunc handler)
: DMenu(parent)
{
mAction = action;
mActionFunc = handler;
messageSelection = 0;
mMouseLeft = 140;
mMouseY = INT_MIN;
int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]);
int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]);
mMouseRight = std::max(mr1, mr2);
Init(parent, message, messagemode, playsound);
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound)
{
mParentMenu = parent;
if (message != NULL)
{
mFullMessage = message;
mMessage = V_BreakLines(SmallFont, 300, GStrings.localize(message));
}
mMessageMode = messagemode;
if (playsound)
{
//S_StopSound (CHAN_VOICE);
//S_Sound (CHAN_VOICE | CHANF_UI, "menu/prompt", snd_menuvolume, ATTN_NONE);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::Destroy()
{
mMessage.Reset();
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::CloseSound()
{
M_MenuSound(CurrentMenu ? BackSound : ::CloseSound);
}
//=============================================================================
//
//
//
//=============================================================================
void DMessageBoxMenu::HandleResult(bool res)
{
if (mMessageMode == 0)
{
if (mActionFunc)
{
if (mActionFunc(res)) Close();
}
else if (mAction == NAME_None && mParentMenu)
{
mParentMenu->MenuEvent(res ? MKEY_MBYes : MKEY_MBNo, false);
Close();
}
else
{
Close();
if (res) M_SetMenu(mAction, -1);
}
CloseSound();
}
}
//=============================================================================
//
//
//
//=============================================================================
CVAR(Bool, m_generic_messagebox, false, CVAR_ARCHIVE)
void DMessageBoxMenu::Drawer()
{
int y;
PalEntry fade = 0;
int fontheight = SmallFont->GetHeight();
//V_SetBorderNeedRefresh();
//ST_SetNeedRefresh();
y = 100;
if (m_generic_messagebox)
{
if (mMessage.Size())
{
for (unsigned i = 0; i < mMessage.Size(); i++)
y -= SmallFont->GetHeight() / 2;
for (unsigned i = 0; i < mMessage.Size(); i++)
{
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - mMessage[i].Width / 2, y, mMessage[i].Text,
DTA_Clean, true, TAG_DONE);
y += fontheight;
}
}
if (mMessageMode == 0)
{
y += fontheight;
mMouseY = y;
DrawText(twod, SmallFont,
messageSelection == 0 ? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE);
DrawText(twod, SmallFont,
messageSelection == 1 ? OptionSettings.mFontColorSelection : OptionSettings.mFontColor,
160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE);
if (messageSelection >= 0)
{
auto time = I_msTime() / 30;
if (((time >> 2) % 8) < 6)
{
DrawText(twod, SmallFont, OptionSettings.mFontColorSelection,
(150 - 160) * CleanXfac + screen->GetWidth() / 2,
(y + (fontheight + 1) * messageSelection - 100 + fontheight / 2 - 5) * CleanYfac + screen->GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac,
TAG_DONE);
}
}
}
}
else
{
twod->ClearScreen(0xa0000000);
gi->DrawCenteredTextScreen(origin, mFullMessage, 100, false);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::Responder(event_t *ev)
{
if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown)
{
if (mMessageMode == 0)
{
int ch = tolower(ev->data1);
if (ch == 'n' || ch == ' ')
{
HandleResult(false);
return true;
}
else if (ch == 'y')
{
HandleResult(true);
return true;
}
}
else
{
Close();
return true;
}
return false;
}
else if (ev->type == EV_KeyDown)
{
Close();
return true;
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MenuEvent(int mkey, bool fromcontroller)
{
if (mMessageMode == 0)
{
if ((mkey == MKEY_Up || mkey == MKEY_Down) && m_generic_messagebox)
{
//S_Sound (CHAN_VOICE | CHANF_UI, "menu/cursor", snd_menuvolume, ATTN_NONE);
messageSelection = !messageSelection;
return true;
}
else if (mkey == MKEY_Enter)
{
// 0 is yes, 1 is no
HandleResult(!messageSelection);
return true;
}
else if (mkey == MKEY_Back)
{
HandleResult(false);
return true;
}
return false;
}
else
{
Close();
CloseSound();
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMessageBoxMenu::MouseEvent(int type, int x, int y)
{
if (mMessageMode == 1 || m_generic_messagebox)
{
if (type == MOUSE_Click)
{
return MenuEvent(MKEY_Enter, true);
}
return false;
}
else
{
int sel = -1;
int fh = SmallFont->GetHeight() + 1;
// convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture
x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160;
y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100;
if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh)
{
sel = y >= mMouseY + fh;
}
if (sel != -1 && sel != messageSelection)
{
M_MenuSound(CursorSound);
}
messageSelection = sel;
if (type == MOUSE_Release)
{
return MenuEvent(MKEY_Enter, true);
}
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
void M_StartMessage(const char *message, int messagemode, int scriptId, FName action)
{
if (CurrentMenu == NULL)
{
// only play a sound if no menu was active before
M_StartControlPanel(menuactive == MENU_Off);
}
DMenu *newmenu = new DMessageBoxMenu(CurrentMenu, message, messagemode, false, action);
newmenu->mParentMenu = CurrentMenu;
newmenu->scriptID = scriptId;
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
DMenu* CreateMessageBoxMenu(DMenu* parent, const char* message, int messagemode, int scriptId, bool playsound, FName action, hFunc handler)
{
auto newmenu = new DMessageBoxMenu(CurrentMenu, message, messagemode, false, action, handler);
newmenu->scriptID = scriptId;
return newmenu;
}
void ActivateEndGameMenu()
{
}
CCMD (menu_endgame)
{ // F7
if (!gi->CanSave())
{
return;
}
M_StartControlPanel (true);
FString tempstring;
tempstring << GStrings("ENDGAME") << "\n\n" << GStrings("PRESSYN");
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, 501, false, NAME_None, [](bool res)
{
if (res)
{
STAT_Cancel();
M_ClearMenus();
gi->QuitToTitle();
return false;
}
return true;
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
CCMD (menu_quit)
{ // F10
M_StartControlPanel (true);
FString EndString;
EndString << GStrings("CONFIRM_QUITMSG") << "\n\n" << GStrings("PRESSYN");
DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, EndString, 0, 500, false, NAME_None, [](bool res)
{
if (res) gi->ExitFromMenu();
return true;
});
M_ActivateMenu(newmenu);
}

View file

@ -1,569 +0,0 @@
/*
** optionmenu.cpp
** Handler class for the option menus and associated items
**
**---------------------------------------------------------------------------
** Copyright 2010 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 "c_dispatch.h"
#include "c_console.h"
#include "c_cvars.h"
#include "c_bind.h"
#include "gameconfigfile.h"
#include "menu.h"
#include "v_draw.h"
#include "v_2ddrawer.h"
#include "v_video.h"
#include "i_time.h"
//=============================================================================
//
// Draws a string in the console font, scaled to the 8x8 cells
// used by the default console font.
//
//=============================================================================
FFont *OptionFont()
{
return NewSmallFont;
}
int OptionHeight()
{
return OptionFont()->GetHeight();
}
int OptionWidth(const char * s)
{
return OptionFont()->StringWidth(s);
}
void DrawOptionText(int x, int y, int color, const char *text, bool grayed)
{
PalEntry overlay = grayed? PalEntry(96,48,0,0) : PalEntry(0,0,0);
DrawText (twod, OptionFont(), color, x, y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_END);
}
int DOptionMenu::GetPosition()
{
return mDesc->mPosition * screen->GetHeight() * 2 / CleanYfac_1 / 1080; // y position uses a 1920x1080 screen as reference but has to adjust to scaled 320x200 content.
}
//=============================================================================
//
//
//
//=============================================================================
DOptionMenu::DOptionMenu(DMenu *parent, FOptionMenuDescriptor *desc)
: DMenu(parent)
{
CanScrollUp = false;
CanScrollDown = false;
VisBottom = 0;
mFocusControl = NULL;
Init(parent, desc);
}
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc)
{
mParentMenu = parent;
mDesc = desc;
if (mDesc != NULL && mDesc->mSelectedItem == -1) mDesc->mSelectedItem = FirstSelectable();
}
//=============================================================================
//
//
//
//=============================================================================
int DOptionMenu::FirstSelectable()
{
if (mDesc != NULL)
{
// Go down to the first selectable item
int i = -1;
do
{
i++;
}
while (i < (int)mDesc->mItems.Size() && !mDesc->mItems[i]->Selectable());
if (i>=0 && i < (int)mDesc->mItems.Size()) return i;
}
return -1;
}
//=============================================================================
//
//
//
//=============================================================================
FOptionMenuItem *DOptionMenu::GetItem(FName name)
{
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
FName nm = mDesc->mItems[i]->GetAction(NULL);
if (nm == name) return mDesc->mItems[i];
}
return NULL;
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::Responder (event_t *ev)
{
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_WheelUp)
{
int scrollamt = std::min(2, mDesc->mScrollPos);
mDesc->mScrollPos -= scrollamt;
return true;
}
else if (ev->subtype == EV_GUI_WheelDown)
{
if (CanScrollDown)
{
if (VisBottom < (int)(mDesc->mItems.Size()-2))
{
mDesc->mScrollPos += 2;
VisBottom += 2;
}
else
{
mDesc->mScrollPos++;
VisBottom++;
}
}
return true;
}
}
return Super::Responder(ev);
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller)
{
int startedAt = mDesc->mSelectedItem;
switch (mkey)
{
case MKEY_Up:
if (mDesc->mSelectedItem == -1)
{
mDesc->mSelectedItem = FirstSelectable();
break;
}
do
{
--mDesc->mSelectedItem;
if (mDesc->mScrollPos > 0 &&
mDesc->mSelectedItem <= mDesc->mScrollTop + mDesc->mScrollPos)
{
mDesc->mScrollPos = std::max(mDesc->mSelectedItem - mDesc->mScrollTop - 1, 0);
}
if (mDesc->mSelectedItem < 0)
{
// Figure out how many lines of text fit on the menu
int y = GetPosition();
y *= CleanYfac_1;
int rowheight = OptionSettings.mLinespacing * CleanYfac_1;
int maxitems = (screen->GetHeight() - rowheight - y) / rowheight + 1;
mDesc->mScrollPos = std::max(0, (int)mDesc->mItems.Size() - maxitems + mDesc->mScrollTop);
mDesc->mSelectedItem = mDesc->mItems.Size()-1;
}
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
break;
case MKEY_Down:
if (mDesc->mSelectedItem == -1)
{
mDesc->mSelectedItem = FirstSelectable();
break;
}
do
{
++mDesc->mSelectedItem;
if (CanScrollDown && mDesc->mSelectedItem == VisBottom)
{
mDesc->mScrollPos++;
VisBottom++;
}
if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
{
if (startedAt == -1)
{
mDesc->mSelectedItem = -1;
mDesc->mScrollPos = -1;
break;
}
else
{
mDesc->mSelectedItem = 0;
mDesc->mScrollPos = 0;
}
}
}
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt);
break;
case MKEY_PageUp:
if (mDesc->mScrollPos > 0)
{
mDesc->mScrollPos -= VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
if (mDesc->mScrollPos < 0)
{
mDesc->mScrollPos = 0;
}
if (mDesc->mSelectedItem != -1)
{
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1;
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
{
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
{
mDesc->mSelectedItem = 0;
}
}
if (mDesc->mScrollPos > mDesc->mSelectedItem)
{
mDesc->mScrollPos = mDesc->mSelectedItem;
}
}
}
break;
case MKEY_PageDown:
if (CanScrollDown)
{
int pagesize = VisBottom - mDesc->mScrollPos - mDesc->mScrollTop;
mDesc->mScrollPos += pagesize;
if (mDesc->mScrollPos + mDesc->mScrollTop + pagesize > (int)mDesc->mItems.Size())
{
mDesc->mScrollPos = mDesc->mItems.Size() - mDesc->mScrollTop - pagesize;
}
if (mDesc->mSelectedItem != -1)
{
mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos;
while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable())
{
if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size())
{
mDesc->mSelectedItem = 0;
}
}
if (mDesc->mScrollPos > mDesc->mSelectedItem)
{
mDesc->mScrollPos = mDesc->mSelectedItem;
}
}
}
break;
case MKEY_Enter:
if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate(mDesc->mMenuName))
{
return true;
}
// fall through to default
default:
if (mDesc->mSelectedItem >= 0 &&
mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true;
return Super::MenuEvent(mkey, fromcontroller);
}
if (mDesc->mSelectedItem != startedAt)
{
M_MenuSound(CursorSound);
}
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool DOptionMenu::MouseEvent(int type, int x, int y)
{
y = (y / CleanYfac_1) - mDesc->mDrawTop;
if (mFocusControl)
{
mFocusControl->MouseEvent(type, x, y);
return true;
}
else
{
int yline = (y / OptionSettings.mLinespacing);
if (yline >= mDesc->mScrollTop)
{
yline += mDesc->mScrollPos;
}
if ((unsigned)yline < mDesc->mItems.Size() && mDesc->mItems[yline]->Selectable())
{
if (yline != mDesc->mSelectedItem)
{
mDesc->mSelectedItem = yline;
//M_MenuSound(CursorSound); too noisy
}
mDesc->mItems[yline]->MouseEvent(type, x, y);
return true;
}
}
mDesc->mSelectedItem = -1;
return Super::MouseEvent(type, x, y);
}
//=============================================================================
//
//
//
//=============================================================================
void DOptionMenu::Ticker ()
{
Super::Ticker();
for(unsigned i=0;i<mDesc->mItems.Size(); i++)
{
mDesc->mItems[i]->Ticker();
}
}
//=============================================================================
//
//
//
//=============================================================================
int DOptionMenu::GetIndent()
{
int indent = std::max(0, (mDesc->mIndent + 40) - CleanWidth_1 / 2);
return screen->GetWidth() / 2 + indent * CleanXfac_1;
}
void DOptionMenu::Drawer ()
{
int y = GetPosition();
if (mDesc->mTitle.IsNotEmpty())
{
gi->DrawMenuCaption(origin, GStrings.localize(mDesc->mTitle));
}
mDesc->mDrawTop = y;
int fontheight = OptionSettings.mLinespacing * CleanYfac_1;
y *= CleanYfac_1;
int indent = GetIndent();
int ytop = y + mDesc->mScrollTop * 8 * CleanYfac_1;
int lastrow = screen->GetHeight() - OptionFont()->GetHeight() * CleanYfac_1;
unsigned i;
for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight)
{
// Don't scroll the uppermost items
if ((int)i == mDesc->mScrollTop)
{
i += mDesc->mScrollPos;
if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu
}
bool isSelected = mDesc->mSelectedItem == (int)i;
int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, isSelected);
if (cur_indent >= 0 && isSelected && mDesc->mItems[i]->Selectable())
{
auto time = I_msTime() / 30;
if ((((time>>2)%8) < 6) || CurrentMenu != this)
{
DrawOptionText(cur_indent + 3 * CleanXfac_1, y, OptionSettings.mFontColorSelection, "");
//M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd");
}
}
}
CanScrollUp = (mDesc->mScrollPos > 0);
CanScrollDown = (i < mDesc->mItems.Size());
VisBottom = i - 1;
if (CanScrollUp)
{
DrawOptionText(screen->GetWidth() - 11 * CleanXfac_1, ytop, OptionSettings.mFontColorSelection, "");
//M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a");
}
if (CanScrollDown)
{
DrawOptionText(screen->GetWidth() - 11 * CleanXfac_1 , y - 8*CleanYfac_1, OptionSettings.mFontColorSelection, "");
//M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b");
}
Super::Drawer();
}
//=============================================================================
//
// base class for menu items
//
//=============================================================================
FOptionMenuItem::~FOptionMenuItem()
{
}
int FOptionMenuItem::Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected)
{
return indent;
}
bool FOptionMenuItem::Selectable()
{
return true;
}
bool FOptionMenuItem::MouseEvent(int type, int x, int y)
{
if (Selectable() && type == DMenu::MOUSE_Release)
{
return CurrentMenu->MenuEvent(MKEY_Enter, true);
}
return false;
}
int FOptionMenuItem::GetIndent()
{
if (mCentered) return 0;
if (screen->GetWidth() < 640) return screen->GetWidth() / 2;
return OptionWidth(GStrings.localize(mLabel));
}
void FOptionMenuItem::drawText(int x, int y, int color, const char * text, bool grayed)
{
DrawOptionText(x, y, color, text, grayed);
}
int FOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed)
{
const char *label = GStrings.localize(mLabel);
int x;
int w = OptionWidth(label) * CleanXfac_1;
if (!mCentered) x = indent - w;
else x = (screen->GetWidth() - w) / 2;
DrawOptionText(x, y, color, label, grayed);
return x;
}
void FOptionMenuItem::drawValue(int indent, int y, int color, const char *text, bool grayed)
{
DrawOptionText(indent + CursorSpace(), y, color, text, grayed);
}
int FOptionMenuItem::CursorSpace()
{
return (14 * CleanXfac_1);
}
void FOptionMenuDescriptor::CalcIndent()
{
// calculate the menu indent
int widest = 0, thiswidth;
for (unsigned i = 0; i < mItems.Size(); i++)
{
thiswidth = mItems[i]->GetIndent();
if (thiswidth > widest) widest = thiswidth;
}
mIndent = widest + 4;
}
//=============================================================================
//
//
//
//=============================================================================
FOptionMenuItem *FOptionMenuDescriptor::GetItem(FName name)
{
for(unsigned i=0;i<mItems.Size(); i++)
{
FName nm = mItems[i]->GetAction(NULL);
if (nm == name) return mItems[i];
}
return NULL;
}
class PlayerMenu : public DOptionMenu
{
using Super = DOptionMenu;
public:
void Drawer()
{
// Hack: The team item is #3. This part doesn't work properly yet.
gi->DrawPlayerSprite(origin, (mDesc->mSelectedItem == 3));
Super::Drawer();
}
};
static TMenuClassDescriptor<PlayerMenu> _ppm("NewPlayerMenu");
void RegisterOptionMenus()
{
menuClasses.Push(&_ppm);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,725 @@
/*
** menu.cpp
** Menu base class and global interface
**
**---------------------------------------------------------------------------
** Copyright 2010 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 "c_dispatch.h"
#include "d_gui.h"
#include "c_buttons.h"
#include "c_console.h"
#include "c_bind.h"
#include "d_eventbase.h"
#include "g_input.h"
#include "configfile.h"
#include "gstrings.h"
#include "menu.h"
#include "vm.h"
#include "v_video.h"
#include "i_system.h"
#include "types.h"
#include "texturemanager.h"
#include "v_draw.h"
#include "vm.h"
#include "gamestate.h"
#include "i_interface.h"
#include "d_event.h"
#include "st_start.h"
#include "i_system.h"
#include "gameconfigfile.h"
#include "gamecontrol.h"
#include "raze_sound.h"
#include "gamestruct.h"
#include "razemenu.h"
#include "mapinfo.h"
#include "statistics.h"
#include "i_net.h"
#include "savegamehelp.h"
#include "gi.h"
EXTERN_CVAR(Int, cl_gfxlocalization)
EXTERN_CVAR(Bool, m_quickexit)
EXTERN_CVAR(Bool, saveloadconfirmation) // [mxd]
EXTERN_CVAR(Bool, quicksaverotation)
EXTERN_CVAR(Bool, show_messages)
CVAR(Bool, menu_sounds, true, CVAR_ARCHIVE) // added mainly because RR's sounds are so supremely annoying.
typedef void(*hfunc)();
DMenu* CreateMessageBoxMenu(DMenu* parent, const char* message, int messagemode, bool playsound, FName action = NAME_None, hfunc handler = nullptr);
bool OkForLocalization(FTextureID texnum, const char* substitute);
void D_ToggleHud();
void I_WaitVBL(int count);
extern bool hud_toggled;
bool help_disabled;
FNewGameStartup NewGameStartupInfo;
//FNewGameStartup NewGameStartupInfo;
bool M_SetSpecialMenu(FName& menu, int param)
{
// Engine credits need a different approach to work with the option search
#if 0
// Transitions between the engine credits pages need to pop off the last slide
if (!strnicmp(menu.GetChars(), "EngineCredits", 13) && CurrentMenu && !strnicmp(CurrentMenu->GetClass()->TypeName.GetChars(), "EngineCredits", 13))
{
auto m = CurrentMenu;
CurrentMenu = m->mParentMenu;
m->mParentMenu = nullptr;
m->Destroy();
}
#endif
switch (menu.GetIndex())
{
case NAME_Mainmenu:
if (gi->CanSave()) menu = NAME_IngameMenu;
break;
case NAME_Skillmenu:
// sent from the episode menu
NewGameStartupInfo.Episode = param;
NewGameStartupInfo.Level = 0;
NewGameStartupInfo.Skill = gDefaultSkill;
return true;
case NAME_Startgame:
case NAME_StartgameNoSkill:
menu = NAME_Startgame;
NewGameStartupInfo.Skill = param;
if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param;
if (gi->StartGame(NewGameStartupInfo))
{
M_ClearMenus();
STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill);
inputState.ClearAllInput();
}
return false;
case NAME_CustomSubMenu1:
menu = ENamedName(menu.GetIndex() + param);
break;
case NAME_Savegamemenu:
if (!gi->CanSave())
{
// cannot save outside the game.
M_StartMessage(GStrings("SAVEDEAD"), 1, NAME_None);
return true;
}
break;
case NAME_Quitmenu:
// This is no separate class
C_DoCommand("menu_quit");
return false;
case NAME_EndGameMenu:
// This is no separate class
C_DoCommand("menu_endgame");
return false;
}
// End of special checks
return true;
}
//=============================================================================
//
//
//
//=============================================================================
void M_StartControlPanel(bool makeSound, bool)
{
static bool created = false;
// intro might call this repeatedly
if (CurrentMenu != NULL)
return;
if (!created) // Cannot do this earlier.
{
created = true;
M_CreateMenus();
}
GSnd->SetSfxPaused(true, PAUSESFX_MENU);
gi->MenuOpened();
if (makeSound && menu_sounds) gi->MenuSound(ActivateSound);
M_DoStartControlPanel(false);
}
void System_MenuClosed()
{
GSnd->SetSfxPaused(false, PAUSESFX_MENU);
inputState.ClearAllInput();
gi->MenuClosed();
}
//==========================================================================
//
//
//
//==========================================================================
void System_MenuDim()
{
if (gamestate != GS_MENUSCREEN) // With GS_MENUSCREEN we can assume that the background has been tuned for proper menu display already.
{
Dim(twod, 0, 0.5f, 0, 0, screen->GetWidth(), screen->GetHeight());
}
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(menu_quit)
{ // F10
M_StartControlPanel(true);
FString EndString;
EndString << GStrings("CONFIRM_QUITMSG") << "\n\n" << GStrings("PRESSYN");
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, EndString, 0, false, NAME_None, []()
{
gi->ExitFromMenu();
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(menu_endgame)
{ // F7
if (!gi->CanSave())
{
return;
}
M_StartControlPanel(true);
FString tempstring;
tempstring << GStrings("ENDGAME") << "\n\n" << GStrings("PRESSYN");
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
STAT_Cancel();
M_ClearMenus();
gi->QuitToTitle();
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
//=============================================================================
//
//
//
//=============================================================================
CCMD(quicksave)
{ // F6
if (!gi->CanSave()) return;
if (savegameManager.quickSaveSlot == NULL || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Savegamemenu);
return;
}
auto slot = savegameManager.quickSaveSlot;
// [mxd]. Just save the game, no questions asked.
if (!saveloadconfirmation)
{
G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true);
return;
}
FString tempstring = GStrings("QSPROMPT");
tempstring.Substitute("%s", slot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_SaveGame(savegameManager.quickSaveSlot->Filename, savegameManager.quickSaveSlot->SaveTitle, true, true);
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
//
//
//=============================================================================
CCMD(quickload)
{ // F9
if (netgame)
{
M_StartControlPanel(true);
M_StartMessage(GStrings("QLOADNET"), 1);
return;
}
if (savegameManager.quickSaveSlot == nullptr || savegameManager.quickSaveSlot == (FSaveGameNode*)1)
{
M_StartControlPanel(true);
// signal that whatever gets loaded should be the new quicksave
savegameManager.quickSaveSlot = (FSaveGameNode*)1;
M_SetMenu(NAME_Loadgamemenu);
return;
}
// [mxd]. Just load the game, no questions asked.
if (!saveloadconfirmation)
{
G_LoadGame(savegameManager.quickSaveSlot->Filename);
return;
}
FString tempstring = GStrings("QLPROMPT");
tempstring.Substitute("%s", savegameManager.quickSaveSlot->SaveTitle.GetChars());
M_StartControlPanel(true);
DMenu* newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []()
{
G_LoadGame(savegameManager.quickSaveSlot->Filename);
});
M_ActivateMenu(newmenu);
}
//=============================================================================
//
// Creation wrapper
//
//=============================================================================
static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int height, int hotkey, const char* text, FFont* font, PalEntry color1, PalEntry color2, FName command, int param)
{
const char* classname =
(g_gameType & GAMEFLAG_BLOOD) ? "ListMenuItemBloodTextItem" :
(g_gameType & GAMEFLAG_SW) ? "ListMenuItemSWTextItem" :
(g_gameType & GAMEFLAG_PSEXHUMED) ? "ListMenuItemExhumedTextItem" : "ListMenuItemDukeTextItem";
auto c = PClass::FindClass(classname);
auto p = c->CreateNew();
FString keystr = FString(char(hotkey));
FString textstr = text;
VMValue params[] = { p, x, y, height, &keystr, &textstr, font, int(color1.d), int(color2.d), command.GetIndex(), param };
auto f = dyn_cast<PFunction>(c->FindSymbol("InitDirect", false));
VMCall(f->Variants[0].Implementation, params, countof(params), nullptr, 0);
return (DMenuItemBase*)p;
}
//=============================================================================
//
// Creates the episode menu
//
//=============================================================================
static void BuildEpisodeMenu()
{
// Build episode menu
int addedVolumes = 0;
DMenuDescriptor** desc = MenuDescriptors.CheckKey(NAME_Episodemenu);
if (desc != nullptr && (*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor* ld = static_cast<DListMenuDescriptor*>(*desc);
DMenuItemBase* popped = nullptr;
if (ld->mItems.Size() && ld->mItems.Last()->IsKindOf(NAME_ListMenuItemBloodDripDrawer))
{
ld->mItems.Pop(popped);
}
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
int y = ld->mYpos;
for (int i = 0; i < MAXVOLUMES; i++)
{
if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP))
{
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0);
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0],
gVolumeNames[i], ld->mFont, 0, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag.
y += ld->mLinespacing;
ld->mItems.Push(it);
addedVolumes++;
if (gVolumeSubtitles[i].IsNotEmpty())
{
auto it = CreateListMenuItemStaticText(ld->mXpos, y, gVolumeSubtitles[i], SmallFont, CR_GRAY, false);
y += ld->mLinespacing * 6 / 10;
ld->mItems.Push(it);
}
}
}
#if 0 // this needs to be backed by a working selection menu, until that gets done it must be disabled.
if (!(g_gameType & GAMEFLAG_SHAREWARE))
{
//auto it = new FListMenuItemNativeStaticText(ld->mXpos, "", NIT_SmallFont); // empty entry as spacer.
//ld->mItems.Push(it);
y += ld->mLinespacing / 3;
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, 'U', "$MNU_USERMAP", ld->mFont, 0, 0, NAME_UsermapMenu);
ld->mItems.Push(it);
addedVolumes++;
}
#endif
if (addedVolumes == 1)
{
ld->mAutoselect = 0;
}
if (popped) ld->mItems.Push(popped);
}
// Build skill menu
int addedSkills = 0;
desc = MenuDescriptors.CheckKey(NAME_Skillmenu);
if (desc != nullptr && (*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor)))
{
DListMenuDescriptor* ld = static_cast<DListMenuDescriptor*>(*desc);
DMenuItemBase* popped = nullptr;
if (ld->mItems.Size() && ld->mItems.Last()->IsKindOf(NAME_ListMenuItemBloodDripDrawer))
{
ld->mItems.Pop(popped);
}
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
int y = ld->mYpos;
for (int i = 0; i < MAXSKILLS; i++)
{
if (gSkillNames[i].IsNotEmpty())
{
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gSkillNames[i][0], gSkillNames[i], ld->mFont, 0, 0, NAME_Startgame, i);
y += ld->mLinespacing;
ld->mItems.Push(it);
addedSkills++;
}
}
if (addedSkills == 0)
{
// Need to add one item with the default skill so that the menu does not break.
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, 0, "", ld->mFont, 0, 0, NAME_Startgame, gDefaultSkill);
ld->mItems.Push(it);
}
if (addedSkills == 1)
{
ld->mAutoselect = 0;
}
if (popped) ld->mItems.Push(popped);
}
}
//=============================================================================
//
// Reads any XHAIRS lumps for the names of crosshairs and
// adds them to the display options menu.
//
//=============================================================================
static void InitCrosshairsList()
{
int lastlump, lump;
lastlump = 0;
FOptionValues **opt = OptionValues.CheckKey(NAME_Crosshairs);
if (opt == nullptr)
{
return; // no crosshair value list present. No need to go on.
}
FOptionValues::Pair *pair = &(*opt)->mValues[(*opt)->mValues.Reserve(1)];
pair->Value = 0;
pair->Text = "None";
while ((lump = fileSystem.FindLump("XHAIRS", &lastlump)) != -1)
{
FScanner sc(lump);
while (sc.GetNumber())
{
FOptionValues::Pair value;
value.Value = sc.Number;
sc.MustGetString();
value.Text = sc.String;
if (value.Value != 0)
{ // Check if it already exists. If not, add it.
unsigned int i;
for (i = 1; i < (*opt)->mValues.Size(); ++i)
{
if ((*opt)->mValues[i].Value == value.Value)
{
break;
}
}
if (i < (*opt)->mValues.Size())
{
(*opt)->mValues[i].Text = value.Text;
}
else
{
(*opt)->mValues.Push(value);
}
}
}
}
}
//==========================================================================
//
// Defines how graphics substitution is handled.
// 0: Never replace a text-containing graphic with a font-based text.
// 1: Always replace, regardless of any missing information. Useful for testing the substitution without providing full data.
// 2: Only replace for non-default texts, i.e. if some language redefines the string's content, use it instead of the graphic. Never replace a localized graphic.
// 3: Only replace if the string is not the default and the graphic comes from the IWAD. Never replace a localized graphic.
// 4: Like 1, but lets localized graphics pass.
//
// The default is 3, which only replaces known content with non-default texts.
//
//==========================================================================
bool CheckSkipGameOptionBlock(FScanner& sc) { return false; } // not applicable
#if 0
CUSTOM_CVAR(Int, cl_gfxlocalization, 3, CVAR_ARCHIVE)
{
if (self < 0 || self > 4) self = 0;
}
bool OkForLocalization(FTextureID texnum, const char* substitute)
{
if (!texnum.isValid()) return false;
// First the unconditional settings, 0='never' and 1='always'.
if (cl_gfxlocalization == 1 || gameinfo.forcetextinmenus) return false;
if (cl_gfxlocalization == 0 || gameinfo.forcenogfxsubstitution) return true;
return TexMan.OkForLocalization(texnum, substitute, cl_gfxlocalization);
}
#endif
void SetDefaultMenuColors()
{
PClass* cls = nullptr;
//OptionSettings.mTitleColor = CR_RED;// V_FindFontColor(gameinfo.mTitleColor);
OptionSettings.mFontColor = CR_RED;
OptionSettings.mFontColorValue = CR_GRAY;
OptionSettings.mFontColorMore = CR_GRAY;
OptionSettings.mFontColorHeader = CR_GOLD;
OptionSettings.mFontColorHighlight = CR_YELLOW;
OptionSettings.mFontColorSelection = CR_BRICK;
gameinfo.mSliderColor = "Orange";
if (g_gameType & GAMEFLAG_BLOOD)
{
OptionSettings.mFontColorHeader = CR_DARKGRAY;
OptionSettings.mFontColorHighlight = CR_WHITE;
OptionSettings.mFontColorSelection = CR_DARKRED;
gameinfo.mSliderColor = "Red";
cls = PClass::FindClass("BloodMenuDelegate");
}
else if (g_gameType & GAMEFLAG_SW)
{
OptionSettings.mFontColorHeader = CR_DARKRED;
OptionSettings.mFontColorHighlight = CR_WHITE;
gameinfo.mSliderColor = "Red";
cls = PClass::FindClass("SWMenuDelegate");
}
else if (g_gameType & GAMEFLAG_PSEXHUMED)
{
OptionSettings.mFontColorHeader = CR_LIGHTBLUE;
OptionSettings.mFontColorHighlight = CR_SAPPHIRE;
OptionSettings.mFontColorSelection = CR_ORANGE;
OptionSettings.mFontColor = CR_FIRE;
gameinfo.mSliderColor = "Yellow";
cls = PClass::FindClass("ExhumedMenuDelegate");
}
else
{
if (g_gameType & (GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI))
{
OptionSettings.mFontColor = CR_DARKGREEN;
OptionSettings.mFontColorHeader = CR_DARKGRAY;
OptionSettings.mFontColorHighlight = CR_WHITE;
OptionSettings.mFontColorSelection = CR_DARKGREEN;
gameinfo.mSliderColor = "Green";
}
else if (g_gameType & GAMEFLAG_RRALL)
{
OptionSettings.mFontColor = CR_BROWN;
OptionSettings.mFontColorHeader = CR_DARKBROWN;
OptionSettings.mFontColorHighlight = CR_ORANGE;
OptionSettings.mFontColorSelection = CR_TAN;
gameinfo.mSliderColor = "Tan";
}
cls = PClass::FindClass("DukeMenuDelegate");
}
if (!cls) cls = PClass::FindClass("RazeMenuDelegate");
if (cls) menuDelegate = cls->CreateNew();
}
void BuildGameMenus()
{
BuildEpisodeMenu();
InitCrosshairsList();
UpdateJoystickMenu(nullptr);
}
//=============================================================================
//
// [RH] Most menus can now be accessed directly
// through console commands.
//
//=============================================================================
EXTERN_CVAR(Int, screenblocks)
CCMD(reset2defaults)
{
C_SetDefaultBindings();
C_SetCVarsToDefaults();
}
CCMD(reset2saved)
{
GameConfig->DoGlobalSetup();
GameConfig->DoGameSetup(currentGame);
}
CCMD(menu_main)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1);
}
CCMD(openhelpmenu)
{
if (!help_disabled)
{
M_StartControlPanel(true);
M_SetMenu(NAME_HelpMenu);
}
}
CCMD(opensavemenu)
{
if (gi->CanSave())
{
M_StartControlPanel(true);
M_SetMenu(NAME_Savegamemenu);
}
}
CCMD(openloadmenu)
{
M_StartControlPanel(true);
M_SetMenu(NAME_Loadgamemenu);
}
// The sound system is not yet capable of resolving this properly.
DEFINE_ACTION_FUNCTION(_RazeMenuDelegate, PlaySound)
{
PARAM_SELF_STRUCT_PROLOGUE(void);
PARAM_NAME(name);
EMenuSounds soundindex;
switch (name.GetIndex())
{
case NAME_menu_cursor:
soundindex = CursorSound;
break;
case NAME_menu_choose:
soundindex = ChooseSound;
break;
case NAME_menu_backup:
soundindex = BackSound;
break;
case NAME_menu_clear:
case NAME_menu_dismiss:
soundindex = CloseSound;
break;
case NAME_menu_change:
soundindex = ChangeSound;
break;
case NAME_menu_advance:
soundindex = AdvanceSound;
break;
default:
return 0;
}
gi->MenuSound(soundindex);
return 0;
}
// C_ToggleConsole cannot be exported for security reasons as it can be used to make the engine unresponsive.
DEFINE_ACTION_FUNCTION(_RazeMenuDelegate, MenuDismissed)
{
if (CurrentMenu == nullptr && gamestate == GS_MENUSCREEN) C_ToggleConsole();
return 0;
}
DEFINE_ACTION_FUNCTION(_PlayerMenu, DrawPlayerSprite)
{
PARAM_PROLOGUE;
PARAM_INT(selected);
gi->DrawPlayerSprite(DVector2(0.,0.), selected);
return 0;
}

View file

@ -0,0 +1,41 @@
#pragma once
#include "menu.h"
#include "gamestruct.h"
#include "c_cvars.h"
#include "savegamemanager.h"
extern bool help_disabled;
void M_StartControlPanel (bool makeSound, bool scaleoverride = false);
extern FNewGameStartup NewGameStartupInfo;
void M_StartupEpisodeMenu(FNewGameStartup *gs);
void M_StartupSkillMenu(FNewGameStartup *gs);
void SetDefaultMenuColors();
void BuildGameMenus();
class FSavegameManager : public FSavegameManagerBase
{
void PerformSaveGame(const char *fn, const char *sgdesc) override;
void PerformLoadGame(const char *fn, bool) override;
FString ExtractSaveComment(FSerializer &arc) override;
FString BuildSaveName(const char* prefix, int slot) override;
void ReadSaveStrings() override;
};
extern FSavegameManager savegameManager;
enum EMenuSounds : int
{
ActivateSound,
CursorSound,
AdvanceSound,
BackSound,
CloseSound,
PageSound,
ChangeSound,
ChooseSound
};
EXTERN_CVAR(Bool, menu_sounds)

View file

@ -17,7 +17,7 @@
#define BEGIN_SW_NS namespace ShadowWarrior { #define BEGIN_SW_NS namespace ShadowWarrior {
#define END_SW_NS } #define END_SW_NS }
#define BEGIN_PS_NS namespace Powerslave { #define BEGIN_PS_NS namespace Exhumed {
#define END_PS_NS } #define END_PS_NS }
#else #else

View file

@ -53,12 +53,15 @@
#include "gamestruct.h" #include "gamestruct.h"
#include "automap.h" #include "automap.h"
#include "statusbar.h" #include "statusbar.h"
#include "gamestate.h"
#include "razemenu.h"
static CompositeSavegameWriter savewriter; static CompositeSavegameWriter savewriter;
static FResourceFile *savereader; static FResourceFile *savereader;
void LoadEngineState(); void LoadEngineState();
void SaveEngineState(); void SaveEngineState();
void WriteSavePic(FileWriter* file, int width, int height); void WriteSavePic(FileWriter* file, int width, int height);
extern FString BackupSaveGame;
CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, cl_savedir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -564,3 +567,129 @@ void LoadEngineState()
fr.Close(); fr.Close();
} }
} }
//=============================================================================
//
//
//
//=============================================================================
CVAR(Bool, saveloadconfirmation, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, autosavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextautosave = -1;
CVAR(Int, disableautosave, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Int, autosavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 1)
self = 1;
}
CVAR(Int, quicksavenum, 0, CVAR_NOSET | CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static int nextquicksave = -1;
CUSTOM_CVAR(Int, quicksavecount, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 1)
self = 1;
}
void DoLoadGame(const char* name)
{
if (OpenSaveGameForRead(name))
{
if (gi->LoadGame())
{
gameaction = ga_level;
}
else
{
I_Error("%s: Failed to load savegame", name);
}
}
else
{
I_Error("%s: Failed to open savegame", name);
}
}
void G_LoadGame(const char *filename)
{
inputState.ClearAllInput();
gi->FreeLevelData();
DoLoadGame(filename);
BackupSaveGame = filename;
}
void G_SaveGame(const char *fn, const char *desc, bool ok4q, bool forceq)
{
if (OpenSaveGameForWrite(fn, desc))
{
if (gi->SaveGame() && FinishSavegameWrite())
{
savegameManager.NotifyNewSave(fn, desc, ok4q, forceq);
Printf(PRINT_NOTIFY, "%s\n", GStrings("GAME SAVED"));
BackupSaveGame = fn;
}
}
}
void M_Autosave()
{
if (disableautosave) return;
if (!gi->CanSave()) return;
FString description;
FString file;
// Keep a rotating sets of autosaves
UCVarValue num;
const char* readableTime;
int count = autosavecount != 0 ? autosavecount : 1;
if (nextautosave == -1)
{
nextautosave = (autosavenum + 1) % count;
}
num.Int = nextautosave;
autosavenum.ForceSet(num, CVAR_Int);
auto Filename = G_BuildSaveName(FStringf("auto%04d", nextautosave));
readableTime = myasctime();
FStringf SaveTitle("Autosave %s", readableTime);
nextautosave = (nextautosave + 1) % count;
G_SaveGame(Filename, SaveTitle, false, false);
}
CCMD(autosave)
{
gameaction = ga_autosave;
}
CCMD(rotatingquicksave)
{
if (!gi->CanSave()) return;
FString description;
FString file;
// Keep a rotating sets of quicksaves
UCVarValue num;
const char* readableTime;
int count = quicksavecount != 0 ? quicksavecount : 1;
if (nextquicksave == -1)
{
nextquicksave = (quicksavenum + 1) % count;
}
num.Int = nextquicksave;
quicksavenum.ForceSet(num, CVAR_Int);
FSaveGameNode sg;
auto Filename = G_BuildSaveName(FStringf("quick%04d", nextquicksave));
readableTime = myasctime();
FStringf SaveTitle("Quicksave %s", readableTime);
nextquicksave = (nextquicksave + 1) % count;
G_SaveGame(Filename, SaveTitle, false, false);
}

View file

@ -17,8 +17,12 @@ class FileReader;
FString G_BuildSaveName (const char *prefix); FString G_BuildSaveName (const char *prefix);
int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu); int G_ValidateSavegame(FileReader &fr, FString *savetitle, bool formenu);
void G_LoadGame(const char* filename);
void G_SaveGame(const char* fn, const char* desc, bool ok4q, bool forceq);
void SaveEngineState(); void SaveEngineState();
void LoadEngineState(); void LoadEngineState();
void M_Autosave();
#define SAVEGAME_EXT ".dsave" #define SAVEGAME_EXT ".dsave"

View file

@ -43,7 +43,7 @@
#include "s_soundinternal.h" #include "s_soundinternal.h"
#include "animtexture.h" #include "animtexture.h"
#include "gamestate.h" #include "gamestate.h"
#include "menu.h" #include "razemenu.h"
#include "raze_sound.h" #include "raze_sound.h"
#include "SmackerDecoder.h" #include "SmackerDecoder.h"
#include "movie/playmve.h" #include "movie/playmve.h"

View file

@ -59,7 +59,7 @@
#include "m_fixed.h" #include "m_fixed.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "gamestruct.h" #include "gamestruct.h"
#include "menu.h" #include "razemenu.h"
#include "mapinfo.h" #include "mapinfo.h"
#include "../version.h" #include "../version.h"

View file

@ -619,6 +619,9 @@ int tileImportFromTexture(const char* fn, int tilenum, int alphacut, int istextu
if (xsiz <= 0 || ysiz <= 0) if (xsiz <= 0 || ysiz <= 0)
return -2; return -2;
// create a new game texture here - we want to give it a new name!
tex = MakeGameTexture(tex->GetTexture(), FStringf("#%05d", tilenum), ETextureType::Override);
TexMan.AddGameTexture(tex);
TileFiles.tiledata[tilenum].backup = TileFiles.tiledata[tilenum].texture = tex; TileFiles.tiledata[tilenum].backup = TileFiles.tiledata[tilenum].texture = tex;
if (istexture) if (istexture)
tileSetHightileReplacement(tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0, 0); tileSetHightileReplacement(tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0, 0);
@ -1028,6 +1031,27 @@ bool tileEqualTo(int me, int other)
// //
//=========================================================================== //===========================================================================
void tileUpdateAnimations()
{
for (int i = 0; i < MAXTILES; i++)
{
if (picanm[i].sf & PICANM_ANIMTYPE_MASK)
{
int j = i + animateoffs(i, 0);
auto id1 = TileFiles.tiledata[i].texture->GetID();
auto id2 = TileFiles.tiledata[j].texture->GetID();
TexMan.SetTranslation(id1, id2);
}
}
}
//===========================================================================
//
// Picks a texture for rendering for a given tilenum/palette combination
//
//===========================================================================
bool PickTexture(int picnum, FGameTexture* tex, int paletteid, TexturePick& pick) bool PickTexture(int picnum, FGameTexture* tex, int paletteid, TexturePick& pick)
{ {
@ -1194,3 +1218,22 @@ void processSetAnim(const char* cmd, FScriptPosition& pos, SetAnim& imp)
TileSiz tilesiz; TileSiz tilesiz;
PicAnm picanm; PicAnm picanm;
#if 0 // this only gets in if unavoidable. It'd be preferable if the script side can solely operate on texture names.
#include "vm.h"
static int GetTexture(int tile, int anim)
{
if (tile < 0 || tile >= MAXTILES) return 0;
auto tex = tileGetTexture(tile, anim);
return tex ? tex->GetID().GetIndex() : 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_TileFiles, GetTexture, GetTexture)
{
PARAM_PROLOGUE;
PARAM_INT(tile);
PARAM_BOOL(animate);
ACTION_RETURN_INT(GetTexture(tile, animate));
}
#endif

View file

@ -523,6 +523,7 @@ inline FGameTexture* tileGetTexture(int tile, bool animate = false)
} }
bool tileEqualTo(int me, int other); bool tileEqualTo(int me, int other);
void tileUpdateAnimations();
bool PickTexture(int picnum, FGameTexture* tex, int paletteid, TexturePick& pick); bool PickTexture(int picnum, FGameTexture* tex, int paletteid, TexturePick& pick);

View file

@ -30,106 +30,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gamestate.h" #include "gamestate.h"
#include "mapinfo.h" #include "mapinfo.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "v_draw.h"
#include "vm.h"
#include "menu/menu.h" // to override the local menu.h #include "razemenu.h"
#include "../../glbackend/glbackend.h" #include "../../glbackend/glbackend.h"
BEGIN_PS_NS BEGIN_PS_NS
//----------------------------------------------------------------------------
//
// Implements the native looking menu used for the main menu
// and the episode/skill selection screens, i.e. the parts
// that need to look authentic
//
//----------------------------------------------------------------------------
void menu_DoPlasma();
double zoomsize = 0;
class PSMainMenu : public DListMenu
{
void Init(DMenu* parent, FListMenuDescriptor* desc) override
{
DListMenu::Init(parent, desc);
PlayLocalSound(StaticSound[kSound31], 0, false, CHANF_UI);
}
void Ticker() override
{
// handle the menu zoom-in
if (zoomsize < 1.)
{
zoomsize += 0.0625;
if (zoomsize >= 1.) {
zoomsize = 1.;
}
}
}
void PreDraw() override
{
if (mDesc->mMenuName == NAME_Mainmenu)
menu_DoPlasma();
else
{
auto nLogoTile = EXHUMED ? kExhumedLogo : kPowerslaveLogo;
DrawRel(nLogoTile, 160, 40);
}
}
};
//----------------------------------------------------------------------------
//
// Menu related game interface functions
//
//----------------------------------------------------------------------------
void GameInterface::DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags)
{
int tilenum = (int)strtoll(text, nullptr, 0);
double y = ypos - tilesiz[tilenum].y / 2;
int8_t shade;
if (state == NIT_SelectedState)
{ // currently selected menu item
shade = Sin(I_GetBuildTime() << 4) >> 9;
}
else if (state == NIT_ActiveState) {
shade = 0;
}
else {
shade = 25;
}
// Todo: Replace the boxes with an empty one and draw the text with a font.
auto tex = tileGetTexture(tilenum);
DrawTexture(twod, tex, 160, y + tex->GetDisplayHeight(), DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffset, true, DTA_ScaleX, zoomsize, DTA_ScaleY, zoomsize,
DTA_Color, shadeToLight(shade), TAG_DONE);
// tilesizx is 51
// tilesizy is 33
if (state == NIT_SelectedState)
{
tex = tileGetTexture(kMenuCursorTile);
DrawTexture(twod, tex, 62, ypos - 12, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE);
DrawTexture(twod, tex, 207, ypos - 12, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_FlipX, true, TAG_DONE);
}
}
void GameInterface::MenuOpened() void GameInterface::MenuOpened()
{ {
GrabPalette(); GrabPalette();
zoomsize = 0; menuDelegate->FloatVar(NAME_zoomsize) = 0;
StopAllSounds();
StopLocalSound(); StopLocalSound();
} }
@ -137,17 +50,21 @@ void GameInterface::MenuSound(EMenuSounds snd)
{ {
switch (snd) switch (snd)
{ {
case CursorSound: case ActivateSound:
PlayLocalSound(StaticSound[kSound35], 0, false, CHANF_UI); PlayLocalSound(StaticSound[kSound31], 0, false, CHANF_UI);
break; break;
case AdvanceSound: case CursorSound:
case BackSound: PlayLocalSound(StaticSound[kSound35], 0, false, CHANF_UI);
PlayLocalSound(StaticSound[kSound33], 0, false, CHANF_UI); break;
break;
default: case AdvanceSound:
return; case BackSound:
PlayLocalSound(StaticSound[kSound33], 0, false, CHANF_UI);
break;
default:
return;
} }
} }
@ -156,16 +73,10 @@ void GameInterface::QuitToTitle()
gameaction = ga_mainmenu; gameaction = ga_mainmenu;
} }
void GameInterface::MenuClosed()
{
}
bool GameInterface::StartGame(FNewGameStartup& gs) bool GameInterface::StartGame(FNewGameStartup& gs)
{ {
auto map = FindMapByLevelNum(gs.Episode); auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels.
DeferedStartGame(map, gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels. DeferedStartGame(map, 1, true);
return true; return true;
} }
@ -174,26 +85,22 @@ FSavegameInfo GameInterface::GetSaveSig()
return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS }; return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS };
} }
void GameInterface::DrawMenuCaption(const DVector2& origin, const char* text)
{
// Fixme: should use the extracted font from the menu items (i.e. BigFont) and a stretched box for the menu items.
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - SmallFont->StringWidth(text)/2, 10, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
}
END_PS_NS END_PS_NS
//---------------------------------------------------------------------------- using namespace Exhumed;
//
// Class registration
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw)
static TMenuClassDescriptor<Powerslave::PSMainMenu> _mm("Exhumed.MainMenu");
void RegisterPSMenus()
{ {
menuClasses.Push(&_mm); menu_DoPlasma();
return 0;
} }
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw)
{
auto nLogoTile = EXHUMED ? kExhumedLogo : kPowerslaveLogo;
DrawRel(nLogoTile, 160, 40);
return 0;
}

View file

@ -46,7 +46,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "cheathandler.h" #include "cheathandler.h"
#include "inputstate.h" #include "inputstate.h"
#include "d_protocol.h" #include "d_protocol.h"
#include "core/menu/menu.h" #include "texturemanager.h"
#include "razemenu.h"
BEGIN_PS_NS BEGIN_PS_NS
@ -475,13 +476,26 @@ void ExitGame()
throw CExitEvent(0); throw CExitEvent(0);
} }
#define x(a, b) registerName(#a, b);
static void SetTileNames()
{
auto registerName = [](const char* name, int index)
{
TexMan.AddAlias(name, tileGetTexture(index));
};
#include "namelist.h"
}
#undef x
void GameInterface::app_init() void GameInterface::app_init()
{ {
int i; int i;
//int esi = 1; //int esi = 1;
//int edi = esi; //int edi = esi;
#if 0
help_disabled = true; help_disabled = true;
#endif
// Create the global level table. Parts of the engine need it, even though the game itself does not. // Create the global level table. Parts of the engine need it, even though the game itself does not.
for (int i = 0; i <= 32; i++) for (int i = 0; i <= 32; i++)
{ {
@ -507,6 +521,7 @@ void GameInterface::app_init()
// temp - moving InstallEngine(); before FadeOut as we use nextpage() in FadeOut // temp - moving InstallEngine(); before FadeOut as we use nextpage() in FadeOut
InstallEngine(); InstallEngine();
LoadDefinitions(); LoadDefinitions();
SetTileNames();
TileFiles.SetBackup(); TileFiles.SetBackup();

View file

@ -237,15 +237,12 @@ struct GameInterface : ::GameInterface
void app_init() override; void app_init() override;
void clearlocalinputstate() override; void clearlocalinputstate() override;
bool GenerateSavePic() override; bool GenerateSavePic() override;
void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override;
void MenuOpened() override; void MenuOpened() override;
void MenuSound(EMenuSounds snd) override; void MenuSound(EMenuSounds snd) override;
void MenuClosed() override;
bool StartGame(FNewGameStartup& gs) override; bool StartGame(FNewGameStartup& gs) override;
FSavegameInfo GetSaveSig() override; FSavegameInfo GetSaveSig() override;
void DrawMenuCaption(const DVector2& origin, const char* text) override; bool LoadGame() override;
bool LoadGame(FSaveGameNode* sv) override; bool SaveGame() override;
bool SaveGame(FSaveGameNode* sv) override;
bool CanSave() override; bool CanSave() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; } ReservedSpace GetReservedScreenSpace(int viewsize) override { return { 0, 24 }; }
void QuitToTitle() override; void QuitToTitle() override;

View file

@ -44,10 +44,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "cheathandler.h" #include "cheathandler.h"
#include "statistics.h" #include "statistics.h"
#include "g_input.h" #include "g_input.h"
#include "core/menu/menu.h" #include "razemenu.h"
#include "d_net.h" #include "d_net.h"
#include "automap.h" #include "automap.h"
#include "raze_music.h" #include "raze_music.h"
#include "v_draw.h"
BEGIN_PS_NS BEGIN_PS_NS
@ -137,7 +138,7 @@ static void Intermission(MapRecord *from_map, MapRecord *to_map)
{ {
TArray<JobDesc> jobs; TArray<JobDesc> jobs;
StopAllSounds(); if (from_map) StopAllSounds();
bCamera = false; bCamera = false;
automapMode = am_off; automapMode = am_off;

View file

@ -21,7 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "player.h" #include "player.h"
#include "status.h" #include "status.h"
#include "view.h" #include "view.h"
#include "menu.h" #include "razemenu.h"
BEGIN_PS_NS BEGIN_PS_NS

View file

@ -0,0 +1,30 @@
x(PowerslaveLogo, 3442)
x(MenuNewGameTile, 3460)
x(MenuLoadGameTile, 3461)
x(MenuMusicTile, 3465)
x(MenuSoundFxTile, 3466)
x(MenuCursorTile, 3468)
x(MenuBlank, 3469)
x(SkullHead, 3582)
x(ExhumedLogo, 3592)
x(Energy1, 3604)
x(Energy2, 3605)
x(ClockSymbol1, 3606)
x(ClockSymbol2, 3607)
x(ClockSymbol3, 3608)
x(ClockSymbol4, 3609)
x(ClockSymbol5, 3610)
x(ClockSymbol6, 3611)
x(ClockSymbol7, 3612)
x(ClockSymbol8, 3613)
x(ClockSymbol9, 3614)
x(ClockSymbol10, 3615)
x(ClockSymbol11, 3616)
x(ClockSymbol12, 3617)
x(ClockSymbol13, 3618)
x(ClockSymbol14, 3619)
x(ClockSymbol15, 3620)
x(ClockSymbol16, 3621)
x(TileLoboLaptop, 3623)
x(TileBMGLogo, 3368)
x(TilePIELogo, 3349)

View file

@ -3368,7 +3368,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3346 3346 #define kTile3346 3346
#define kTile3347 3347 #define kTile3347 3347
#define kTile3348 3348 #define kTile3348 3348
#define kTilePIELogo 3349
#define kTile3350 3350 #define kTile3350 3350
#define kTile3351 3351 #define kTile3351 3351
#define kTile3352 3352 #define kTile3352 3352
@ -3387,7 +3386,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3365 3365 // sky #define kTile3365 3365 // sky
#define kTile3366 3366 // sky #define kTile3366 3366 // sky
#define kTile3367 3367 // sky #define kTile3367 3367 // sky
#define kTileBMGLogo 3368
#define kTile3369 3369 #define kTile3369 3369
#define kTile3370 3370 #define kTile3370 3370
#define kTile3371 3371 #define kTile3371 3371
@ -3461,7 +3459,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3439 3439 #define kTile3439 3439
#define kTile3440 3440 #define kTile3440 3440
#define kTile3441 3441 #define kTile3441 3441
#define kPowerslaveLogo 3442
#define kTile3443 3443 #define kTile3443 3443
#define kTile3444 3444 #define kTile3444 3444
#define kTile3445 3445 #define kTile3445 3445
@ -3479,16 +3476,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3457 3457 #define kTile3457 3457
#define kTile3458 3458 #define kTile3458 3458
#define kTile3459 3459 #define kTile3459 3459
#define kMenuNewGameTile 3460
#define kMenuLoadGameTile 3461
#define kTile3462 3462 #define kTile3462 3462
#define kTile3463 3463 #define kTile3463 3463
#define kTile3464 3464 #define kTile3464 3464
#define kMenuMusicTile 3465
#define kMenuSoundFxTile 3466
#define kTile3467 3467 #define kTile3467 3467
#define kMenuCursorTile 3468
#define kMenuBlankTitleTile 3469
#define kTile3470 3470 #define kTile3470 3470
#define kTile3471 3471 #define kTile3471 3471
#define kTile3472 3472 #define kTile3472 3472
@ -3601,7 +3592,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3579 3579 #define kTile3579 3579
#define kTile3580 3580 #define kTile3580 3580
#define kTile3581 3581 #define kTile3581 3581
#define kSkullHead 3582
#define kTile3583 3583 #define kTile3583 3583
#define kTile3584 3584 #define kTile3584 3584
#define kTile3585 3585 #define kTile3585 3585
@ -3611,7 +3601,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3589 3589 #define kTile3589 3589
#define kTile3590 3590 #define kTile3590 3590
#define kTile3591 3591 #define kTile3591 3591
#define kExhumedLogo 3592
#define kTile3593 3593 #define kTile3593 3593
#define kTile3594 3594 #define kTile3594 3594
#define kTile3595 3595 #define kTile3595 3595
@ -3623,26 +3612,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile3601 3601 #define kTile3601 3601
#define kTile3602 3602 #define kTile3602 3602
#define kTile3603 3603 #define kTile3603 3603
#define kEnergy1 3604
#define kEnergy2 3605
#define kClockSymbol1 3606
#define kClockSymbol2 3607
#define kClockSymbol3 3608
#define kClockSymbol4 3609
#define kClockSymbol5 3610
#define kClockSymbol6 3611
#define kClockSymbol7 3612
#define kClockSymbol8 3613
#define kClockSymbol9 3614
#define kClockSymbol10 3615
#define kClockSymbol11 3616
#define kClockSymbol12 3617
#define kClockSymbol13 3618
#define kClockSymbol14 3619
#define kClockSymbol15 3620
#define kClockSymbol16 3621
#define kTile3622 3622 #define kTile3622 3622
#define kTileLoboLaptop 3623
#define kTile3624 3624 #define kTile3624 3624
#define kTile3625 3625 #define kTile3625 3625
#define kTile3626 3626 #define kTile3626 3626
@ -6164,4 +6134,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define kTile6142 6142 #define kTile6142 6142
#define kTile6143 6143 #define kTile6143 6143
BEGIN_PS_NS
#define x(a, b) k##a = b,
enum
{
#include "namelist.h"
};
#undef x
END_PS_NS
#endif #endif

View file

@ -34,14 +34,14 @@ void LoadTextureState();
static TArray<SavegameHelper*> sghelpers(TArray<SavegameHelper*>::NoInit); static TArray<SavegameHelper*> sghelpers(TArray<SavegameHelper*>::NoInit);
bool GameInterface::SaveGame(FSaveGameNode* sv) bool GameInterface::SaveGame()
{ {
for (auto sgh : sghelpers) sgh->Save(); for (auto sgh : sghelpers) sgh->Save();
SaveTextureState(); SaveTextureState();
return 1; // CHECKME return 1; // CHECKME
} }
bool GameInterface::LoadGame(FSaveGameNode* sv) bool GameInterface::LoadGame()
{ {
for (auto sgh : sghelpers) sgh->Load(); for (auto sgh : sghelpers) sgh->Load();

View file

@ -222,7 +222,6 @@ void InitFX(void)
auto& S_sfx = soundEngine->GetSounds(); auto& S_sfx = soundEngine->GetSounds();
S_sfx.Resize(1); S_sfx.Resize(1);
S_sfx[0].Clear(); S_sfx[0].lumpnum = sfx_empty;
for (size_t i = 0; i < kMaxSoundFiles; i++) for (size_t i = 0; i < kMaxSoundFiles; i++)
{ {
StaticSound[i] = LoadSound(SoundFiles[i]); StaticSound[i] = LoadSound(SoundFiles[i]);

View file

@ -369,11 +369,11 @@ public:
DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein) DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein)
{ {
if (currentLevel->flags & MI_USERMAP) if (currentLevel->flags & MI_USERMAP)
gfx_offset = RRTILE403; gfx_offset = BONUSPIC01;
else if (!isRRRA()) else if (!isRRRA())
gfx_offset = RRTILE403 + clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13); gfx_offset = BONUSPIC01 + clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13);
else else
gfx_offset = LEVELMAP + clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13); gfx_offset = LEVELMAP01 + clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13);
lastmapname = currentLevel->DisplayName(); lastmapname = currentLevel->DisplayName();
@ -544,7 +544,7 @@ public:
int Frame(uint64_t clock, bool skiprequest) int Frame(uint64_t clock, bool skiprequest)
{ {
int currentclock = int(clock * 120 / 1'000'000'000); int currentclock = int(clock * 120 / 1'000'000'000);
auto tex = tileGetTexture(RRTILE8677 + ((currentclock >> 4) & 1)); auto tex = tileGetTexture(ENDGAME + ((currentclock >> 4) & 1));
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
if (!S_CheckSoundPlaying(-1, 35) && currentclock > 15*120) return 0; // make sure it stays, even if sound is off. if (!S_CheckSoundPlaying(-1, 35) && currentclock > 15*120) return 0; // make sure it stays, even if sound is off.
if (skiprequest) if (skiprequest)

View file

@ -35,7 +35,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
#include "gamecvars.h" #include "gamecvars.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "c_bind.h" #include "c_bind.h"
#include "menu/menu.h" #include "razemenu.h"
#include "gstrings.h" #include "gstrings.h"
#include "version.h" #include "version.h"
#include "names.h" #include "names.h"
@ -46,177 +46,12 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
BEGIN_DUKE_NS BEGIN_DUKE_NS
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static void Menu_DrawBackground(const DVector2 &origin)
{
DrawTexture(twod, tileGetTexture(TILE_MENUSCREEN), origin.X + 160, origin.Y + 100, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff808080, DTA_CenterOffset, true, TAG_DONE);
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static void Menu_DrawCursor(double x, double y, double scale, bool right)
{
int mclock = I_GetBuildTime();
const int frames = isRR() ? 16 : 7;
int picnum;
if (!right) picnum = TILE_SPINNINGNUKEICON + ((mclock >> 3) % frames);
else picnum = TILE_SPINNINGNUKEICON + frames - 1 - ((frames - 1 + (mclock >> 3)) % frames);
int light = 231 + (calcSinTableValue(mclock<<5) / 768.);
PalEntry pe(255, light, light, light);
DrawTexture(twod, tileGetTexture(picnum), x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_Color, pe, DTA_CenterOffsetRel, true, TAG_DONE);
}
//----------------------------------------------------------------------------
//
// Implements the native looking menu used for the main menu
// and the episode/skill selection screens, i.e. the parts
// that need to look authentic
//
//----------------------------------------------------------------------------
class DukeListMenu : public DListMenu
{
using Super = DListMenu;
protected:
void Ticker() override
{
// Lay out the menu.
int y_upper = mDesc->mYpos;
int y_lower = y_upper + mDesc->mYbotton;
int y = 0;
int spacing = 0;
const int height = 15; // cannot take value from the font because it would be inconsistent
int totalheight = 0, numvalidentries = mDesc->mItems.Size();
for (unsigned e = 0; e < mDesc->mItems.Size(); ++e)
{
auto entry = mDesc->mItems[e];
entry->mHidden = false;
entry->SetHeight(height);
totalheight += height;
}
if (mDesc->mSpacing <= 0) spacing = std::max(0, (y_lower - y_upper - totalheight) / (numvalidentries > 1 ? numvalidentries - 1 : 1));
if (spacing <= 0) spacing = mDesc->mSpacing;
int totalHeight;
for (unsigned e = 0; e < mDesc->mItems.Size(); ++e)
{
auto entry = mDesc->mItems[e];
if (!entry->mHidden)
{
entry->SetY(y_upper + y);
y += height;
totalHeight = y;
y += spacing;
}
}
}
};
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
class DukeMainMenu : public DukeListMenu
{
virtual void Init(DMenu* parent = nullptr, FListMenuDescriptor* desc = nullptr) override
{
DukeListMenu::Init(parent, desc);
}
void PreDraw() override
{
DukeListMenu::PreDraw();
double x = origin.X + 160;
if (isRRRA())
{
DrawTexture(twod, tileGetTexture(TILE_THREEDEE), x-5, origin.Y+57, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_ScaleX, 0.253, DTA_ScaleY, 0.253, DTA_CenterOffsetRel, true, TAG_DONE);
}
else if (isRR())
{
DrawTexture(twod, tileGetTexture(TILE_INGAMEDUKETHREEDEE), x+5, origin.Y + 24, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_ScaleX, 0.36, DTA_ScaleY, 0.36, DTA_CenterOffsetRel, true, TAG_DONE);
}
else
{
DrawTexture(twod, tileGetTexture(TILE_INGAMEDUKETHREEDEE), x, origin.Y + 29, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, TAG_DONE);
if (PLUTOPAK)
{
int mclock = I_GetBuildTime();
int light = 223 + (calcSinTableValue(mclock<<4) / 512.);
PalEntry pe(255, light, light, light);
DrawTexture(twod, tileGetTexture(TILE_PLUTOPAKSPRITE + 2), x + 100, origin.Y + 36, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_Color, pe, DTA_CenterOffsetRel, true, TAG_DONE);
}
}
}
};
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// Menu related game interface functions // Menu related game interface functions
// //
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void GameInterface::DrawNativeMenuText(int fontnum, int state, double oxpos, double ypos, float fontscale, const char* text, int flags)
{
double xpos = oxpos;
int trans;
PalEntry pe;
double scale = isRR() ? 0.4 : 1.;
if (flags & LMF_Centered) xpos -= BigFont->StringWidth(text) * scale * 0.5;
if (state == NIT_InactiveState)
{
trans = TRANSLATION(Translation_Remap, 1);
pe = 0xffffffff;
}
else if (state == NIT_SelectedState)
{
trans = 0;
int mclock = I_GetBuildTime();
int light = 231 + (calcSinTableValue(mclock<<5) / 768.);
pe = PalEntry(255, light, light, light);
}
else
{
trans = 0;
pe = 0xffa0a0a0;
}
DrawText(twod, BigFont, CR_UNDEFINED, xpos, ypos, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_Color, pe,
DTA_TranslationIndex, trans, TAG_DONE);
if (state == NIT_SelectedState)
{
const int cursorOffset = 110;
const double cursorScale = isRR() ? 0.2 : 1.0;
const double ymid = ypos + 7; // half height must be hardcoded or layouts will break.
if (flags & LMF_Centered)
{
Menu_DrawCursor(oxpos + cursorOffset, ymid, cursorScale, false);
Menu_DrawCursor(oxpos - cursorOffset, ymid, cursorScale, true);
}
else
Menu_DrawCursor(oxpos - cursorOffset, ymid, cursorScale, false);
}
}
void GameInterface::MenuOpened() void GameInterface::MenuOpened()
{ {
StopCommentary(); StopCommentary();
@ -253,10 +88,6 @@ void GameInterface::MenuSound(EMenuSounds snd)
} }
} }
void GameInterface::MenuClosed()
{
}
bool GameInterface::CanSave() bool GameInterface::CanSave()
{ {
if (ud.recstat == 2 || gamestate != GS_LEVEL) return false; if (ud.recstat == 2 || gamestate != GS_LEVEL) return false;
@ -270,7 +101,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
{ {
if (g_gameType & GAMEFLAG_SHAREWARE) if (g_gameType & GAMEFLAG_SHAREWARE)
{ {
M_StartMessage(GStrings("BUYDUKE"), 1, -1); M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None);
return false; return false;
} }
} }
@ -295,6 +126,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
} }
Net_ClearFifo(); Net_ClearFifo();
} }
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level)); auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
if (map) if (map)
{ {
@ -310,30 +142,6 @@ FSavegameInfo GameInterface::GetSaveSig()
return { SAVESIG_DN3D, MINSAVEVER_DN3D, SAVEVER_DN3D }; return { SAVESIG_DN3D, MINSAVEVER_DN3D, SAVEVER_DN3D };
} }
void GameInterface::DrawMenuCaption(const DVector2& origin, const char* text)
{
DrawTexture(twod, tileGetTexture(TILE_MENUBAR), origin.X + 160, origin.Y + 19, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_Color, 0xff808080, DTA_CenterOffsetRel, 1, TAG_DONE);
FString t = text;
size_t newlen = t.Len();
if (t[t.Len() - 1] == ':') newlen--;
if (newlen > 63) newlen = 63;
t.Truncate(newlen);
double scale = isRR() ? 0.4 : 1.0;
double x = 160 + origin.X - BigFont->StringWidth(t) * scale * 0.5;
DrawText(twod, BigFont, CR_UNTRANSLATED, x, origin.Y + 12, t, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
}
void GameInterface::DrawCenteredTextScreen(const DVector2 &origin, const char *text, int position, bool bg)
{
if (bg) Menu_DrawBackground(origin);
else if (!isRR())
{
Menu_DrawCursor(160, 130, 1, false);
}
::GameInterface::DrawCenteredTextScreen(origin, text, position, bg);
}
void GameInterface::DrawPlayerSprite(const DVector2& origin, bool onteam) void GameInterface::DrawPlayerSprite(const DVector2& origin, bool onteam)
{ {
int mclock = I_GetBuildTime(); int mclock = I_GetBuildTime();
@ -352,190 +160,5 @@ void GameInterface::QuitToTitle()
gameaction = ga_startup; gameaction = ga_startup;
} }
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
static void shadowminitext(int32_t xx, int32_t yy, const char* t, int32_t p)
{
double x = FixedToFloat(xx);
double y = FixedToFloat(yy);
DrawText(twod, SmallFont2, CR_UNDEFINED, x + 1, y + 1, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, 0.5, TAG_DONE);
DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, p), TAG_DONE);
}
static void mgametextcenter(int32_t xx, int32_t yy, const char* t)
{
double x = FixedToFloat(xx) + 160. - SmallFont->StringWidth(t) * 0.5;
double y = FixedToFloat(yy);
DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
}
//----------------------------------------------------------------------------
//
// allows the front end to override certain fullscreen image menus
// with custom implementations.
//
// This is needed because the credits screens in Duke Nukem
// are either done by providing an image or by printing text, based on the version used.
//
//----------------------------------------------------------------------------
bool GameInterface::DrawSpecialScreen(const DVector2& origin, int tilenum)
{
// Older versions of Duke Nukem create the credits screens manually.
// On the latest version there's real graphics for this.
bool haveCredits = !(g_gameType & GAMEFLAG_DUKE) || (VOLUMEALL && PLUTOPAK);
int32_t m, l;
if (!haveCredits)
{
if (tilenum == 2504)
{
Menu_DrawBackground(origin);
DrawMenuCaption(origin, GStrings("MNU_CREDITS"));
m = int(origin.X * 65536) + (20 << 16);
l = int(origin.Y * 65536) + (33 << 16);
shadowminitext(m, l, "Original Concept", 12); l += 7 << 16;
shadowminitext(m, l, "Todd Replogle and Allen H. Blum III", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Produced & Directed By", 12); l += 7 << 16;
shadowminitext(m, l, "Greg Malone", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Executive Producer", 12); l += 7 << 16;
shadowminitext(m, l, "George Broussard", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "BUILD Engine", 12); l += 7 << 16;
shadowminitext(m, l, "Ken Silverman", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Game Programming", 12); l += 7 << 16;
shadowminitext(m, l, "Todd Replogle", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "3D Engine/Tools/Net", 12); l += 7 << 16;
shadowminitext(m, l, "Ken Silverman", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Network Layer/Setup Program", 12); l += 7 << 16;
shadowminitext(m, l, "Mark Dochtermann", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Map Design", 12); l += 7 << 16;
shadowminitext(m, l, "Allen H. Blum III", 12); l += 7 << 16;
shadowminitext(m, l, "Richard Gray", 12); l += 7 << 16;
m = int(origin.X * 65536) + (180 << 16);
l = int(origin.Y * 65536) + (33 << 16);
shadowminitext(m, l, "3D Modeling", 12); l += 7 << 16;
shadowminitext(m, l, "Chuck Jones", 12); l += 7 << 16;
shadowminitext(m, l, "Sapphire Corporation", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Artwork", 12); l += 7 << 16;
shadowminitext(m, l, "Dirk Jones, Stephen Hornback", 12); l += 7 << 16;
shadowminitext(m, l, "James Storey, David Demaret", 12); l += 7 << 16;
shadowminitext(m, l, "Douglas R. Wood", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Sound Engine", 12); l += 7 << 16;
shadowminitext(m, l, "Jim Dose", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Sound & Music Development", 12); l += 7 << 16;
shadowminitext(m, l, "Robert Prince", 12); l += 7 << 16;
shadowminitext(m, l, "Lee Jackson", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Voice Talent", 12); l += 7 << 16;
shadowminitext(m, l, "Lani Minella - Voice Producer", 12); l += 7 << 16;
shadowminitext(m, l, "Jon St. John as \"Duke Nukem\"", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Graphic Design", 12); l += 7 << 16;
shadowminitext(m, l, "Packaging, Manual, Ads", 12); l += 7 << 16;
shadowminitext(m, l, "Robert M. Atkins", 12); l += 7 << 16;
shadowminitext(m, l, "Michael Hadwin", 12); l += 7 << 16;
return true;
}
else if (tilenum == 2505)
{
Menu_DrawBackground(origin);
DrawMenuCaption(origin, GStrings("MNU_CREDITS"));
m = int(origin.X * 65536) + (20 << 16);
l = int(origin.Y * 65536) + (33 << 16);
shadowminitext(m, l, "Special Thanks To", 12); l += 7 << 16;
shadowminitext(m, l, "Steven Blackburn, Tom Hall", 12); l += 7 << 16;
shadowminitext(m, l, "Scott Miller, Joe Siegler", 12); l += 7 << 16;
shadowminitext(m, l, "Terry Nagy, Colleen Compton", 12); l += 7 << 16;
shadowminitext(m, l, "HASH, Inc., FormGen, Inc.", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "The 3D Realms Beta Testers", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Nathan Anderson, Wayne Benner", 12); l += 7 << 16;
shadowminitext(m, l, "Glenn Brensinger, Rob Brown", 12); l += 7 << 16;
shadowminitext(m, l, "Erik Harris, Ken Heckbert", 12); l += 7 << 16;
shadowminitext(m, l, "Terry Herrin, Greg Hively", 12); l += 7 << 16;
shadowminitext(m, l, "Hank Leukart, Eric Baker", 12); l += 7 << 16;
shadowminitext(m, l, "Jeff Rausch, Kelly Rogers", 12); l += 7 << 16;
shadowminitext(m, l, "Mike Duncan, Doug Howell", 12); l += 7 << 16;
shadowminitext(m, l, "Bill Blair", 12); l += 7 << 16;
m = int(origin.X * 65536) + (160 << 16);
l = int(origin.Y * 65536) + (33 << 16);
shadowminitext(m, l, "Company Product Support", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "The following companies were cool", 12); l += 7 << 16;
shadowminitext(m, l, "enough to give us lots of stuff", 12); l += 7 << 16;
shadowminitext(m, l, "during the making of Duke Nukem 3D.", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Altec Lansing Multimedia", 12); l += 7 << 16;
shadowminitext(m, l, "for tons of speakers and the", 12); l += 7 << 16;
shadowminitext(m, l, "THX-licensed sound system.", 12); l += 7 << 16;
shadowminitext(m, l, "For info call 1-800-548-0620", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Creative Labs, Inc.", 12); l += 7 << 16;
l += 3 << 16;
shadowminitext(m, l, "Thanks for the hardware, guys.", 12); l += 7 << 16;
return true;
}
else if (tilenum == 2506)
{
Menu_DrawBackground(origin);
DrawMenuCaption(origin, GStrings("MNU_CREDITS"));
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (50 << 16), "Duke Nukem 3D is a trademark of");
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (59 << 16), "3D Realms Entertainment");
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (77 << 16), "Duke Nukem 3D");
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (86 << 16), "(C) 1996 3D Realms Entertainment");
if (VOLUMEONE)
{
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (106 << 16), "Please read LICENSE.DOC for shareware");
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + (115 << 16), "distribution grants and restrictions.");
}
mgametextcenter(int(origin.X * 65536), int(origin.Y * 65536) + ((VOLUMEONE ? 134 : 115) << 16), "Made in Dallas, Texas USA");
return true;
}
}
return false;
}
END_DUKE_NS END_DUKE_NS
//----------------------------------------------------------------------------
//
// Class registration
//
//----------------------------------------------------------------------------
static TMenuClassDescriptor<Duke3d::DukeMainMenu> _mm("Duke.MainMenu");
static TMenuClassDescriptor<Duke3d::DukeListMenu> _lm("Duke.ListMenu");
static TMenuClassDescriptor<DImageScrollerMenu> _ism("Duke.ImageScrollerMenu"); // does not implement a new class, we only need the descriptor.
void RegisterDuke3dMenus()
{
menuClasses.Push(&_mm);
menuClasses.Push(&_lm);
menuClasses.Push(&_ism);
}

View file

@ -252,7 +252,6 @@ int TILE_CAMCORNER;
int TILE_CAMLIGHT; int TILE_CAMLIGHT;
int TILE_STATIC; int TILE_STATIC;
int TILE_BOTTOMSTATUSBAR; int TILE_BOTTOMSTATUSBAR;
int TILE_SPINNINGNUKEICON;
int TILE_THREEDEE; int TILE_THREEDEE;
int TILE_INGAMEDUKETHREEDEE; int TILE_INGAMEDUKETHREEDEE;
int TILE_PLUTOPAKSPRITE; int TILE_PLUTOPAKSPRITE;

Some files were not shown because too many files have changed in this diff Show more