diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7a9eafd8..c0029ca3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -922,15 +922,12 @@ set( FASTMATH_PCH_SOURCES intermission/intermission.cpp intermission/intermission_parse.cpp menu/joystickmenu.cpp - menu/listmenu.cpp menu/loadsavemenu.cpp menu/menu.cpp menu/menudef.cpp - menu/menuinput.cpp menu/messagebox.cpp menu/optionmenu.cpp menu/playermenu.cpp - menu/readthis.cpp menu/videomenu.cpp oplsynth/fmopl.cpp oplsynth/mlopl.cpp diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index cd48e304e..6430d95ad 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -207,7 +207,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt) { // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; PARAM_INT(val); UCVarValue v; v.Int = val; @@ -219,7 +219,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetFloat) { // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; PARAM_FLOAT(val); UCVarValue v; v.Float = (float)val; @@ -231,7 +231,7 @@ DEFINE_ACTION_FUNCTION(_CVar, SetString) { // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && DMenu::CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; PARAM_STRING(val); UCVarValue v; v.String = val.GetChars(); diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 343903495..184f33adb 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -666,7 +666,7 @@ void C_DoCommand (const char *cmd, int keynum) // This is only accessible to the special menu item to run CCMDs. DEFINE_ACTION_FUNCTION(DOptionMenuItemCommand, DoCommand) { - if (DMenu::CurrentMenu == nullptr) return 0; + if (CurrentMenu == nullptr) return 0; PARAM_PROLOGUE; PARAM_STRING(cmd); C_DoCommand(cmd); diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index f7fb94510..471c02cc8 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -95,23 +95,6 @@ char *copystring (const char *s) return b; } -//============================================================================ -// -// ncopystring -// -// If the string has no content, returns NULL. Otherwise, returns a copy. -// -//============================================================================ - -char *ncopystring (const char *string) -{ - if (string == NULL || string[0] == 0) - { - return NULL; - } - return copystring (string); -} - //========================================================================== // // ReplaceString diff --git a/src/cmdlib.h b/src/cmdlib.h index 6e9fcd622..7af4e630b 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -38,7 +38,6 @@ int ParseHex(const char *str, FScriptPosition *sc = nullptr); bool IsNum (const char *str); // [RH] added char *copystring(const char *s); -char *ncopystring(const char *s); void ReplaceString (char **ptr, const char *str); bool CheckWildcards (const char *pattern, const char *text); diff --git a/src/d_net.cpp b/src/d_net.cpp index 0985cf41c..a480ae0ae 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -73,7 +73,7 @@ EXTERN_CVAR (Int, autosavecount) #define SIMULATEERRORS 0 extern BYTE *demo_p; // [RH] Special "ticcmds" get recorded in demos -extern char savedescription[SAVESTRINGSIZE]; +extern FString savedescription; extern FString savegamefile; extern short consistancy[MAXPLAYERS][BACKUPTICS]; @@ -2418,8 +2418,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) savegamefile = s; delete[] s; s = ReadString (stream); - memset (savedescription, 0, sizeof(savedescription)); - strncpy (savedescription, s, sizeof(savedescription)); + savedescription = s; if (player != consoleplayer) { // Paths sent over the network will be valid for the system that sent diff --git a/src/g_game.cpp b/src/g_game.cpp index dcd36b041..e5a48c245 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -226,7 +226,7 @@ int mousex; int mousey; FString savegamefile; -char savedescription[SAVESTRINGSIZE]; +FString savedescription; // [RH] Name of screenshot file to generate (usually NULL) FString shotfile; @@ -1081,7 +1081,7 @@ void G_Ticker () G_DoSaveGame (true, savegamefile, savedescription); gameaction = ga_nothing; savegamefile = ""; - savedescription[0] = '\0'; + savedescription = ""; break; case ga_autosave: G_DoAutoSave (); @@ -2068,8 +2068,7 @@ void G_SaveGame (const char *filename, const char *description) else { savegamefile = filename; - strncpy (savedescription, description, sizeof(savedescription)-1); - savedescription[sizeof(savedescription)-1] = '\0'; + savedescription = description; sendsave = true; } } @@ -2119,7 +2118,7 @@ extern void P_CalcHeight (player_t *); void G_DoAutoSave () { - char description[SAVESTRINGSIZE]; + FString description; FString file; // Keep up to four autosaves at a time UCVarValue num; @@ -2147,10 +2146,7 @@ void G_DoAutoSave () } readableTime = myasctime (); - strcpy (description, "Autosave "); - strncpy (description+9, readableTime+4, 12); - description[9+12] = 0; - + description.Format("Autosave %.12s", readableTime + 4); G_DoSaveGame (false, file, description); } @@ -2310,7 +2306,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio WriteZip(filename, savegame_filenames, savegame_content); - savegameManager.NotifyNewSave (filename.GetChars(), description, okForQuicksave); + savegameManager.NotifyNewSave (filename, description, okForQuicksave); // delete the JSON buffers we created just above. Everything else will // either still be needed or taken care of automatically. diff --git a/src/g_level.cpp b/src/g_level.cpp index ef49a3680..333d133cf 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1462,6 +1462,7 @@ void G_InitLevelLocals () level.LevelName = level.info->LookupLevelName(); level.NextMap = info->NextMap; level.NextSecretMap = info->NextSecretMap; + level.F1Pic = info->F1Pic; compatflags.Callback(); compatflags2.Callback(); @@ -1910,6 +1911,7 @@ DEFINE_FIELD(FLevelLocals, LevelName) DEFINE_FIELD(FLevelLocals, MapName) DEFINE_FIELD(FLevelLocals, NextMap) DEFINE_FIELD(FLevelLocals, NextSecretMap) +DEFINE_FIELD(FLevelLocals, F1Pic) DEFINE_FIELD(FLevelLocals, maptype) DEFINE_FIELD(FLevelLocals, Music) DEFINE_FIELD(FLevelLocals, musicorder) diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 357e2e154..1b3d1dba2 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -25,6 +25,7 @@ struct FLevelLocals FString MapName; // the lump name (E1M1, MAP01, etc) FString NextMap; // go here when using the regular exit FString NextSecretMap; // map to go to when used secret exit + FString F1Pic; EMapType maptype; TStaticArray vertexes; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 6f2636337..f83e18ece 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1970,6 +1970,7 @@ static void ClearMapinfo() DefaultSkill = -1; DeinitIntermissions(); level.info = NULL; + level.F1Pic = ""; } //========================================================================== diff --git a/src/gi.cpp b/src/gi.cpp index b05000d01..247036ea7 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -51,6 +51,7 @@ DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon1) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, ArmorIcon2) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, gametype) DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, norandomplayerclass) +DEFINE_FIELD_X(GameInfoStruct, gameinfo_t, infoPages) const char *GameNames[17] = diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index 9b16b90f4..59b3ad990 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -186,12 +186,12 @@ void UpdateJoystickMenu(IJoystickConfig *selected) { opt->mSelectedItem = opt->mItems.Size() - 1; } - opt->CalcIndent(); + //opt->CalcIndent(); // If the joystick config menu is open, close it if the device it's open for is gone. - if (DMenu::CurrentMenu != nullptr && (DMenu::CurrentMenu->IsKindOf("JoystickConfigMenu"))) + if (CurrentMenu != nullptr && (CurrentMenu->IsKindOf("JoystickConfigMenu"))) { - auto p = DMenu::CurrentMenu->PointerVar("mJoy"); + auto p = CurrentMenu->PointerVar("mJoy"); if (p != nullptr) { unsigned i; @@ -204,7 +204,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected) } if (i == Joysticks.Size()) { - DMenu::CurrentMenu->Close(); + CurrentMenu->Close(); } } } diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp deleted file mode 100644 index 18c627359..000000000 --- a/src/menu/listmenu.cpp +++ /dev/null @@ -1,281 +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_video.h" -#include "v_font.h" -#include "cmdlib.h" -#include "gstrings.h" -#include "g_level.h" -#include "gi.h" -#include "d_gui.h" -#include "d_event.h" -#include "menu/menu.h" - -IMPLEMENT_CLASS(DListMenu, false, false) - -IMPLEMENT_POINTERS_START(DListMenu) -IMPLEMENT_POINTER(mFocusControl) -IMPLEMENT_POINTERS_END - -//============================================================================= -// -// -// -//============================================================================= - -DListMenu::DListMenu(DMenu *parent, DListMenuDescriptor *desc) -: DMenu(parent) -{ - mDesc = NULL; - if (desc != NULL) Init(parent, desc); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenu::Init(DMenu *parent, DListMenuDescriptor *desc) -{ - mParentMenu = parent; - GC::WriteBarrier(this, parent); - mDesc = desc; - if (desc->mCenter) - { - int center = 160; - for(unsigned i=0;imItems.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;imItems.Size(); i++) - { - int width = mDesc->mItems[i]->GetWidth(); - - if (width > 0) - { - mDesc->mItems[i]->SetX(center); - } - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -DMenuItemBase *DListMenu::GetItem(FName name) -{ - for(unsigned i=0;imItems.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; - S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - } - } - for(int i = 0; i < mDesc->mSelectedItem; i++) - { - if (mDesc->mItems[i]->CheckHotkey(ch)) - { - mDesc->mSelectedItem = i; - S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - } - } - } - } - return Super::Responder(ev); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DListMenu::MenuEvent (int mkey, bool fromcontroller) -{ - int oldSelect = mDesc->mSelectedItem; - int startedAt = mDesc->mSelectedItem; - if (startedAt < 0) startedAt = 0; - - 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); - if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - 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); - if (mDesc->mSelectedItem == startedAt) mDesc->mSelectedItem = oldSelect; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - - case MKEY_Enter: - if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate()) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - } - return true; - - default: - return Super::MenuEvent(mkey, fromcontroller); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DListMenu::MouseEvent(int type, int x, int y) -{ - 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; - - 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;imItems.Size(); i++) - { - if (mDesc->mItems[i]->CheckCoordinate(x, y)) - { - if ((int)i != mDesc->mSelectedItem) - { - //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - mDesc->mSelectedItem = i; - 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;imItems.Size(); i++) - { - mDesc->mItems[i]->Ticker(); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenu::Drawer () -{ - for(unsigned i=0;imItems.Size(); i++) - { - if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(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); - Super::Drawer(); -} - -//============================================================================= -// -// base class for menu items -// -//============================================================================= -IMPLEMENT_CLASS(DMenuItemBase, false, false) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index ec92a591f..fdca9abc8 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -49,18 +49,8 @@ #include "serializer.h" #include "resourcefiles/resourcefile.h" - -//============================================================================= -// -// This should remain native because exposing 'remove' would allow -// the creation of actual malware mods! -// -//============================================================================= - -void SavegameManager::DeleteEntry(int Selected) -{ - remove(SaveGames[Selected]->Filename.GetChars()); -} +// Save name length limit for old binary formats. +#define OLDSAVESTRINGSIZE 24 //============================================================================= // @@ -68,7 +58,7 @@ void SavegameManager::DeleteEntry(int Selected) // //============================================================================= -void SavegameManager::ClearSaveGames() +void FSavegameManager::ClearSaveGames() { for (unsigned i = 0; ibNoDelete ? index - 1 : index; + if (listindex < 0) return index; + + remove(SaveGames[index]->Filename.GetChars()); + UnloadSaveData(); + FSaveGameNode *file = SaveGames[index]; if (quickSaveSlot == SaveGames[index]) @@ -93,18 +94,33 @@ int SavegameManager::RemoveSaveSlot(int index) quickSaveSlot = nullptr; } if (!file->bNoDelete) delete file; + + if (LastSaved == listindex) LastSaved = -1; + else if (LastSaved > listindex) LastSaved--; + if (LastAccessed == listindex) LastAccessed = -1; + else if (LastAccessed > listindex) LastAccessed--; + SaveGames.Delete(index); if ((unsigned)index >= SaveGames.Size()) index--; + ExtractSaveData(index); return index; } +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveSaveSlot) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + ACTION_RETURN_INT(self->RemoveSaveSlot(sel)); +} + + //============================================================================= // // // //============================================================================= -int SavegameManager::InsertSaveNode(FSaveGameNode *node) +int FSavegameManager::InsertSaveNode(FSaveGameNode *node) { if (SaveGames.Size() == 0) { @@ -120,8 +136,7 @@ int SavegameManager::InsertSaveNode(FSaveGameNode *node) unsigned int i; for (i = 0; i < SaveGames.Size(); i++) { - if (SaveGames[i]->bOldVersion || - stricmp(node->Title, SaveGames[i]->Title) <= 0) + if (SaveGames[i]->bOldVersion || node->SaveTitle.CompareNoCase(SaveGames[i]->SaveTitle) <= 0) { break; } @@ -139,7 +154,7 @@ int SavegameManager::InsertSaveNode(FSaveGameNode *node) // //============================================================================= -void SavegameManager::ReadSaveStrings() +void FSavegameManager::ReadSaveStrings() { if (SaveGames.Size() == 0) { @@ -212,7 +227,7 @@ void SavegameManager::ReadSaveStrings() node->Filename = filepath; node->bOldVersion = oldVer; node->bMissingWads = missing; - strncpy(node->Title, title.GetChars(), SAVESTRINGSIZE); + node->SaveTitle = title; InsertSaveNode(node); delete savegame; } @@ -225,7 +240,7 @@ void SavegameManager::ReadSaveStrings() { PNGHandle *png; char sig[16]; - char title[SAVESTRINGSIZE + 1]; + char title[OLDSAVESTRINGSIZE + 1]; bool oldVer = true; bool addIt = false; bool missing = false; @@ -237,7 +252,7 @@ void SavegameManager::ReadSaveStrings() // Old savegame versions are always added to the menu so // the user can easily delete them if desired. - title[SAVESTRINGSIZE] = 0; + title[OLDSAVESTRINGSIZE] = 0; if (nullptr != (png = M_VerifyPNG(file))) @@ -246,9 +261,9 @@ void SavegameManager::ReadSaveStrings() if (ver != nullptr) { // An old version - if (!M_GetPNGText(png, "Title", title, SAVESTRINGSIZE)) + if (!M_GetPNGText(png, "Title", title, OLDSAVESTRINGSIZE)) { - strncpy(title, I_FindName(&c_file), SAVESTRINGSIZE); + strncpy(title, I_FindName(&c_file), OLDSAVESTRINGSIZE); } addIt = true; delete[] ver; @@ -263,7 +278,7 @@ void SavegameManager::ReadSaveStrings() if (strncmp(sig, "ZDOOMSAVE", 9) == 0) { - if (fread(title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) + if (fread(title, 1, OLDSAVESTRINGSIZE, file) == OLDSAVESTRINGSIZE) { addIt = true; } @@ -271,7 +286,7 @@ void SavegameManager::ReadSaveStrings() else { memcpy(title, sig, 16); - if (fread(title + 16, 1, SAVESTRINGSIZE - 16, file) == SAVESTRINGSIZE - 16 && + if (fread(title + 16, 1, OLDSAVESTRINGSIZE - 16, file) == OLDSAVESTRINGSIZE - 16 && fread(sig, 1, 16, file) == 16 && strncmp(sig, "ZDOOMSAVE", 9) == 0) { @@ -287,7 +302,7 @@ void SavegameManager::ReadSaveStrings() node->Filename = filepath; node->bOldVersion = true; node->bMissingWads = false; - memcpy(node->Title, title, SAVESTRINGSIZE); + node->SaveTitle = title; InsertSaveNode(node); } fclose(file); @@ -299,17 +314,25 @@ void SavegameManager::ReadSaveStrings() } } +DEFINE_ACTION_FUNCTION(FSavegameManager, ReadSaveStrings) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->ReadSaveStrings(); + return 0; +} + + //============================================================================= // // // //============================================================================= -void SavegameManager::NotifyNewSave(const char *file, const char *title, bool okForQuicksave) +void FSavegameManager::NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave) { FSaveGameNode *node; - if (file == nullptr) + if (file.IsEmpty()) return; ReadSaveStrings(); @@ -324,7 +347,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok if (node->Filename.CompareNoCase(file) == 0) #endif { - strcpy(node->Title, title); + node->SaveTitle = title; node->bOldVersion = false; node->bMissingWads = false; if (okForQuicksave) @@ -337,7 +360,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok } node = new FSaveGameNode; - strcpy(node->Title, title); + node->SaveTitle = title; node->Filename = file; node->bOldVersion = false; node->bMissingWads = false; @@ -356,7 +379,7 @@ void SavegameManager::NotifyNewSave(const char *file, const char *title, bool ok // //============================================================================= -void SavegameManager::LoadSavegame(int Selected) +void FSavegameManager::LoadSavegame(int Selected) { G_LoadGame(SaveGames[Selected]->Filename.GetChars(), true); if (quickSaveSlot == (FSaveGameNode*)1) @@ -368,13 +391,21 @@ void SavegameManager::LoadSavegame(int Selected) LastAccessed = Selected; } +DEFINE_ACTION_FUNCTION(FSavegameManager, LoadSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + self->LoadSavegame(sel); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::DoSave(int Selected, const char *savegamestring) +void FSavegameManager::DoSave(int Selected, const char *savegamestring) { if (Selected != 0) { @@ -404,17 +435,38 @@ void SavegameManager::DoSave(int Selected, const char *savegamestring) V_SetBorderNeedRefresh(); } +DEFINE_ACTION_FUNCTION(FSavegameManager, DoSave) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + PARAM_STRING(name); + self->DoSave(sel, name); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::ExtractSaveData(int index) +unsigned FSavegameManager::ExtractSaveData(int index) { FResourceFile *resf; FSaveGameNode *node; + if (index == -1) + { + if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) + { + index = LastSaved + 1; + } + else + { + index = LastAccessed < 0? 0 : LastAccessed; + } + } + UnloadSaveData(); if ((unsigned)index < SaveGames.Size() && @@ -427,7 +479,7 @@ void SavegameManager::ExtractSaveData(int index) if (info == nullptr) { // this should not happen because the file has already been verified. - return; + return index; } void *data = info->CacheLump(); FSerializer arc; @@ -477,6 +529,14 @@ void SavegameManager::ExtractSaveData(int index) } delete resf; } + return index; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, ExtractSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(sel); + ACTION_RETURN_INT(self->ExtractSaveData(sel)); } //============================================================================= @@ -485,7 +545,7 @@ void SavegameManager::ExtractSaveData(int index) // //============================================================================= -void SavegameManager::UnloadSaveData() +void FSavegameManager::UnloadSaveData() { if (SavePic != nullptr) { @@ -506,13 +566,20 @@ void SavegameManager::UnloadSaveData() SavePicData.Clear(); } +DEFINE_ACTION_FUNCTION(FSavegameManager, UnloadSaveData) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->UnloadSaveData(); + return 0; +} + //============================================================================= // // // //============================================================================= -void SavegameManager::ClearSaveStuff() +void FSavegameManager::ClearSaveStuff() { UnloadSaveData(); if (quickSaveSlot == (FSaveGameNode*)1) @@ -521,18 +588,11 @@ void SavegameManager::ClearSaveStuff() } } - -//============================================================================= -// -// -// -//============================================================================= - -bool SavegameManager::DrawSavePic(int x, int y, int w, int h) +DEFINE_ACTION_FUNCTION(FSavegameManager, ClearSaveStuff) { - if (SavePic == nullptr) return false; - screen->DrawTexture(SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); - return true; + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->ClearSaveStuff(); + return 0; } //============================================================================= @@ -541,7 +601,68 @@ bool SavegameManager::DrawSavePic(int x, int y, int w, int h) // //============================================================================= -void SavegameManager::SetFileInfo(int Selected) +bool FSavegameManager::DrawSavePic(int x, int y, int w, int h) +{ + if (SavePic == nullptr) return false; + screen->DrawTexture(SavePic, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_Masked, false, TAG_DONE); + return true; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSavePic) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(w); + PARAM_INT(h); + ACTION_RETURN_BOOL(self->DrawSavePic(x, y, w, h)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::DrawSaveComment(FFont *font, int cr, int x, int y, int scalefactor) +{ + int sx = CleanXfac; + int sy = CleanYfac; + + CleanXfac = CleanYfac = scalefactor; + + // I'm not sure why SaveComment would go nullptr in this loop, but I got + // a crash report where it was nullptr when i reached 1, so now I check + // for that. + for (int i = 0; SaveComment != nullptr && SaveComment[i].Width >= 0 && i < 6; ++i) + { + screen->DrawText(font, cr, x, y + font->GetHeight() * i * scalefactor, SaveComment[i].Text, DTA_CleanNoMove, true, TAG_DONE); + } + + CleanXfac = sx; + CleanYfac = sy; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, DrawSaveComment) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_POINTER(fnt, FFont); + PARAM_INT(cr); + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(fac); + self->DrawSaveComment(fnt, cr, x, y, fac); + return 0; +} + + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::SetFileInfo(int Selected) { if (!SaveGames[Selected]->Filename.IsEmpty()) { @@ -556,405 +677,12 @@ void SavegameManager::SetFileInfo(int Selected) } } -SavegameManager savegameManager; - - - - -class DLoadSaveMenu : public DListMenu +DEFINE_ACTION_FUNCTION(FSavegameManager, SetFileInfo) { - DECLARE_CLASS(DLoadSaveMenu, DListMenu) - -protected: - - SavegameManager *manager; - - int Selected; - int TopItem; - - - 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; - - // this needs to be kept in memory so that the texture can access it when it needs to. - bool mEntering; - char savegamestring[SAVESTRINGSIZE]; - DTextEnterMenu *mInput = nullptr; - - DLoadSaveMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - void OnDestroy() override; - - void Drawer (); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - bool Responder(event_t *ev); -}; - -IMPLEMENT_CLASS(DLoadSaveMenu, false, false) - - - -//============================================================================= -// -// End of static savegame maintenance code -// -//============================================================================= - -DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, DListMenuDescriptor *desc) -: DListMenu(parent, desc) -{ - manager = &savegameManager; - manager->ReadSaveStrings(); - - savepicLeft = 10; - savepicTop = 54*CleanYfac; - savepicWidth = 216*screen->GetWidth()/640; - savepicHeight = 135*screen->GetHeight()/400; - manager->WindowSize = savepicWidth; - - rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; - listboxLeft = savepicLeft + savepicWidth + 14; - listboxTop = savepicTop; - listboxWidth = screen->GetWidth() - listboxLeft - 10; - int listboxHeight1 = screen->GetHeight() - listboxTop - 10; - listboxRows = (listboxHeight1 - 1) / rowHeight; - listboxHeight = listboxRows * rowHeight + 1; - listboxRight = listboxLeft + listboxWidth; - listboxBottom = listboxTop + listboxHeight; - - commentLeft = savepicLeft; - commentTop = savepicTop + savepicHeight + 16; - commentWidth = savepicWidth; - commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac; - commentRight = commentLeft + commentWidth; - commentBottom = commentTop + commentHeight; -} - -void DLoadSaveMenu::OnDestroy() -{ - manager->ClearSaveStuff (); - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DLoadSaveMenu::Drawer () -{ - Super::Drawer(); - - FSaveGameNode *node; - int i; - unsigned j; - bool didSeeSelected = false; - - // Draw picture area - if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) - { - return; - } - - V_DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); - if (!manager->DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) - { - screen->Clear (savepicLeft, savepicTop, - savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); - - if (manager->SaveGames.Size() > 0) - { - const char *text = - (Selected == -1 || !manager->SaveGames[Selected]->bOldVersion) - ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, - savepicTop+(savepicHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw comment area - V_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); - screen->Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); - if (manager->SaveComment != nullptr) - { - // I'm not sure why SaveComment would go nullptr in this loop, but I got - // a crash report where it was nullptr when i reached 1, so now I check - // for that. - for (i = 0; manager->SaveComment != nullptr && manager->SaveComment[i].Width >= 0 && i < 6; ++i) - { - screen->DrawText (SmallFont, CR_GOLD, commentLeft, commentTop - + SmallFont->GetHeight()*i*CleanYfac, manager->SaveComment[i].Text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw file area - V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); - screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); - - if (manager->SaveGames.Size() == 0) - { - const char * text = GStrings("MNU_NOFILES"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, - listboxTop+(listboxHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - return; - } - - for (i = 0, j = TopItem; i < listboxRows && j < manager->SaveGames.Size(); i++,j++) - { - int color; - node = manager->SaveGames[j]; - if (node->bOldVersion) - { - color = CR_BLUE; - } - else if (node->bMissingWads) - { - color = CR_ORANGE; - } - else if ((int)j == Selected) - { - color = CR_WHITE; - } - else - { - color = CR_TAN; - } - - if ((int)j == Selected) - { - screen->Clear (listboxLeft, listboxTop+rowHeight*i, - listboxRight, listboxTop+rowHeight*(i+1), -1, - mEntering ? MAKEARGB(255,255,0,0) : MAKEARGB(255,0,0,255)); - didSeeSelected = true; - if (!mEntering) - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - else - { - FString s = mInput->GetText() + SmallFont->GetCursor(); - screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, - DTA_CleanNoMove, true, TAG_DONE); - } - } - else - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) -{ - switch (mkey) - { - case MKEY_Up: - if (manager->SaveGames.Size() > 1) - { - if (Selected == -1) Selected = TopItem; - else - { - if (--Selected < 0) Selected = manager->SaveGames.Size()-1; - if (Selected < TopItem) TopItem = Selected; - else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_Down: - if (manager->SaveGames.Size() > 1) - { - if (Selected == -1) Selected = TopItem; - else - { - if (unsigned(++Selected) >= manager->SaveGames.Size()) Selected = 0; - if (Selected < TopItem) TopItem = Selected; - else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_PageDown: - if (manager->SaveGames.Size() > 1) - { - if (TopItem >= (int)manager->SaveGames.Size() - listboxRows) - { - TopItem = 0; - if (Selected != -1) Selected = 0; - } - else - { - TopItem = MIN(TopItem + listboxRows, manager->SaveGames.Size() - listboxRows); - if (TopItem > Selected && Selected != -1) Selected = TopItem; - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_PageUp: - if (manager->SaveGames.Size() > 1) - { - if (TopItem == 0) - { - TopItem = manager->SaveGames.Size() - listboxRows; - if (Selected != -1) Selected = TopItem; - } - else - { - TopItem = MAX(TopItem - listboxRows, 0); - if (Selected >= TopItem + listboxRows) Selected = TopItem; - } - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - } - return true; - - case MKEY_Enter: - return false; // This event will be handled by the subclasses - - case MKEY_MBYes: - { - if ((unsigned)Selected < manager->SaveGames.Size()) - { - int listindex = manager->SaveGames[0]->bNoDelete? Selected-1 : Selected; - manager->DeleteEntry(Selected); - manager->UnloadSaveData (); - Selected = manager->RemoveSaveSlot (Selected); - manager->ExtractSaveData (Selected); - - if (manager->LastSaved == listindex) manager->LastSaved = -1; - else if (manager->LastSaved > listindex) manager->LastSaved--; - if (manager->LastAccessed == listindex) manager->LastAccessed = -1; - else if (manager->LastAccessed > listindex) manager->LastAccessed--; - } - return true; - } - - default: - return Super::MenuEvent(mkey, fromcontroller); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::MouseEvent(int type, int x, int y) -{ - if (x >= listboxLeft && x < listboxLeft + listboxWidth && - y >= listboxTop && y < listboxTop + listboxHeight) - { - int lineno = (y - listboxTop) / rowHeight; - - if (TopItem + lineno < (int)manager->SaveGames.Size()) - { - Selected = TopItem + lineno; - manager->UnloadSaveData (); - manager->ExtractSaveData (Selected); - if (type == MOUSE_Release) - { - if (MenuEvent(MKEY_Enter, true)) - { - return true; - } - } - } - else Selected = -1; - } - else Selected = -1; - - return Super::MouseEvent(type, x, y); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadSaveMenu::Responder (event_t *ev) -{ - if (ev->type == EV_GUI_Event) - { - if (ev->subtype == EV_GUI_KeyDown) - { - if ((unsigned)Selected < manager->SaveGames.Size()) - { - switch (ev->data1) - { - case GK_F1: - manager->SetFileInfo(Selected); - return true; - - case GK_DEL: - case '\b': - { - FString EndString; - EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), manager->SaveGames[Selected]->Title, GStrings("PRESSYN")); - M_StartMessage (EndString, 0); - } - return true; - } - } - } - else if (ev->subtype == EV_GUI_WheelUp) - { - if (TopItem > 0) TopItem--; - return true; - } - else if (ev->subtype == EV_GUI_WheelDown) - { - if (TopItem < (int)manager->SaveGames.Size() - listboxRows) TopItem++; - return true; - } - } - return Super::Responder(ev); + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(i); + self->SetFileInfo(i); + return 0; } @@ -964,23 +692,16 @@ bool DLoadSaveMenu::Responder (event_t *ev) // //============================================================================= -class DSaveMenu : public DLoadSaveMenu +unsigned FSavegameManager::SavegameCount() { - DECLARE_CLASS(DSaveMenu, DLoadSaveMenu) - - FSaveGameNode NewSaveNode; - -public: - - DSaveMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - void OnDestroy() override; - bool Responder (event_t *ev); - bool MenuEvent (int mkey, bool fromcontroller); - -}; - -IMPLEMENT_CLASS(DSaveMenu, false, false) + return SaveGames.Size(); +} +DEFINE_ACTION_FUNCTION(FSavegameManager, SavegameCount) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + ACTION_RETURN_INT(self->SavegameCount()); +} //============================================================================= // @@ -988,22 +709,36 @@ IMPLEMENT_CLASS(DSaveMenu, false, false) // //============================================================================= -DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) -: DLoadSaveMenu(parent, desc) +FSaveGameNode *FSavegameManager::GetSavegame(int i) { - strcpy (NewSaveNode.Title, GStrings["NEWSAVE"]); + return SaveGames[i]; +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, GetSavegame) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + PARAM_INT(i); + ACTION_RETURN_POINTER(self->GetSavegame(i)); +} + +//============================================================================= +// +// +// +//============================================================================= + +void FSavegameManager::InsertNewSaveNode() +{ + NewSaveNode.SaveTitle = GStrings["NEWSAVE"]; NewSaveNode.bNoDelete = true; - manager->SaveGames.Insert(0, &NewSaveNode); - TopItem = 0; - if (manager->LastSaved == -1) - { - Selected = 0; - } - else - { - Selected = manager->LastSaved + 1; - } - manager->ExtractSaveData (Selected); + SaveGames.Insert(0, &NewSaveNode); +} + +DEFINE_ACTION_FUNCTION(FSavegameManager, InsertNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + self->InsertNewSaveNode(); + return 0; } //============================================================================= @@ -1012,152 +747,39 @@ DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) // //============================================================================= -void DSaveMenu::OnDestroy() +bool FSavegameManager::RemoveNewSaveNode() { - if (manager->SaveGames[0] == &NewSaveNode) + if (SaveGames[0] == &NewSaveNode) { - manager->SaveGames.Delete(0); - if (Selected == 0) Selected = -1; - else Selected--; - } - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) -{ - if (Super::MenuEvent(mkey, fromcontroller)) - { - return true; - } - if (Selected == -1) - { - return false; - } - - if (mkey == MKEY_Enter) - { - if (Selected != 0) - { - strcpy (savegamestring, manager->SaveGames[Selected]->Title); - } - else - { - savegamestring[0] = 0; - } - mInput = new DTextEnterMenu(this, savegamestring, SAVESTRINGSIZE, 1, fromcontroller); - M_ActivateMenu(mInput); - mEntering = true; - } - else if (mkey == MKEY_Input) - { - mEntering = false; - manager->DoSave(Selected, mInput->GetText()); - mInput = nullptr; - } - else if (mkey == MKEY_Abort) - { - mEntering = false; - mInput = nullptr; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSaveMenu::Responder (event_t *ev) -{ - 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; - manager->UnloadSaveData (); - return true; - } - } - } - return Super::Responder(ev); -} - -//============================================================================= -// -// -// -//============================================================================= - -class DLoadMenu : public DLoadSaveMenu -{ - DECLARE_CLASS(DLoadMenu, DLoadSaveMenu) - -public: - - DLoadMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); - - bool MenuEvent (int mkey, bool fromcontroller); -}; - -IMPLEMENT_CLASS(DLoadMenu, false, false) - - -//============================================================================= -// -// -// -//============================================================================= - -DLoadMenu::DLoadMenu(DMenu *parent, DListMenuDescriptor *desc) -: DLoadSaveMenu(parent, desc) -{ - TopItem = 0; - if (manager->LastAccessed != -1) - { - Selected = manager->LastAccessed; - } - manager->ExtractSaveData (Selected); - -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) -{ - if (Super::MenuEvent(mkey, fromcontroller)) - { - return true; - } - if (Selected == -1 || manager->SaveGames.Size() == 0) - { - return false; - } - - if (mkey == MKEY_Enter) - { - manager->LoadSavegame(Selected); + SaveGames.Delete(0); return true; } return false; } +DEFINE_ACTION_FUNCTION(FSavegameManager, RemoveNewSaveNode) +{ + PARAM_SELF_STRUCT_PROLOGUE(FSavegameManager); + ACTION_RETURN_INT(self->RemoveNewSaveNode()); +} + + +FSavegameManager savegameManager; + +DEFINE_ACTION_FUNCTION(FSavegameManager, GetManager) +{ + PARAM_PROLOGUE; + ACTION_RETURN_POINTER(&savegameManager); +} + + + +DEFINE_FIELD(FSaveGameNode, SaveTitle); +DEFINE_FIELD(FSaveGameNode, Filename); +DEFINE_FIELD(FSaveGameNode, bOldVersion); +DEFINE_FIELD(FSaveGameNode, bMissingWads); +DEFINE_FIELD(FSaveGameNode, bNoDelete); + +DEFINE_FIELD(FSavegameManager, WindowSize); +DEFINE_FIELD(FSavegameManager, quickSaveSlot); + diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index f32c20373..6569c00c7 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -63,24 +63,23 @@ CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) +CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE) CVAR(Int, m_use_mouse, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -DMenu *DMenu::CurrentMenu; DEFINE_ACTION_FUNCTION(DMenu, GetCurrentMenu) { - ACTION_RETURN_OBJECT(DMenu::CurrentMenu); + ACTION_RETURN_OBJECT(CurrentMenu); } -int DMenu::MenuTime; DEFINE_ACTION_FUNCTION(DMenu, MenuTime) { - ACTION_RETURN_INT(DMenu::MenuTime); + ACTION_RETURN_INT(MenuTime); } FGameStartup GameStartupInfo; @@ -92,6 +91,8 @@ bool MenuButtonOrigin[NUM_MKEYS]; int BackbuttonTime; float BackbuttonAlpha; static bool MenuEnabled = true; +DMenu *CurrentMenu; +int MenuTime; void M_InitVideoModes(); extern PClass *DefaultListMenuClass; @@ -140,7 +141,7 @@ void M_MarkMenus() { GC::Mark(pair->Value); } - GC::Mark(DMenu::CurrentMenu); + GC::Mark(CurrentMenu); } //============================================================================ // @@ -237,7 +238,7 @@ bool DMenu::MenuEvent (int mkey, bool fromcontroller) { Close(); S_Sound (CHAN_VOICE | CHAN_UI, - DMenu::CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE); + CurrentMenu != nullptr? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE); return true; } } @@ -272,13 +273,13 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller) void DMenu::Close () { - if (DMenu::CurrentMenu == nullptr) return; // double closing can happen in the save menu. - assert(DMenu::CurrentMenu == this); - DMenu::CurrentMenu = mParentMenu; + if (CurrentMenu == nullptr) return; // double closing can happen in the save menu. + assert(CurrentMenu == this); + CurrentMenu = mParentMenu; Destroy(); - if (DMenu::CurrentMenu != nullptr) + if (CurrentMenu != nullptr) { - GC::WriteBarrier(DMenu::CurrentMenu); + GC::WriteBarrier(CurrentMenu); } else { @@ -401,7 +402,7 @@ void DMenu::CallTicker() void DMenu::Drawer () { - if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) + if (this == CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) { FTexture *tex = TexMan(gameinfo.mBackButton); int w = tex->GetScaledWidth() * CleanXfac; @@ -444,21 +445,6 @@ DEFINE_ACTION_FUNCTION(DMenu, Close) return 0; } -DEFINE_ACTION_FUNCTION(DMenu, GetItem) -{ - PARAM_SELF_PROLOGUE(DMenu); - PARAM_NAME(name); - ACTION_RETURN_OBJECT(self->GetItem(name)); -} - -DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, GetItem) -{ - PARAM_SELF_PROLOGUE(DOptionMenuDescriptor); - PARAM_NAME(name); - ACTION_RETURN_OBJECT(self->GetItem(name)); -} - - bool DMenu::DimAllowed() { return true; @@ -486,7 +472,7 @@ bool DMenu::TranslateKeyboardEvents() void M_StartControlPanel (bool makeSound) { // intro might call this repeatedly - if (DMenu::CurrentMenu != nullptr) + if (CurrentMenu != nullptr) return; ResetButtonStates (); @@ -518,9 +504,9 @@ void M_StartControlPanel (bool makeSound) void M_ActivateMenu(DMenu *menu) { if (menuactive == MENU_Off) menuactive = MENU_On; - if (DMenu::CurrentMenu != nullptr) DMenu::CurrentMenu->ReleaseCapture(); - DMenu::CurrentMenu = menu; - GC::WriteBarrier(DMenu::CurrentMenu); + if (CurrentMenu != nullptr) CurrentMenu->ReleaseCapture(); + CurrentMenu = menu; + GC::WriteBarrier(CurrentMenu); } DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu) @@ -602,6 +588,11 @@ void M_SetMenu(FName menu, int param) M_InitVideoModes(); break; + case NAME_Quitmenu: + // The separate menu class no longer exists but the name still needs support for existing mods. + C_DoCommand("menu_quit"); + return; + } // End of special checks @@ -629,10 +620,10 @@ void M_SetMenu(FName menu, int param) if (cls == nullptr) cls = DefaultListMenuClass; if (cls == nullptr) cls = PClass::FindClass("ListMenu"); - DListMenu *newmenu = (DListMenu *)cls->CreateNew(); - IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) + DMenu *newmenu = (DMenu *)cls->CreateNew(); + IFVIRTUALPTRNAME(newmenu, "ListMenu", Init) { - VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld }; + VMValue params[3] = { newmenu, CurrentMenu, ld }; GlobalVMStack.Call(func, params, 3, nullptr, 0); } M_ActivateMenu(newmenu); @@ -648,7 +639,7 @@ void M_SetMenu(FName menu, int param) DMenu *newmenu = (DMenu*)cls->CreateNew(); IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) { - VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld }; + VMValue params[3] = { newmenu, CurrentMenu, ld }; GlobalVMStack.Call(func, params, 3, nullptr, 0); } M_ActivateMenu(newmenu); @@ -663,7 +654,7 @@ void M_SetMenu(FName menu, int param) if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu))) { DMenu *newmenu = (DMenu*)menuclass->CreateNew(); - newmenu->mParentMenu = DMenu::CurrentMenu; + newmenu->mParentMenu = CurrentMenu; M_ActivateMenu(newmenu); return; } @@ -699,7 +690,7 @@ bool M_Responder (event_t *ev) return false; } - if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) + if (CurrentMenu != nullptr && menuactive != MENU_Off) { // There are a few input sources we are interested in: // @@ -734,9 +725,9 @@ bool M_Responder (event_t *ev) } // pass everything else on to the current menu - return DMenu::CurrentMenu->CallResponder(ev); + return CurrentMenu->CallResponder(ev); } - else if (DMenu::CurrentMenu->TranslateKeyboardEvents()) + else if (CurrentMenu->TranslateKeyboardEvents()) { ch = ev->data1; keyup = ev->subtype == EV_GUI_KeyUp; @@ -755,7 +746,7 @@ bool M_Responder (event_t *ev) default: if (!keyup) { - return DMenu::CurrentMenu->CallResponder(ev); + return CurrentMenu->CallResponder(ev); } break; } @@ -838,11 +829,11 @@ bool M_Responder (event_t *ev) { MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; } - DMenu::CurrentMenu->CallMenuEvent(mkey, fromcontroller); + CurrentMenu->CallMenuEvent(mkey, fromcontroller); return true; } } - return DMenu::CurrentMenu->CallResponder(ev) || !keyup; + return CurrentMenu->CallResponder(ev) || !keyup; } else if (MenuEnabled) { @@ -883,10 +874,10 @@ bool M_Responder (event_t *ev) void M_Ticker (void) { - DMenu::MenuTime++; - if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) + MenuTime++; + if (CurrentMenu != nullptr && menuactive != MENU_Off) { - DMenu::CurrentMenu->CallTicker(); + CurrentMenu->CallTicker(); for (int i = 0; i < NUM_MKEYS; ++i) { @@ -895,7 +886,7 @@ void M_Ticker (void) if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) { MenuButtonTickers[i] = KEY_REPEAT_RATE; - DMenu::CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]); + CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]); } } } @@ -935,14 +926,14 @@ void M_Drawer (void) } - if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) + if (CurrentMenu != nullptr && menuactive != MENU_Off) { - if (DMenu::CurrentMenu->DimAllowed()) + if (CurrentMenu->DimAllowed()) { screen->Dim(fade); V_SetBorderNeedRefresh(); } - DMenu::CurrentMenu->CallDrawer(); + CurrentMenu->CallDrawer(); } } @@ -955,10 +946,10 @@ void M_Drawer (void) void M_ClearMenus () { M_DemoNoPlay = false; - if (DMenu::CurrentMenu != nullptr) + if (CurrentMenu != nullptr) { - DMenu::CurrentMenu->Destroy(); - DMenu::CurrentMenu = nullptr; + CurrentMenu->Destroy(); + CurrentMenu = nullptr; } V_SetBorderNeedRefresh(); menuactive = MENU_Off; @@ -1196,11 +1187,11 @@ CCMD(reset2saved) // This really should be in the script but we can't do scripted CCMDs yet. CCMD(undocolorpic) { - if (DMenu::CurrentMenu != NULL) + if (CurrentMenu != NULL) { - IFVIRTUALPTR(DMenu::CurrentMenu, DMenu, ResetColor) + IFVIRTUALPTR(CurrentMenu, DMenu, ResetColor) { - VMValue params[] = { (DObject*)DMenu::CurrentMenu }; + VMValue params[] = { (DObject*)CurrentMenu }; GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); } } @@ -1210,6 +1201,8 @@ CCMD(undocolorpic) DEFINE_FIELD(DMenu, mParentMenu) +DEFINE_FIELD(DMenu, mMouseCapture); +DEFINE_FIELD(DMenu, mBackbuttonSelected); DEFINE_FIELD(DMenuDescriptor, mMenuName) DEFINE_FIELD(DMenuDescriptor, mNetgameMessage) @@ -1220,9 +1213,6 @@ DEFINE_FIELD(DMenuItemBase, mYpos) DEFINE_FIELD(DMenuItemBase, mAction) DEFINE_FIELD(DMenuItemBase, mEnabled) -DEFINE_FIELD(DListMenu, mDesc) -DEFINE_FIELD(DListMenu, mFocusControl) - DEFINE_FIELD(DListMenuDescriptor, mItems) DEFINE_FIELD(DListMenuDescriptor, mSelectedItem) DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX) @@ -1322,50 +1312,6 @@ DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, con return (DMenuItemBase*)p; } -bool DMenuItemBase::CheckCoordinate(int x, int y) -{ - IFVIRTUAL(DMenuItemBase, CheckCoordinate) - { - VMValue params[] = { (DObject*)this, x, y }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return !!retval; - } - return false; -} - -void DMenuItemBase::Ticker() -{ - IFVIRTUAL(DMenuItemBase, Ticker) - { - VMValue params[] = { (DObject*)this }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); - } -} - -void DMenuItemBase::Drawer(bool selected) -{ - IFVIRTUAL(DMenuItemBase, Drawer) - { - VMValue params[] = { (DObject*)this, selected }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); - } -} - -bool DMenuItemBase::Selectable() -{ - IFVIRTUAL(DMenuItemBase, Selectable) - { - VMValue params[] = { (DObject*)this }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return !!retval; - } - return false; -} - bool DMenuItemBase::Activate() { IFVIRTUAL(DMenuItemBase, Activate) @@ -1378,18 +1324,6 @@ bool DMenuItemBase::Activate() } return false; } -FName DMenuItemBase::GetAction(int *pparam) -{ - IFVIRTUAL(DMenuItemBase, GetAction) - { - VMValue params[] = { (DObject*)this }; - int retval[2]; - VMReturn ret[2]; ret[0].IntAt(&retval[0]); ret[1].IntAt(&retval[1]); - GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr); - return ENamedName(retval[0]); - } - return NAME_None; -} bool DMenuItemBase::SetString(int i, const char *s) { @@ -1447,112 +1381,4 @@ bool DMenuItemBase::GetValue(int i, int *pvalue) return false; } - -void DMenuItemBase::Enable(bool on) -{ - IFVIRTUAL(DMenuItemBase, Enable) - { - VMValue params[] = { (DObject*)this, on }; - GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); - } -} - -bool DMenuItemBase::MenuEvent(int mkey, bool fromcontroller) -{ - IFVIRTUAL(DMenuItemBase, MenuEvent) - { - VMValue params[] = { (DObject*)this, mkey, fromcontroller }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return !!retval; - } - return false; -} - -bool DMenuItemBase::MouseEvent(int type, int x, int y) -{ - IFVIRTUAL(DMenuItemBase, MouseEvent) - { - VMValue params[] = { (DObject*)this, type, x, y }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return !!retval; - } - return false; -} - -bool DMenuItemBase::CheckHotkey(int c) -{ - IFVIRTUAL(DMenuItemBase, CheckHotkey) - { - VMValue params[] = { (DObject*)this, c }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return !!retval; - } - return false; -} - -int DMenuItemBase::GetWidth() -{ - IFVIRTUAL(DMenuItemBase, GetWidth) - { - VMValue params[] = { (DObject*)this }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return retval; - } - return false; -} - -int DMenuItemBase::GetIndent() -{ - IFVIRTUAL(DMenuItemBase, GetIndent) - { - VMValue params[] = { (DObject*)this }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return retval; - } - return false; -} - -int DMenuItemBase::Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) -{ - IFVIRTUAL(DMenuItemBase, Draw) - { - VMValue params[] = { (DObject*)this, desc, y, indent, selected }; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); - return retval; - } - return false; -} - -void DMenuItemBase::DrawSelector(int xofs, int yofs, FTextureID tex) -{ - if (tex.isNull()) - { - if ((DMenu::MenuTime % 8) < 6) - { - screen->DrawText(ConFont, OptionSettings.mFontColorSelection, - (mXpos + xofs - 160) * CleanXfac + screen->GetWidth() / 2, - (mYpos + yofs - 100) * CleanYfac + screen->GetHeight() / 2, - "\xd", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, - TAG_DONE); - } - } - else - { - screen->DrawTexture(TexMan(tex), mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE); - } -} - +IMPLEMENT_CLASS(DMenuItemBase, false, false) diff --git a/src/menu/menu.h b/src/menu/menu.h index d272fe242..415e3dff6 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -58,46 +58,58 @@ extern FGameStartup GameStartupInfo; struct FSaveGameNode { - char Title[SAVESTRINGSIZE]; + FString SaveTitle; FString Filename; - bool bOldVersion; - bool bMissingWads; - bool bNoDelete; - - FSaveGameNode() { bNoDelete = false; } + bool bOldVersion = false; + bool bMissingWads = false; + bool bNoDelete = false; }; -struct SavegameManager +struct FSavegameManager { +private: TArray SaveGames; + FSaveGameNode NewSaveNode; int LastSaved = -1; int LastAccessed = -1; - int WindowSize = 0; - FSaveGameNode *quickSaveSlot = nullptr; - FileReader *currentSavePic = nullptr; TArray SavePicData; - FTexture *SavePic = nullptr; FBrokenLines *SaveComment = nullptr; - void ClearSaveGames(); +public: + int WindowSize = 0; + FSaveGameNode *quickSaveSlot = nullptr; + ~FSavegameManager(); + +private: int InsertSaveNode(FSaveGameNode *node); - int RemoveSaveSlot(int index); +public: + void NotifyNewSave(const FString &file, const FString &title, bool okForQuicksave); + void ClearSaveGames(); + void ReadSaveStrings(); - void NotifyNewSave(const char *file, const char *title, bool okForQuicksave); + void UnloadSaveData(); + + int RemoveSaveSlot(int index); void LoadSavegame(int Selected); void DoSave(int Selected, const char *savegamestring); - void DeleteEntry(int Selected); - void ExtractSaveData(int index); - void UnloadSaveData(); + 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(); }; -extern SavegameManager savegameManager; +extern FSavegameManager savegameManager; +class DMenu; +extern DMenu *CurrentMenu; +extern int MenuTime; //============================================================================= // @@ -241,9 +253,7 @@ class DMenu : public DObject DECLARE_CLASS (DMenu, DObject) HAS_OBJECT_POINTERS -protected: - bool mMouseCapture; - bool mBackbuttonSelected; + public: enum @@ -258,10 +268,9 @@ public: BACKBUTTON_TIME = 4*TICRATE }; - static DMenu *CurrentMenu; - static int MenuTime; - TObjPtr mParentMenu; + bool mMouseCapture; + bool mBackbuttonSelected; DMenu(DMenu *parent = NULL); virtual bool Responder (event_t *ev); @@ -277,8 +286,6 @@ public: virtual bool CheckFocus(DMenuItemBase *fc) { return false; } virtual void ReleaseFocus() {} - virtual DMenuItemBase *GetItem(FName name) { return nullptr; } - bool CallResponder(event_t *ev); bool CallMenuEvent(int mkey, bool fromcontroller); bool CallMouseEvent(int type, int x, int y); @@ -308,70 +315,15 @@ public: FNameNoInit mAction; bool mEnabled; - bool CheckCoordinate(int x, int y); - void Ticker(); - void Drawer(bool selected); - bool Selectable(); bool Activate(); - FName GetAction(int *pparam); 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 Enable(bool on); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - bool CheckHotkey(int c); - int GetWidth(); - int GetIndent(); - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected); void OffsetPositionY(int ydelta) { mYpos += ydelta; } int GetY() { return mYpos; } - int GetX() { return mXpos; } - void SetX(int x) { mXpos = x; } - - void DrawSelector(int xofs, int yofs, FTextureID tex); - }; -//============================================================================= -// -// list menu class runs a menu described by a DListMenuDescriptor -// -//============================================================================= - -class DListMenu : public DMenu -{ - DECLARE_CLASS(DListMenu, DMenu) - HAS_OBJECT_POINTERS; -public: - - DListMenuDescriptor *mDesc; - DMenuItemBase *mFocusControl; - - DListMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL); - virtual void Init(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL); - DMenuItemBase *GetItem(FName name); - bool Responder (event_t *ev); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - void Ticker (); - void Drawer (); - void SetFocus(DMenuItemBase *fc) - { - mFocusControl = fc; - } - bool CheckFocus(DMenuItemBase *fc) - { - return mFocusControl == fc; - } - void ReleaseFocus() - { - mFocusControl = NULL; - } -}; - - //============================================================================= // // @@ -396,41 +348,10 @@ extern FOptionMap OptionValues; //============================================================================= // -// Input some text +// // //============================================================================= -class DTextEnterMenu : public DMenu -{ - DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu) - -public: - FString mEnterString; - unsigned int mEnterSize; - unsigned int mEnterPos; - int mSizeMode; // 1: size is length in chars. 2: also check string width - bool mInputGridOkay; - - int InputGridX; - int InputGridY; - - // [TP] - bool AllowColors; - - - // [TP] Added allowcolors - DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors = false); - - void Drawer (); - bool MenuEvent (int mkey, bool fromcontroller); - bool Responder(event_t *ev); - bool MouseEvent(int type, int x, int y); - FString GetText(); -}; - - - - struct event_t; void M_EnableMenu (bool on) ; bool M_Responder (event_t *ev); diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 8afed617e..f9ecbd808 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -147,7 +147,7 @@ static void DeinitMenus() } MenuDescriptors.Clear(); OptionValues.Clear(); - DMenu::CurrentMenu = nullptr; + CurrentMenu = nullptr; savegameManager.ClearSaveGames(); } @@ -294,7 +294,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) { sc.MustGetString(); PClass *cls = PClass::FindClass(sc.String); - if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DListMenu))) + if (cls == nullptr || !cls->IsDescendantOf("ListMenu")) { sc.ScriptError("Unknown menu class '%s'", sc.String); } @@ -868,7 +868,6 @@ static void ParseOptionMenu(FScanner &sc) ParseOptionMenuBody(sc, desc); ReplaceMenu(sc, desc); - if (desc->mIndent == 0) desc->CalcIndent(); } @@ -1340,7 +1339,7 @@ void M_StartupSkillMenu(FGameStartup *gs) // Delete previous contents for(unsigned i=0; imItems.Size(); i++) { - FName n = ld->mItems[i]->GetAction(nullptr); + FName n = ld->mItems[i]->mAction; if (n == NAME_Startgame || n == NAME_StartgameConfirm) { ld->mItems.Resize(i); diff --git a/src/menu/menuinput.cpp b/src/menu/menuinput.cpp deleted file mode 100644 index 9a0b717b5..000000000 --- a/src/menu/menuinput.cpp +++ /dev/null @@ -1,396 +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/menu.h" -#include "v_video.h" -#include "c_cvars.h" -#include "d_event.h" -#include "d_gui.h" -#include "v_font.h" -#include "v_palette.h" -#include "cmdlib.h" -// [TP] New #includes -#include "v_text.h" - -IMPLEMENT_CLASS(DTextEnterMenu, true, false) - -#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) - -DEFINE_FIELD(DTextEnterMenu, mInputGridOkay) - -//============================================================================= -// -// -// -//============================================================================= - -// [TP] Added allowcolors -DTextEnterMenu::DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors) -: DMenu(parent) -{ - mEnterString = textbuffer; - mEnterSize = maxlen < 0 ? UINT_MAX : unsigned(maxlen); - mSizeMode = sizemode; - mInputGridOkay = showgrid || m_showinputgrid; - if (mEnterString.IsNotEmpty()) - { - 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] -} - -//============================================================================= -// -// -// -//============================================================================= - -FString DTextEnterMenu::GetText() -{ - return mEnterString; -} - -//============================================================================= -// -// -// -//============================================================================= - - -//============================================================================= -// -// -// -//============================================================================= - -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; - if (mEnterString.Len() < mEnterSize && - (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) - { - mEnterString.AppendFormat("%c", (char)ev->data1); - } - return true; - } - char ch = (char)ev->data1; - if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b') - { - if (mEnterString.IsNotEmpty()) - { - mEnterString.Truncate(mEnterString.Len() - 1); - } - } - else if (ev->subtype == EV_GUI_KeyDown) - { - if (ch == GK_ESCAPE) - { - DMenu *parent = mParentMenu; - Close(); - parent->CallMenuEvent(MKEY_Abort, false); - return true; - } - else if (ch == '\r') - { - if (mEnterString.IsNotEmpty()) - { - // [TP] If we allow color codes, colorize the string now. - if (AllowColors) - mEnterString = strbin1(mEnterString); - - DMenu *parent = mParentMenu; - parent->CallMenuEvent(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; - const int cell_height = 12 * CleanYfac; - 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 (CallMenuEvent(MKEY_Enter, true)) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - if (m_use_mouse == 2) InputGridX = InputGridY = -1; - return true; - } - } - } - else - { - InputGridX = InputGridY = -1; - } - return Super::MouseEvent(type, x, y); -} - - - -//============================================================================= -// -// -// -//============================================================================= - -bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller) -{ - if (key == MKEY_Back) - { - mParentMenu->CallMenuEvent(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.IsNotEmpty()) - { - mEnterString.Truncate(mEnterString.Len() - 1); - } - 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.IsNotEmpty()) - { - DMenu *parent = mParentMenu; - Close(); - parent->CallMenuEvent(MKEY_Input, false); - return true; - } - } - else if (ch == '\b') // bs - { - if (mEnterString.IsNotEmpty()) - { - mEnterString.Truncate(mEnterString.Len() - 1); - } - } - else if (mEnterString.Len() < mEnterSize && - (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) - { - mEnterString += char(ch); - } - } - return true; - - default: - break; // Keep GCC quiet - } - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -void DTextEnterMenu::Drawer () -{ - mParentMenu->CallDrawer(); - if (mInputGridOkay) - { - const int cell_width = 18 * CleanXfac; - const int cell_height = 12 * CleanYfac; - const int top_padding = cell_height / 2 - SmallFont->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. - screen->Dim(0, 0.8f, - 0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/, - screen->GetHeight() - INPUTGRID_HEIGHT * cell_height, - screen->GetWidth() /*13 * cell_width*/, - INPUTGRID_HEIGHT * cell_height); - - if (InputGridX >= 0 && InputGridY >= 0) - { - // Highlight the background behind the selected character. - screen->Dim(MAKERGB(255,248,220), 0.6f, - InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2, - InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(), - cell_width, cell_height); - } - - 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]; - FTexture *pic = SmallFont->GetChar(ch, &width); - EColorRange color; - - // The highlighted character is yellow; the rest are dark gray. - color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY; - - if (pic != NULL) - { - // Draw a normal character. - screen->DrawChar(SmallFont, color, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true, TAG_DONE); - } - else if (ch == ' ') - { - FRemapTable *remap = SmallFont->GetColorTranslation(color); - // 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 * 3 / 4; - const int x2 = x1 + width * 3 * CleanXfac / 2; - const int y1 = yy + top_padding; - const int y2 = y1 + SmallFont->GetHeight() * CleanYfac; - const int palentry = remap->Remap[remap->NumEntries * 2 / 3]; - const uint32 palcolor = remap->Palette[remap->NumEntries * 2 / 3]; - screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top - screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom - screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left - screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right - } - else if (ch == '\b' || ch == 0) - { - // Draw the backspace and end "characters". - const char *const str = ch == '\b' ? "BS" : "ED"; - screen->DrawText(SmallFont, color, - xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2, - yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE); - } - } - } - } - Super::Drawer(); -} - - -DEFINE_ACTION_FUNCTION(DTextEnterMenu, Open) -{ - PARAM_PROLOGUE; - PARAM_OBJECT(parent, DMenu); - PARAM_STRING(text); - PARAM_INT(maxlen); - PARAM_INT(sizemode); - PARAM_BOOL(fromcontroller); - auto m = new DTextEnterMenu(parent, text.GetChars(), maxlen, sizemode, fromcontroller, false); - M_ActivateMenu(m); - ACTION_RETURN_OBJECT(m); -} - -DEFINE_ACTION_FUNCTION(DTextEnterMenu, GetText) -{ - PARAM_SELF_PROLOGUE(DTextEnterMenu); - ACTION_RETURN_STRING(self->GetText()); -} diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index e15940acb..8cbaa3e24 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -46,52 +46,15 @@ #include "c_dispatch.h" #include "g_game.h" - EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd] -class DMessageBoxMenu : public DMenu +typedef void(*hfunc)(); +DEFINE_ACTION_FUNCTION(DMessageBoxMenu, CallHandler) { - DECLARE_CLASS(DMessageBoxMenu, DMenu) - - FBrokenLines *mMessage; - int mMessageMode; - int messageSelection; - int mMouseLeft, mMouseRight, mMouseY; - FName mAction; - -public: - - DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None); - void OnDestroy() override; - 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); -}; - -IMPLEMENT_CLASS(DMessageBoxMenu, false, false) - -//============================================================================= -// -// -// -//============================================================================= - -DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action) -: DMenu(parent) -{ - mAction = action; - messageSelection = 0; - mMouseLeft = 140; - mMouseY = INT_MIN; - int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]); - int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]); - mMouseRight = MAX(mr1, mr2); - - Init(parent, message, messagemode, playsound); + PARAM_PROLOGUE; + PARAM_POINTERTYPE(Handler, hfunc); + Handler(); + return 0; } //============================================================================= @@ -100,325 +63,15 @@ DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int message // //============================================================================= -void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound) +DMenu *CreateMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action = NAME_None, hfunc handler = nullptr) { - mParentMenu = parent; - if (message != NULL) - { - if (*message == '$') message = GStrings(message+1); - mMessage = V_BreakLines(SmallFont, 300, message); - } - else mMessage = NULL; - mMessageMode = messagemode; - if (playsound) - { - S_StopSound (CHAN_VOICE); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE); - } -} + auto c = PClass::FindClass("MessageBoxMenu"); + auto p = c->CreateNew(); + VMValue params[] = { p, parent, FString(message), messagemode, playsound, action.GetIndex(), handler }; -//============================================================================= -// -// -// -//============================================================================= - -void DMessageBoxMenu::OnDestroy() -{ - if (mMessage != NULL) V_FreeBrokenLines(mMessage); - mMessage = NULL; - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DMessageBoxMenu::CloseSound() -{ - S_Sound (CHAN_VOICE | CHAN_UI, - DMenu::CurrentMenu != NULL? "menu/backup" : "menu/dismiss", snd_menuvolume, ATTN_NONE); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DMessageBoxMenu::HandleResult(bool res) -{ - if (mParentMenu != NULL) - { - if (mMessageMode == 0) - { - if (mAction == NAME_None) - { - mParentMenu->CallMenuEvent(res? MKEY_MBYes : MKEY_MBNo, false); - Close(); - } - else - { - Close(); - if (res) M_SetMenu(mAction, -1); - } - CloseSound(); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DMessageBoxMenu::Drawer () -{ - int i, y; - PalEntry fade = 0; - - int fontheight = SmallFont->GetHeight(); - //V_SetBorderNeedRefresh(); - //ST_SetNeedRefresh(); - - y = 100; - - if (mMessage != NULL) - { - for (i = 0; mMessage[i].Width >= 0; i++) - y -= SmallFont->GetHeight () / 2; - - for (i = 0; mMessage[i].Width >= 0; i++) - { - screen->DrawText (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; - screen->DrawText(SmallFont, - messageSelection == 0? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, - 160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE); - screen->DrawText(SmallFont, - messageSelection == 1? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, - 160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE); - - if (messageSelection >= 0) - { - if ((DMenu::MenuTime%8) < 6) - { - screen->DrawText(ConFont, 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); - } - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -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) - { - S_Sound (CHAN_VOICE | CHAN_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) - { - 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) - { - //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - messageSelection = sel; - if (type == MOUSE_Release) - { - return MenuEvent(MKEY_Enter, true); - } - return true; - } -} - -//============================================================================= -// -// -// -//============================================================================= -//============================================================================= -// -// -// -//============================================================================= - -class DQuitMenu : public DMessageBoxMenu -{ - DECLARE_CLASS(DQuitMenu, DMessageBoxMenu) - -public: - - DQuitMenu(bool playsound = false); - virtual void HandleResult(bool res); -}; - -IMPLEMENT_CLASS(DQuitMenu, false, false) - -//============================================================================= -// -// -// -//============================================================================= - -DQuitMenu::DQuitMenu(bool playsound) -{ - int messageindex = gametic % gameinfo.quitmessages.Size(); - FString EndString; - const char *msg = gameinfo.quitmessages[messageindex]; - if (msg[0] == '$') - { - if (msg[1] == '*') - { - EndString = GStrings(msg+2); - } - else - { - EndString.Format("%s\n\n%s", GStrings(msg+1), GStrings("DOSY")); - } - } - else EndString = gameinfo.quitmessages[messageindex]; - - Init(NULL, EndString, 0, playsound); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DQuitMenu::HandleResult(bool res) -{ - if (res) - { - if (!netgame) - { - if (gameinfo.quitSound.IsNotEmpty()) - { - S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE); - I_WaitVBL (105); - } - } - ST_Endoom(); - } - else - { - Close(); - CloseSound(); - } + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenu*)p; } //============================================================================= @@ -430,70 +83,42 @@ void DQuitMenu::HandleResult(bool res) CCMD (menu_quit) { // F10 M_StartControlPanel (true); - DMenu *newmenu = new DQuitMenu(false); - newmenu->mParentMenu = DMenu::CurrentMenu; + + int messageindex = gametic % gameinfo.quitmessages.Size(); + FString EndString; + const char *msg = gameinfo.quitmessages[messageindex]; + if (msg[0] == '$') + { + if (msg[1] == '*') + { + EndString = GStrings(msg + 2); + } + else + { + EndString.Format("%s\n\n%s", GStrings(msg + 1), GStrings("DOSY")); + } + } + else EndString = gameinfo.quitmessages[messageindex]; + + DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, EndString, 0, false, NAME_None, []() + { + if (!netgame) + { + if (gameinfo.quitSound.IsNotEmpty()) + { + S_Sound(CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE); + I_WaitVBL(105); + } + } + ST_Endoom(); + }); + + M_ActivateMenu(newmenu); } -//============================================================================= -// -// -// -//============================================================================= -//============================================================================= -// -// -// -//============================================================================= - -class DEndGameMenu : public DMessageBoxMenu -{ - DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu) - -public: - - DEndGameMenu(bool playsound = false); - virtual void HandleResult(bool res); -}; - -IMPLEMENT_CLASS(DEndGameMenu, false, false) - -//============================================================================= -// -// -// -//============================================================================= - -DEndGameMenu::DEndGameMenu(bool playsound) -{ - Init(NULL, GStrings(netgame ? "NETEND" : "ENDGAME"), 0, playsound); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DEndGameMenu::HandleResult(bool res) -{ - if (res) - { - M_ClearMenus (); - if (!netgame) - { - D_StartTitle (); - } - } - else - { - Close(); - CloseSound(); - } -} - //============================================================================= // // @@ -510,67 +135,18 @@ CCMD (menu_endgame) //M_StartControlPanel (true); S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - DMenu *newmenu = new DEndGameMenu(false); - newmenu->mParentMenu = DMenu::CurrentMenu; - M_ActivateMenu(newmenu); -} -//============================================================================= -// -// -// -//============================================================================= -//============================================================================= -// -// -// -//============================================================================= - -class DQuickSaveMenu : public DMessageBoxMenu -{ - DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu) - -public: - - DQuickSaveMenu(bool playsound = false); - virtual void HandleResult(bool res); -}; - -IMPLEMENT_CLASS(DQuickSaveMenu, false, false) - -//============================================================================= -// -// -// -//============================================================================= - -DQuickSaveMenu::DQuickSaveMenu(bool playsound) -{ - FString tempstring; - - tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->Title); - Init(NULL, tempstring, 0, playsound); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DQuickSaveMenu::HandleResult(bool res) -{ - if (res) + FString tempstring = GStrings(netgame ? "NETEND" : "ENDGAME"); + DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() { - G_SaveGame (savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); M_ClearMenus(); - } - else - { - Close(); - CloseSound(); - } + if (!netgame) + { + D_StartTitle(); + } + }); + + M_ActivateMenu(newmenu); } //============================================================================= @@ -601,72 +177,23 @@ CCMD (quicksave) // [mxd]. Just save the game, no questions asked. if (!saveloadconfirmation) { - G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title); + G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); return; } S_Sound(CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - DMenu *newmenu = new DQuickSaveMenu(false); - newmenu->mParentMenu = DMenu::CurrentMenu; - M_ActivateMenu(newmenu); -} -//============================================================================= -// -// -// -//============================================================================= -//============================================================================= -// -// -// -//============================================================================= - -class DQuickLoadMenu : public DMessageBoxMenu -{ - DECLARE_CLASS(DQuickLoadMenu, DMessageBoxMenu) - -public: - - DQuickLoadMenu(bool playsound = false); - virtual void HandleResult(bool res); -}; - -IMPLEMENT_CLASS(DQuickLoadMenu, false, false) - -//============================================================================= -// -// -// -//============================================================================= - -DQuickLoadMenu::DQuickLoadMenu(bool playsound) -{ FString tempstring; + tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars()); - tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->Title); - Init(NULL, tempstring, 0, playsound); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DQuickLoadMenu::HandleResult(bool res) -{ - if (res) + DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() { - G_LoadGame (savegameManager.quickSaveSlot->Filename.GetChars()); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); + G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->SaveTitle.GetChars()); + S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); M_ClearMenus(); - } - else - { - Close(); - CloseSound(); - } + }); + + M_ActivateMenu(newmenu); } //============================================================================= @@ -699,10 +226,17 @@ CCMD (quickload) G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); return; } + FString tempstring; + tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->SaveTitle.GetChars()); M_StartControlPanel(true); - DMenu *newmenu = new DQuickLoadMenu(false); - newmenu->mParentMenu = DMenu::CurrentMenu; + + DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, tempstring, 0, false, NAME_None, []() + { + G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); + S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); + M_ClearMenus(); + }); M_ActivateMenu(newmenu); } @@ -714,13 +248,13 @@ CCMD (quickload) void M_StartMessage(const char *message, int messagemode, FName action) { - if (DMenu::CurrentMenu == NULL) + if (CurrentMenu == NULL) { // only play a sound if no menu was active before M_StartControlPanel(menuactive == MENU_Off); } - DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action); - newmenu->mParentMenu = DMenu::CurrentMenu; + DMenu *newmenu = CreateMessageBoxMenu(CurrentMenu, message, messagemode, false, action); + newmenu->mParentMenu = CurrentMenu; M_ActivateMenu(newmenu); } @@ -732,4 +266,4 @@ DEFINE_ACTION_FUNCTION(DMenu, StartMessage) PARAM_NAME_DEF(action); M_StartMessage(msg, mode, action); return 0; -} \ No newline at end of file +} diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index acbb3c58e..87f0adcf1 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -49,32 +49,6 @@ #include "menu/menu.h" -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenuDescriptor::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; -} - -DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, CalcIndent) -{ - PARAM_SELF_PROLOGUE(DOptionMenuDescriptor); - self->CalcIndent(); - return 0; -} - //============================================================================= // // @@ -85,7 +59,7 @@ DMenuItemBase *DOptionMenuDescriptor::GetItem(FName name) { for(unsigned i=0;iGetAction(NULL); + FName nm = mItems[i]->mAction; if (nm == name) return mItems[i]; } return NULL; diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 519af0a9d..c730f2d81 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -61,12 +61,12 @@ EXTERN_CVAR(Bool, cl_run) DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(r); PARAM_INT(g); PARAM_INT(b); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { char command[24]; players[consoleplayer].userinfo.ColorChanged(MAKERGB(r, g, b)); @@ -86,12 +86,12 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, PlayerNameChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_STRING(s); const char *pp = s; FString command("name \""); - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { // Escape any backslashes or quotation marks before sending the name to the console. for (auto p = pp; *p != '\0'; ++p) @@ -116,9 +116,9 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, PlayerNameChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorSetChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(sel); - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { players[consoleplayer].userinfo.ColorSetChanged(sel); char command[24]; @@ -136,10 +136,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, ColorSetChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, ClassChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(sel); PARAM_POINTER(cls, FPlayerClass); - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { players[consoleplayer].userinfo.PlayerClassNumChanged(gameinfo.norandomplayerclass ? sel : sel - 1); cvar_set("playerclass", sel == 0 && !gameinfo.norandomplayerclass ? "Random" : GetPrintableDisplayName(cls->Type).GetChars()); @@ -156,9 +156,9 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, ClassChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, SkinChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(sel); - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { players[consoleplayer].userinfo.SkinNumChanged(sel); cvar_set("skin", Skins[sel].Name); @@ -174,10 +174,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, SkinChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, AutoaimChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_FLOAT(val); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { autoaim = float(val); } @@ -192,10 +192,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, AutoaimChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, TeamChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(val); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { team = val == 0 ? TEAM_NONE : val - 1; } @@ -210,10 +210,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, TeamChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(v); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { cvar_set("gender", v == 0 ? "male" : v == 1 ? "female" : "other"); } @@ -228,10 +228,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, SwitchOnPickupChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(v); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { neverswitchonpickup = !!v; } @@ -246,10 +246,10 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, SwitchOnPickupChanged) DEFINE_ACTION_FUNCTION(DPlayerMenu, AlwaysRunChanged) { - PARAM_SELF_PROLOGUE(DListMenu); + PARAM_SELF_PROLOGUE(DMenu); PARAM_INT(v); // only allow if the menu is active to prevent abuse. - if (self == DMenu::CurrentMenu) + if (self == CurrentMenu) { cl_run = !!v; } diff --git a/src/menu/readthis.cpp b/src/menu/readthis.cpp deleted file mode 100644 index 06d4620da..000000000 --- a/src/menu/readthis.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* -** readthis.cpp -** Help screens -** -**--------------------------------------------------------------------------- -** 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/menu.h" -#include "v_video.h" -#include "g_level.h" -#include "gi.h" -#include "g_levellocals.h" -#include "textures/textures.h" - -class DReadThisMenu : public DMenu -{ - DECLARE_CLASS(DReadThisMenu, DMenu) - int mScreen; - int mInfoTic; - -public: - - DReadThisMenu(DMenu *parent = NULL); - void Drawer(); - bool MenuEvent(int mkey, bool fromcontroller); - bool DimAllowed () { return false; } - bool MouseEvent(int type, int x, int y); -}; - -IMPLEMENT_CLASS(DReadThisMenu, false, false) - -//============================================================================= -// -// Read This Menus -// -//============================================================================= - -DReadThisMenu::DReadThisMenu(DMenu *parent) -: DMenu(parent) -{ - mScreen = 1; - mInfoTic = gametic; -} - - -//============================================================================= -// -// -// -//============================================================================= - -void DReadThisMenu::Drawer() -{ - FTexture *tex = NULL, *prevpic = NULL; - double alpha; - - // Did the mapper choose a custom help page via MAPINFO? - if ((level.info != NULL) && level.info->F1Pic.Len() != 0) - { - tex = TexMan.FindTexture(level.info->F1Pic); - mScreen = 1; - } - - if (tex == NULL) - { - tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()]; - } - - if (mScreen > 1) - { - prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()]; - } - - screen->Dim(0, 1.0, 0,0, SCREENWIDTH, SCREENHEIGHT); - alpha = MIN((gametic - mInfoTic) * (3. / TICRATE), 1.); - if (alpha < 1. && prevpic != NULL) - { - screen->DrawTexture (prevpic, 0, 0, DTA_Fullscreen, true, TAG_DONE); - } - screen->DrawTexture (tex, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha, TAG_DONE); - -} - - -//============================================================================= -// -// -// -//============================================================================= - -bool DReadThisMenu::MenuEvent(int mkey, bool fromcontroller) -{ - if (mkey == MKEY_Enter) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - mScreen++; - mInfoTic = gametic; - if ((level.info != NULL && level.info->F1Pic.Len() != 0) || mScreen > int(gameinfo.infoPages.Size())) - { - Close(); - } - return true; - } - else return Super::MenuEvent(mkey, fromcontroller); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DReadThisMenu::MouseEvent(int type, int x, int y) -{ - if (type == MOUSE_Click) - { - return MenuEvent(MKEY_Enter, true); - } - return false; -} - diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 54be79e3c..3977d6a01 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -347,7 +347,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker } // Convert the rest of the data to our own internal format. - node->Dialogue = ncopystring (speech.Dialogue); + node->Dialogue = speech.Dialogue; // The speaker's portrait, if any. speech.Dialogue[0] = 0; //speech.Backdrop[8] = 0; @@ -360,7 +360,7 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker // The speaker's name, if any. speech.Sound[0] = 0; //speech.Name[16] = 0; - node->SpeakerName = ncopystring(speech.Name); + node->SpeakerName = speech.Name; // The item the speaker should drop when killed. node->DropType = dyn_cast(GetStrifeType(speech.DropType)); @@ -422,7 +422,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker } // Convert the rest of the data to our own internal format. - node->Dialogue = ncopystring (speech.Dialogue); + node->Dialogue = speech.Dialogue; // The Teaser version doesn't have portraits. node->Backdrop.SetInvalid(); @@ -440,7 +440,7 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker // The speaker's name, if any. speech.Dialogue[0] = 0; //speech.Name[16] = 0; - node->SpeakerName = ncopystring (speech.Name); + node->SpeakerName = speech.Name; // The item the speaker should drop when killed. node->DropType = dyn_cast(GetStrifeType (speech.DropType)); @@ -505,7 +505,7 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses) // The message to record in the log for this reply. reply->LogNumber = rsp->Log; - reply->LogString = NULL; + reply->LogString = ""; // The item to receive when this reply is used. reply->GiveType = dyn_cast(GetStrifeType (rsp->GiveType)); @@ -520,31 +520,32 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses) reply->ItemCheck[k].Item = inv; reply->ItemCheck[k].Amount = rsp->Count[k]; } + reply->PrintAmount = reply->ItemCheck[0].Amount; reply->ItemCheckRequire.Clear(); reply->ItemCheckExclude.Clear(); // If the first item check has a positive amount required, then // add that to the reply string. Otherwise, use the reply as-is. - reply->Reply = copystring (rsp->Reply); + reply->Reply = rsp->Reply; reply->NeedsGold = (rsp->Count[0] > 0); // QuickYes messages are shown when you meet the item checks. // QuickNo messages are shown when you don't. if (rsp->Yes[0] == '_' && rsp->Yes[1] == 0) { - reply->QuickYes = NULL; + reply->QuickYes = ""; } else { - reply->QuickYes = ncopystring (rsp->Yes); + reply->QuickYes = rsp->Yes; } if (reply->ItemCheck[0].Item != 0) { - reply->QuickNo = ncopystring (rsp->No); + reply->QuickNo = rsp->No; } else { - reply->QuickNo = NULL; + reply->QuickNo = ""; } reply->Next = *replyptr; *replyptr = reply; @@ -560,9 +561,6 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses) FStrifeDialogueNode::~FStrifeDialogueNode () { - if (SpeakerName != NULL) delete[] SpeakerName; - if (Dialogue != NULL) delete[] Dialogue; - if (Goodbye != nullptr) delete[] Goodbye; FStrifeDialogueReply *tokill = Children; while (tokill != NULL) { @@ -580,9 +578,6 @@ FStrifeDialogueNode::~FStrifeDialogueNode () FStrifeDialogueReply::~FStrifeDialogueReply () { - if (Reply != NULL) delete[] Reply; - if (QuickYes != NULL) delete[] QuickYes; - if (QuickNo != NULL) delete[] QuickNo; } //============================================================================ @@ -672,7 +667,7 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) static bool ShouldSkipReply(FStrifeDialogueReply *reply, player_t *player) { - if (reply->Reply == nullptr) + if (reply->Reply.IsEmpty()) return true; int i; @@ -772,7 +767,7 @@ public: ReplyText = GStrings(ReplyText + 1); } FString ReplyString = ReplyText; - if (reply->NeedsGold) ReplyString.AppendFormat(" for %u", reply->ItemCheck[0].Amount); + if (reply->NeedsGold) ReplyString.AppendFormat(" for %u", reply->PrintAmount); FBrokenLines *ReplyLines = V_BreakLines (SmallFont, 320-50-10, ReplyString); @@ -785,7 +780,7 @@ public: V_FreeBrokenLines (ReplyLines); } const char *goodbyestr = CurNode->Goodbye; - if (goodbyestr == nullptr) + if (*goodbyestr == 0) { char goodbye[25]; mysnprintf(goodbye, countof(goodbye), "TXT_RANDOMGOODBYE_%d", 1 + (pr_randomspeech() % NUM_RANDOM_GOODBYES)); @@ -1012,7 +1007,7 @@ public: linesize = 10 * CleanYfac; // Who is talking to you? - if (CurNode->SpeakerName != NULL) + if (CurNode->SpeakerName.IsNotEmpty()) { speakerName = CurNode->SpeakerName; if (speakerName[0] == '$') speakerName = GStrings(speakerName+1); @@ -1097,7 +1092,7 @@ public: if (response == mSelection+1) { - int color = ((DMenu::MenuTime%8) < 4) || DMenu::CurrentMenu != this ? CR_RED:CR_GREY; + int color = ((MenuTime%8) < 4) || CurrentMenu != this ? CR_RED:CR_GREY; x = (50 + 3 - 160) * CleanXfac + screen->GetWidth() / 2; int yy = (y + fontheight/2 - 5 - 100) * CleanYfac + screen->GetHeight() / 2; @@ -1135,9 +1130,9 @@ void P_FreeStrifeConversations () ClassRoots.Clear(); PrevNode = NULL; - if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu))) + if (CurrentMenu != NULL && CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu))) { - DMenu::CurrentMenu->Close(); + CurrentMenu->Close(); } } @@ -1315,7 +1310,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply if (!CheckStrifeItem(player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount)) { // No, you don't. Say so and let the NPC animate negatively. - if (reply->QuickNo && isconsole) + if (reply->QuickNo.IsNotEmpty() && isconsole) { TerminalResponse(reply->QuickNo); } @@ -1396,7 +1391,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply } // Update the quest log, if needed. - if (reply->LogString != NULL) + if (reply->LogString.IsNotEmpty()) { const char *log = reply->LogString; if (log[0] == '$') @@ -1482,10 +1477,10 @@ void P_ConversationCommand (int netcode, int pnum, BYTE **stream) // The conversation menus are normally closed by the menu code, but that // doesn't happen during demo playback, so we need to do it here. - if (demoplayback && DMenu::CurrentMenu != NULL && - DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu))) + if (demoplayback && CurrentMenu != NULL && + CurrentMenu->IsKindOf(RUNTIME_CLASS(DConversationMenu))) { - DMenu::CurrentMenu->Close(); + CurrentMenu->Close(); } if (netcode == DEM_CONVREPLY) { diff --git a/src/p_conversation.h b/src/p_conversation.h index 8771d95ae..c10c4c697 100644 --- a/src/p_conversation.h +++ b/src/p_conversation.h @@ -26,11 +26,11 @@ struct FStrifeDialogueNode int ItemCheckNode; // index into StrifeDialogues PClassActor *SpeakerType; - char *SpeakerName; + FString SpeakerName; FSoundID SpeakerVoice; FTextureID Backdrop; - char *Dialogue; - char *Goodbye = nullptr; // must init to null for binary scripts to work as intended + FString Dialogue; + FString Goodbye; // must init to null for binary scripts to work as intended FStrifeDialogueReply *Children; }; @@ -44,15 +44,16 @@ struct FStrifeDialogueReply PClassActor *GiveType; int ActionSpecial; int Args[5]; + int PrintAmount; TArray ItemCheck; TArray ItemCheckRequire; TArray ItemCheckExclude; - char *Reply; - char *QuickYes; + FString Reply; + FString QuickYes; + FString QuickNo; + FString LogString; int NextNode; // index into StrifeDialogues int LogNumber; - char *LogString; - char *QuickNo; bool NeedsGold; }; diff --git a/src/p_usdf.cpp b/src/p_usdf.cpp index f4ebe5ebd..b8e290deb 100644 --- a/src/p_usdf.cpp +++ b/src/p_usdf.cpp @@ -232,20 +232,21 @@ class USDFParser : public UDMFParserBase // Todo: Finalize if (reply->ItemCheck.Size() > 0) { - if (reply->ItemCheck[0].Amount <= 0) reply->NeedsGold = false; + reply->PrintAmount = reply->ItemCheck[0].Amount; + if (reply->PrintAmount <= 0) reply->NeedsGold = false; } - reply->Reply = ncopystring(ReplyString); - reply->QuickYes = ncopystring(QuickYes); + reply->Reply = ReplyString; + reply->QuickYes = QuickYes; if (reply->ItemCheck.Size() > 0 && reply->ItemCheck[0].Item != NULL) { - reply->QuickNo = ncopystring(QuickNo); + reply->QuickNo = QuickNo; } else { - reply->QuickNo = NULL; + reply->QuickNo = ""; } - reply->LogString = ncopystring(LogString); + reply->LogString = LogString; if(!closeDialog) reply->NextNode *= -1; return true; } @@ -373,9 +374,9 @@ class USDFParser : public UDMFParserBase } } } - node->SpeakerName = ncopystring(SpeakerName); - node->Dialogue = ncopystring(Dialogue); - node->Goodbye = ncopystring(Goodbye); + node->SpeakerName = SpeakerName; + node->Dialogue = Dialogue; + node->Goodbye = Goodbye; return true; } diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index 45167580f..d6d6aa4e1 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -802,7 +802,7 @@ void R_InitSkins (void) if (spr == 0 && maxframe <= 0) { - Printf (PRINT_BOLD, "Skin %s (#%u) has no frames. Removing.\n", Skins[i].Name, i); + Printf (PRINT_BOLD, "Skin %s (#%u) has no frames. Removing.\n", Skins[i].Name.GetChars(), i); remove = true; break; } diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 4fb167f56..3808194e1 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -906,7 +906,10 @@ void InitThingdef() fieldptr = new PField("OptionMenuSettings", NewStruct("FOptionMenuSettings", nullptr), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&OptionSettings); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); - + fieldptr = new PField("gametic", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&gametic); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + + // Argh. It sucks when bad hacks need to be supported. WP_NOCHANGE is just a bogus pointer but it used everywhere as a special flag. // It cannot be defined as constant because constants can either be numbers or strings but nothing else, so the only 'solution' // is to create a static variable from it and reference that in the script. Yuck!!! @@ -1213,10 +1216,12 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Mid) ACTION_RETURN_STRING(s); } -DEFINE_ACTION_FUNCTION(FStringStruct, Len) +DEFINE_ACTION_FUNCTION(FStringStruct, Truncate) { PARAM_SELF_STRUCT_PROLOGUE(FString); - ACTION_RETURN_INT((int)self->Len()); + PARAM_UINT(len); + self->Truncate(len); + return 0; } // CharAt and CharCodeAt is how JS does it, and JS is similar here in that it doesn't have char type as int. @@ -1239,3 +1244,10 @@ DEFINE_ACTION_FUNCTION(FStringStruct, CharCodeAt) ACTION_RETURN_INT(0); ACTION_RETURN_INT((*self)[pos]); } + +DEFINE_ACTION_FUNCTION(FStringStruct, Filter) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + ACTION_RETURN_STRING(strbin1(*self)); +} + diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index aa191c34e..cda7a1c58 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -8,6 +8,8 @@ #include "doomerrors.h" #include "memarena.h" +class DObject; + extern FMemArena ClassDataAllocator; #define MAX_RETURNS 8 // Maximum number of results a function called by script code can return @@ -1028,6 +1030,7 @@ void NullParam(const char *varname); #define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass()); #define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass()); #define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a; +#define PARAM_POINTERTYPE_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type x = (type )param[p].a; #define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); #define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); #define PARAM_POINTER_NOT_NULL_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)PARAM_NULLCHECK(param[p].a, #x); @@ -1072,6 +1075,7 @@ void NullParam(const char *varname); #define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x) #define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x) #define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type) +#define PARAM_POINTERTYPE(x,type) ++paramnum; PARAM_POINTERTYPE_AT(paramnum,x,type) #define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type) #define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base) #define PARAM_POINTER_NOT_NULL(x,type) ++paramnum; PARAM_POINTER_NOT_NULL_AT(paramnum,x,type) diff --git a/src/v_video.cpp b/src/v_video.cpp index 56f45c316..2a804c392 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -337,6 +337,20 @@ void DCanvas::Dim (PalEntry color) Dim (dimmer, amount, 0, 0, Width, Height); } +DEFINE_ACTION_FUNCTION(_Screen, Dim) +{ + PARAM_PROLOGUE; + PARAM_INT(color); + PARAM_FLOAT(amount); + PARAM_INT(x1); + PARAM_INT(y1); + PARAM_INT(w); + PARAM_INT(h); + screen->Dim(color, float(amount), x1, y1, w, h); + return 0; +} + + //========================================================================== // // DCanvas :: GetScreenshotBuffer diff --git a/src/version.h b/src/version.h index 8d6cb67e0..67e952a68 100644 --- a/src/version.h +++ b/src/version.h @@ -100,7 +100,4 @@ const char *GetVersionString(); #endif -// The maximum length of one save game description for the menus. -#define SAVESTRINGSIZE 24 - #endif //__VERSION_H__ diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index ae582e058..cbe913fa3 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -9,17 +9,20 @@ #include "zscript/menu/menuitembase.txt" #include "zscript/menu/menu.txt" +#include "zscript/menu/messagebox.txt" #include "zscript/menu/listmenu.txt" #include "zscript/menu/listmenuitems.txt" #include "zscript/menu/optionmenu.txt" #include "zscript/menu/optionmenuitems.txt" #include "zscript/menu/colorpickermenu.txt" #include "zscript/menu/joystickmenu.txt" +#include "zscript/menu/loadsavemenu.txt" #include "zscript/menu/playermenu.txt" #include "zscript/menu/playerdisplay.txt" #include "zscript/menu/playercontrols.txt" #include "zscript/menu/textentermenu.txt" #include "zscript/menu/videomenu.txt" +#include "zscript/menu/readthis.txt" #include "zscript/inventory/inventory.txt" #include "zscript/inventory/inv_misc.txt" diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 57bdc7b01..fcad86814 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -158,15 +158,11 @@ enum DrawTextureTags struct Screen native { - int CleanWidth, CleanHeight; - int CleanXFac, CleanYFac; - int CleanWidth_1, CleanHeight_1; - int CleanXFac_1, CleanYFac_1; - native static Color PaletteColor(int index); native static int GetWidth(); native static int GetHeight(); native static void Clear(int left, int top, int right, int bottom, Color color, int palcolor = -1); + native static void Dim(Color col, double amount, int x, int y, int w, int h); native static void DrawHUDTexture(TextureID tex, double x, double y); native static vararg void DrawTexture(TextureID tex, bool animate, double x, double y, ...); @@ -175,13 +171,6 @@ struct Screen native native static void DrawFrame(int x, int y, int w, int h); } -class BrokenLines : Object native -{ - native int Count(); - native int StringWidth(int line); - native String StringAt(int line); -} - struct Font native { enum EColorRange @@ -252,7 +241,7 @@ struct Font native native static int FindFontColor(Name color); native static Font FindFont(Name fontname); native static Font GetFont(Name fontname); - native static BrokenLines BreakLines(String text, int maxlen); + native BrokenLines BreakLines(String text, int maxlen); } struct Translation @@ -310,6 +299,7 @@ struct GameInfoStruct native native String ArmorIcon2; native int gametype; native bool norandomplayerclass; + native Array infoPages; } class Object native @@ -336,6 +326,13 @@ class Object native virtual void OnDestroy() {} } +class BrokenLines : Object native +{ + native int Count(); + native int StringWidth(int line); + native String StringAt(int line); +} + class Thinker : Object native { enum EStatnums @@ -440,6 +437,7 @@ struct LevelLocals native native readonly String MapName; native String NextMap; native String NextSecretMap; + native String F1Pic; native readonly int maptype; native readonly String Music; native readonly int musicorder; @@ -582,9 +580,10 @@ struct StringStruct native native void Replace(String pattern, String replacement); native String Mid(int pos = 0, int len = 2147483647); - native int Len(); + native void Truncate(int newlen); native String CharAt(int pos); native int CharCodeAt(int pos); + native String Filter(); } class Floor : Thinker native diff --git a/wadsrc/static/zscript/menu/listmenu.txt b/wadsrc/static/zscript/menu/listmenu.txt index d920dc26c..db1a4a19c 100644 --- a/wadsrc/static/zscript/menu/listmenu.txt +++ b/wadsrc/static/zscript/menu/listmenu.txt @@ -40,10 +40,10 @@ class ListMenuDescriptor : MenuDescriptor native // //============================================================================= -class ListMenu : Menu native +class ListMenu : Menu { - native ListMenuDescriptor mDesc; - native MenuItemBase mFocusControl; + ListMenuDescriptor mDesc; + MenuItemBase mFocusControl; virtual void Init(Menu parent = NULL, ListMenuDescriptor desc = NULL) { @@ -76,7 +76,13 @@ class ListMenu : Menu native } } - MenuItemBase GetItem(Name name) + //============================================================================= + // + // + // + //============================================================================= + + ListMenuItem GetItem(Name name) { for(int i = 0; i < mDesc.mItems.Size(); i++) { @@ -86,12 +92,173 @@ class ListMenu : Menu native return NULL; } - //bool Responder (InputEventData ev); - //bool MenuEvent (int mkey, bool fromcontroller); - //bool MouseEvent(int type, int x, int y); - //void Ticker (); - //void Drawer (); + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + // tolower + int ch = ev.data1; + ch = ch >= 65 && ch <91? ch + 32 : ch; + + for(int i = mDesc.mSelectedItem + 1; i < mDesc.mItems.Size(); i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) + { + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; + } + } + for(int i = 0; i < mDesc.mSelectedItem; i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) + { + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; + } + } + } + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + int oldSelect = mDesc.mSelectedItem; + int startedAt = mDesc.mSelectedItem; + if (startedAt < 0) startedAt = 0; + + 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); + if (mDesc.mSelectedItem == startedAt) mDesc.mSelectedItem = oldSelect; + MenuSound("menu/cursor"); + return true; + + case MKEY_Down: + do + { + if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) mDesc.mSelectedItem = 0; + } + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt); + if (mDesc.mSelectedItem == startedAt) mDesc.mSelectedItem = oldSelect; + MenuSound("menu/cursor"); + return true; + + case MKEY_Enter: + if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].Activate()) + { + MenuSound("menu/choose"); + } + return true; + + default: + return Super.MenuEvent(mkey, fromcontroller); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + 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; + + 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(int i=0;i= 0 && mDesc.mSelectedItem < mDesc.mItems.Size()) + mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector); + Super.Drawer(); + } + //============================================================================= + // + // + // + //============================================================================= + override void SetFocus(MenuItemBase fc) { mFocusControl = fc; @@ -106,3 +273,4 @@ class ListMenu : Menu native } } + diff --git a/wadsrc/static/zscript/menu/listmenuitems.txt b/wadsrc/static/zscript/menu/listmenuitems.txt index 8920e617b..e3eec6dd1 100644 --- a/wadsrc/static/zscript/menu/listmenuitems.txt +++ b/wadsrc/static/zscript/menu/listmenuitems.txt @@ -52,7 +52,7 @@ class ListMenuItem : MenuItemBase } else { - screen.DrawTexture (tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true); + screen.DrawTexture (tex, true, mXpos + xofs, mYpos + yofs, DTA_Clean, true); } } } diff --git a/wadsrc/static/zscript/menu/loadsavemenu.txt b/wadsrc/static/zscript/menu/loadsavemenu.txt new file mode 100644 index 000000000..34d56f374 --- /dev/null +++ b/wadsrc/static/zscript/menu/loadsavemenu.txt @@ -0,0 +1,577 @@ +/* +** loacpp +** The load game and save game menus +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + + +struct SaveGameNode native +{ + native String SaveTitle; + native String Filename; + native bool bOldVersion; + native bool bMissingWads; + native bool bNoDelete; +} + +struct SavegameManager native +{ + native int WindowSize; + native SaveGameNode quickSaveSlot; + + native static SavegameManager GetManager(); + native void ReadSaveStrings(); + native void UnloadSaveData(); + + native int RemoveSaveSlot(int index); + native void LoadSavegame(int Selected); + native void DoSave(int Selected, String savegamestring); + native int ExtractSaveData(int index); + native void ClearSaveStuff(); + native bool DrawSavePic(int x, int y, int w, int h); + native void DrawSaveComment(Font font, int cr, int x, int y, int scalefactor); + native void SetFileInfo(int Selected); + native int SavegameCount(); + native SaveGameNode GetSavegame(int i); + native void InsertNewSaveNode(); + native bool RemoveNewSaveNode(); + +} + + + +class LoadSaveMenu : ListMenu +{ + SavegameManager manager; + int TopItem; + int Selected; + + 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; + + bool mEntering; + TextEnterMenu mInput; + + + + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + manager = SavegameManager.GetManager(); + manager.ReadSaveStrings(); + + savepicLeft = 10; + savepicTop = 54*CleanYfac; + savepicWidth = 216*screen.GetWidth()/640; + savepicHeight = 135*screen.GetHeight()/400; + manager.WindowSize = savepicWidth / CleanXfac; + + rowHeight = (SmallFont.GetHeight() + 1) * CleanYfac; + listboxLeft = savepicLeft + savepicWidth + 14; + listboxTop = savepicTop; + listboxWidth = screen.GetWidth() - listboxLeft - 10; + int listboxHeight1 = screen.GetHeight() - listboxTop - 10; + listboxRows = (listboxHeight1 - 1) / rowHeight; + listboxHeight = listboxRows * rowHeight + 1; + listboxRight = listboxLeft + listboxWidth; + listboxBottom = listboxTop + listboxHeight; + + commentLeft = savepicLeft; + commentTop = savepicTop + savepicHeight + 16; + commentWidth = savepicWidth; + commentHeight = (51+(screen.GetHeight()>200?10:0))*CleanYfac; + commentRight = commentLeft + commentWidth; + commentBottom = commentTop + commentHeight; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void OnDestroy() + { + manager.ClearSaveStuff (); + Super.OnDestroy(); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + Super.Drawer(); + + SaveGameNode node; + int i; + int j; + bool didSeeSelected = false; + + // Draw picture area + if (gameaction == ga_loadgame || gameaction == ga_loadgamehidecon || gameaction == ga_savegame) + { + return; + } + + Screen.DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); + if (!manager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) + { + screen.Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); + + if (manager.SavegameCount() > 0) + { + String text = (Selected == -1 || !manager.GetSavegame(Selected).bOldVersion)? Stringtable.Localize("$MNU_NOPICTURE") : Stringtable.Localize("$MNU_DIFFVERSION"); + int textlen = SmallFont.StringWidth(text) * CleanXfac; + + screen.DrawText (SmallFont, Font.CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, + savepicTop+(savepicHeight-rowHeight)/2, text, DTA_CleanNoMove, true); + } + } + + // Draw comment area + Screen.DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); + screen.Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); + + manager.DrawSaveComment(SmallFont, Font.CR_GOLD, commentLeft, commentTop, CleanYfac); + + // Draw file area + Screen.DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); + screen.Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + + if (manager.SavegameCount() == 0) + { + String text = Stringtable.Localize("$MNU_NOFILES"); + int textlen = SmallFont.StringWidth(text) * CleanXfac; + + screen.DrawText (SmallFont, Font.CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, listboxTop+(listboxHeight-rowHeight)/2, text, DTA_CleanNoMove, true); + return; + } + + j = TopItem; + for (i = 0; i < listboxRows && j < manager.SavegameCount(); i++) + { + int colr; + node = manager.GetSavegame(j); + if (node.bOldVersion) + { + colr = Font.CR_BLUE; + } + else if (node.bMissingWads) + { + colr = Font.CR_ORANGE; + } + else if (j == Selected) + { + colr = Font.CR_WHITE; + } + else + { + colr = Font.CR_TAN; + } + + if (j == Selected) + { + screen.Clear (listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), mEntering ? Color(255,255,0,0) : Color(255,0,0,255)); + didSeeSelected = true; + if (!mEntering) + { + screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true); + } + else + { + String s = mInput.GetText() .. SmallFont.GetCursor(); + screen.DrawText (SmallFont, Font.CR_WHITE, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, DTA_CleanNoMove, true); + } + } + else + { + screen.DrawText (SmallFont, colr, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node.SaveTitle, DTA_CleanNoMove, true); + } + j++; + } + } + + + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + 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 = MAX(0, Selected - listboxRows + 1); + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + 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 = MAX(0, Selected - listboxRows + 1); + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_PageDown: + if (manager.SavegameCount() > 1) + { + if (TopItem >= manager.SavegameCount() - listboxRows) + { + TopItem = 0; + if (Selected != -1) Selected = 0; + } + else + { + TopItem = MIN(TopItem + listboxRows, manager.SavegameCount() - listboxRows); + if (TopItem > Selected && Selected != -1) Selected = TopItem; + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + return true; + + case MKEY_PageUp: + if (manager.SavegameCount() > 1) + { + if (TopItem == 0) + { + TopItem = MAX(0, manager.SavegameCount() - listboxRows); + if (Selected != -1) Selected = TopItem; + } + else + { + TopItem = MAX(TopItem - listboxRows, 0); + if (Selected >= TopItem + listboxRows) Selected = TopItem; + } + manager.UnloadSaveData (); + manager.ExtractSaveData (Selected); + } + 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); + } + return true; + } + + default: + return Super.MenuEvent(mkey, fromcontroller); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + 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); + if (type == MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + return true; + } + } + } + else Selected = -1; + } + else Selected = -1; + + return Super.MouseEvent(type, x, y); + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (Selected < manager.SavegameCount()) + { + switch (ev.data1) + { + case UIEvent.Key_F1: + manager.SetFileInfo(Selected); + return true; + + case UIEvent.Key_DEL: + { + String EndString; + EndString = String.Format("%s%s%s%s?\n\n%s", Stringtable.Localize("$MNU_DELETESG"), TEXTCOLOR_WHITE, manager.GetSavegame(Selected).SaveTitle, TEXTCOLOR_NORMAL, Stringtable.Localize("$PRESSYN")); + StartMessage (EndString, 0); + } + return true; + } + } + } + else if (ev.subtype == InputEventData.GUI_WheelUp) + { + if (TopItem > 0) TopItem--; + return true; + } + else if (ev.subtype == InputEventData.GUI_WheelDown) + { + if (TopItem < manager.SavegameCount() - listboxRows) TopItem++; + return true; + } + } + return Super.Responder(ev); + } + + +} + +class SaveMenu : LoadSaveMenu +{ + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + manager.InsertNewSaveNode(); + TopItem = 0; + Selected = manager.ExtractSaveData (-1); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void OnDestroy() + { + if (manager.RemoveNewSaveNode()) + { + Selected--; + } + Super.OnDestroy(); + } + + //============================================================================= + // + // + // + //============================================================================= + const SAVESTRINGSIZE = 32; + + override bool MenuEvent (int mkey, bool fromcontroller) + { + + if (Super.MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (Selected == -1) + { + return false; + } + + if (mkey == MKEY_Enter) + { + String SavegameString = (Selected != 0)? manager.GetSavegame(Selected).SaveTitle : ""; + mInput = TextEnterMenu.Open(self, SavegameString, SAVESTRINGSIZE, 1, fromcontroller); + mInput.ActivateMenu(); + mEntering = true; + } + else if (mkey == MKEY_Input) + { + mEntering = false; + manager.DoSave(Selected, mInput.GetText()); + mInput = null; + } + else if (mkey == MKEY_Abort) + { + mEntering = false; + mInput = null; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEventData ev) + { + if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (Selected != -1) + { + switch (ev.data1) + { + case UIEvent.Key_DEL: + // cannot delete 'new save game' item + if (Selected == 0) return true; + break; + + case 78://'N': + Selected = TopItem = 0; + manager.UnloadSaveData (); + return true; + } + } + } + return Super.Responder(ev); + } + + + +} + +//============================================================================= +// +// +// +//============================================================================= + +class LoadMenu : LoadSaveMenu +{ + //============================================================================= + // + // + // + //============================================================================= + + override void Init(Menu parent, ListMenuDescriptor desc) + { + Super.Init(parent, desc); + TopItem = 0; + Selected = manager.ExtractSaveData (-1); + + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (Super.MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (Selected == -1 || manager.SavegameCount() == 0) + { + return false; + } + + if (mkey == MKEY_Enter) + { + manager.LoadSavegame(Selected); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index 1640e9d35..2f5d9454a 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -87,6 +87,8 @@ class Menu : Object native }; native Menu mParentMenu; + native bool mMouseCapture; + native bool mBackbuttonSelected; void Init(Menu parent) { @@ -111,7 +113,6 @@ class Menu : Object native native virtual void Ticker(); native virtual void Drawer(); native void Close(); - native MenuItemBase GetItem(Name n); native void ActivateMenu(); static void MenuSound(Sound snd) diff --git a/wadsrc/static/zscript/menu/messagebox.txt b/wadsrc/static/zscript/menu/messagebox.txt new file mode 100644 index 000000000..deb73046e --- /dev/null +++ b/wadsrc/static/zscript/menu/messagebox.txt @@ -0,0 +1,293 @@ +/* +** messagebox.cpp +** Confirmation, notification screns +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +class MessageBoxMenu : Menu +{ + BrokenLines mMessage; + voidptr Handler; + int mMessageMode; + int messageSelection; + int mMouseLeft, mMouseRight, mMouseY; + Name mAction; + + native static void CallHandler(voidptr hnd); + + + //============================================================================= + // + // + // + //============================================================================= + + void Init(Menu parent, String message, int messagemode, bool playsound = false, Name cmd = 'None', voidptr native_handler = null) + { + Super.Init(parent); + mAction = cmd; + messageSelection = 0; + mMouseLeft = 140; + mMouseY = 0x80000000; + int mr1 = 170 + SmallFont.StringWidth(Stringtable.Localize("$TXT_YES")); + int mr2 = 170 + SmallFont.StringWidth(Stringtable.Localize("TXT_NO")); + mMouseRight = MAX(mr1, mr2); + mParentMenu = parent; + mMessage = SmallFont.BreakLines(message, 300); + mMessageMode = messagemode; + if (playsound) + { + MenuSound ("menu/prompt"); + } + Handler = native_handler; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + int i, y; + + int fontheight = SmallFont.GetHeight(); + + y = 100; + + int c = mMessage.Count(); + for (i = 0; i < c; i++) + y -= SmallFont.GetHeight () / 2; + + for (i = 0; i < c; i++) + { + screen.DrawText (SmallFont, Font.CR_UNTRANSLATED, 160 - mMessage.StringWidth(i)/2, y, mMessage.StringAt(i), DTA_Clean, true); + y += fontheight; + } + + if (mMessageMode == 0) + { + y += fontheight; + mMouseY = y; + screen.DrawText(SmallFont, messageSelection == 0? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y, Stringtable.Localize("$TXT_YES"), DTA_Clean, true); + screen.DrawText(SmallFont, messageSelection == 1? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, 160, y + fontheight + 1, Stringtable.Localize("$TXT_NO"), DTA_Clean, true); + + if (messageSelection >= 0) + { + if ((MenuTime() % 8) < 6) + { + screen.DrawText(ConFont, OptionMenuSettings.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); + } + } + } + } + + + //============================================================================= + // + // + // + //============================================================================= + + protected void CloseSound() + { + MenuSound (GetCurrentMenu() != NULL? "menu/backup" : "menu/dismiss"); + } + + //============================================================================= + // + // + // + //============================================================================= + + virtual void HandleResult(bool res) + { + if (Handler != null) + { + if (res) + { + CallHandler(Handler); + } + else + { + Close(); + CloseSound(); + } + } + else if (mParentMenu != NULL) + { + if (mMessageMode == 0) + { + if (mAction == 'None') + { + mParentMenu.MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false); + Close(); + } + else + { + Close(); + if (res) SetMenu(mAction, -1); + } + CloseSound(); + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder(InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_KeyDown) + { + if (mMessageMode == 0) + { + // tolower + int ch = ev.data1; + ch = ch >= 65 && ch <91? ch + 32 : ch; + + if (ch == 78 /*'n'*/ || ch == 32) + { + HandleResult(false); + return true; + } + else if (ch == 89 /*'y'*/) + { + HandleResult(true); + return true; + } + } + else + { + Close(); + return true; + } + return false; + } + else if (ev.type == InputEventData.KeyDown) + { + Close(); + return true; + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mMessageMode == 0) + { + if (mkey == MKEY_Up || mkey == MKEY_Down) + { + MenuSound("menu/cursor"); + 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; + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + if (mMessageMode == 1) + { + 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) + { + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + messageSelection = sel; + if (type == MOUSE_Release) + { + return MenuEvent(MKEY_Enter, true); + } + return true; + } + } + + +} + + + diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index 5b06f3020..c6f0247a7 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -56,8 +56,6 @@ class OptionMenuDescriptor : MenuDescriptor native native int mPosition; native bool mDontDim; - native void CalcIndent(); - native OptionMenuItem GetItem(Name iname); void Reset() { // Reset the default settings (ignore all other values in the struct) @@ -66,6 +64,25 @@ class OptionMenuDescriptor : MenuDescriptor native mIndent = 0; mDontDim = 0; } + + //============================================================================= + // + // + // + //============================================================================= + + void CalcIndent() + { + // calculate the menu indent + int widest = 0, thiswidth; + + for (int i = 0; i < mItems.Size(); i++) + { + thiswidth = mItems[i].GetIndent(); + if (thiswidth > widest) widest = thiswidth; + } + mIndent = widest + 4; + } } @@ -88,10 +105,27 @@ class OptionMenu : Menu mParentMenu = parent; mDesc = desc; if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable(); - + mDesc.CalcIndent(); } + //============================================================================= + // + // + // + //============================================================================= + + OptionMenuItem GetItem(Name name) + { + for(int i = 0; i < mDesc.mItems.Size(); i++) + { + Name nm = mDesc.mItems[i].GetAction(); + if (nm == name) return mDesc.mItems[i]; + } + return NULL; + } + + //============================================================================= // // diff --git a/wadsrc/static/zscript/menu/optionmenuitems.txt b/wadsrc/static/zscript/menu/optionmenuitems.txt index 4d61edcd0..9ac308372 100644 --- a/wadsrc/static/zscript/menu/optionmenuitems.txt +++ b/wadsrc/static/zscript/menu/optionmenuitems.txt @@ -136,7 +136,7 @@ class OptionMenuItemCommand : OptionMenuItemSubmenu // don't execute if no menu is active if (m == null) return false; // don't execute if this item cannot be found in the current menu. - if (m.mDesc.GetItem(mAction) != self) return false; + if (m.GetItem(mAction) != self) return false; Menu.MenuSound("menu/choose"); DoCommand(mAction); return true; @@ -388,7 +388,7 @@ class EnterKey : Menu let parent = OptionMenu(mParentMenu); if (parent != null) { - let it = parent.mDesc.GetItem('Controlmessage'); + let it = parent.GetItem('Controlmessage'); if (it != null) { it.SetValue(0, which); diff --git a/wadsrc/static/zscript/menu/readthis.txt b/wadsrc/static/zscript/menu/readthis.txt new file mode 100644 index 000000000..a3db8a8a7 --- /dev/null +++ b/wadsrc/static/zscript/menu/readthis.txt @@ -0,0 +1,124 @@ +/* +** readthis.cpp +** Help screens +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +class ReadThisMenu : Menu +{ + int mScreen; + int mInfoTic; + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer() + { + double alpha; + TextureID tex, prevpic; + + if (mScreen == 0) + { + mScreen = 1; + mInfoTic = gametic; + } + + // Did the mapper choose a custom help page via MAPINFO? + if (level.F1Pic.Length() != 0) + { + tex = TexMan.CheckForTexture(level.F1Pic, TexMan.Type_MiscPatch); + mScreen = 1; + } + + if (!tex.IsValid()) + { + tex = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-1], TexMan.Type_MiscPatch); + } + + if (mScreen > 1) + { + prevpic = TexMan.CheckForTexture(gameinfo.infoPages[mScreen-2], TexMan.Type_MiscPatch); + } + + screen.Dim(0, 1.0, 0,0, screen.GetWidth(), screen.GetHeight()); + alpha = MIN((gametic - mInfoTic) * (3. / Thinker.TICRATE), 1.); + if (alpha < 1. && prevpic.IsValid()) + { + screen.DrawTexture (prevpic, false, 0, 0, DTA_Fullscreen, true); + } + else alpha = 1; + screen.DrawTexture (tex, false, 0, 0, DTA_Fullscreen, true, DTA_Alpha, alpha); + + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == MKEY_Enter) + { + MenuSound("menu/choose"); + mScreen++; + mInfoTic = gametic; + if (level.F1Pic.Length() != 0 || mScreen > gameinfo.infoPages.Size()) + { + Close(); + } + return true; + } + else return Super.MenuEvent(mkey, fromcontroller); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + if (type == MOUSE_Click) + { + return MenuEvent(MKEY_Enter, true); + } + return false; + } + +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/textentermenu.txt b/wadsrc/static/zscript/menu/textentermenu.txt index 7974a62bd..356eba1e6 100644 --- a/wadsrc/static/zscript/menu/textentermenu.txt +++ b/wadsrc/static/zscript/menu/textentermenu.txt @@ -1,15 +1,365 @@ +/* +** menuinput.cpp +** The string input code +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ -// This is only the parts that are needed to make the menu fully work right now. More to come later. -class TextEnterMenu : Menu native + +class TextEnterMenu : Menu { - native bool mInputGridOkay; + const INPUTGRID_WIDTH = 13; + const INPUTGRID_HEIGHT = 5; - native static TextEnterMenu Open(Menu parent, String text, int maxlen, int sizemode, bool fromcontroller); - native String GetText(); + const Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-=.,!?@'\":;[]()<>^#$%&*/_ \b"; + + String mEnterString; + int mEnterSize; + int mEnterPos; + int mSizeMode; // 1: size is length in chars. 2: also check string width + bool mInputGridOkay; + int InputGridX; + int InputGridY; + bool AllowColors; + //============================================================================= + // + // + // + //============================================================================= + + // [TP] Added allowcolors + private void Init(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors) + { + Super.init(parent); + mEnterString = textbuffer; + mEnterSize = maxlen < 0 ? 0x7fffffff : maxlen; + mSizeMode = sizemode; + mInputGridOkay = showgrid || m_showinputgrid; + if (mEnterString.Length() > 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] + } + + static TextEnterMenu Open(Menu parent, String textbuffer, int maxlen, int sizemode, bool showgrid = false, bool allowcolors = false) + { + let me = new("TextEnterMenu"); + me.Init(parent, textbuffer, maxlen, sizemode, showgrid, allowcolors); + return me; + } + + //============================================================================= + // + // + // + //============================================================================= + + String GetText() + { + return mEnterString; + } override bool TranslateKeyboardEvents() { return mInputGridOkay; } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder(InputEventData ev) + { + if (ev.type == InputEventData.GUI_Event) + { + // Save game and player name string input + if (ev.subtype == InputEventData.GUI_Char) + { + mInputGridOkay = false; + if (mEnterString.Length() < mEnterSize && + (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString.AppendFormat("%c", ev.data1); + } + return true; + } + int ch = ev.data1; + if ((ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) && ch == 8) + { + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + } + else if (ev.subtype == InputEventData.GUI_KeyDown) + { + if (ch == UIEvent.Key_ESCAPE) + { + Menu parent = mParentMenu; + Close(); + parent.MenuEvent(MKEY_Abort, false); + return true; + } + else if (ch == 13) + { + if (mEnterString.Length() > 0) + { + // [TP] If we allow color codes, colorize the string now. + if (AllowColors) + mEnterString = mEnterString.Filter(); + + Menu parent = mParentMenu; + parent.MenuEvent(MKEY_Input, false); + Close(); + return true; + } + } + } + if (ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) + { + return true; + } + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + if (mMouseCapture || m_use_mouse == 1) + { + int cell_width = 18 * CleanXfac; + int cell_height = 12 * CleanYfac; + int screen_y = screen.GetHeight() - INPUTGRID_HEIGHT * cell_height; + 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 == MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + MenuSound("menu/choose"); + if (m_use_mouse == 2) InputGridX = InputGridY = -1; + return true; + } + } + } + else + { + InputGridX = InputGridY = -1; + } + } + return Super.MouseEvent(type, x, y); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int key, bool fromcontroller) + { + String InputGridChars = Chars; + 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.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + return true; + + case MKEY_Enter: + if (mInputGridOkay) + { + String c = InputGridChars.CharAt(InputGridX + InputGridY * INPUTGRID_WIDTH); + int ch = c.CharCodeAt(0); + if (ch == 0) // end + { + if (mEnterString.Length() > 0) + { + Menu parent = mParentMenu; + Close(); + parent.MenuEvent(MKEY_Input, false); + return true; + } + } + else if (ch == 8) // bs + { + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + } + else if (mEnterString.Length() < mEnterSize && + (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString = mEnterString .. c; + } + } + return true; + + default: + break; // Keep GCC quiet + } + } + return false; + } + + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + mParentMenu.Drawer(); + if (mInputGridOkay) + { + String InputGridChars = Chars; + int cell_width = 18 * CleanXfac; + int cell_height = 12 * CleanYfac; + int top_padding = cell_height / 2 - SmallFont.GetHeight() * CleanYfac / 2; + + // Darken the background behind the character grid. + screen.Dim(0, 0.8, 0, screen.GetHeight() - INPUTGRID_HEIGHT * cell_height, screen.GetWidth(), INPUTGRID_HEIGHT * cell_height); + + if (InputGridX >= 0 && InputGridY >= 0) + { + // Highlight the background behind the selected character. + screen.Dim(Color(255,248,220), 0.6, + InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen.GetWidth() / 2, + InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen.GetHeight(), + cell_width, cell_height); + } + + for (int y = 0; y < INPUTGRID_HEIGHT; ++y) + { + int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen.GetHeight(); + for (int x = 0; x < INPUTGRID_WIDTH; ++x) + { + int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen.GetWidth() / 2; + int ch = InputGridChars.CharCodeAt(y * INPUTGRID_WIDTH + x); + int width = SmallFont.GetCharWidth(ch); + + // The highlighted character is yellow; the rest are dark gray. + int colr = (x == InputGridX && y == InputGridY) ? Font.CR_YELLOW : Font.CR_DARKGRAY; + Color palcolor = (x == InputGridX && y == InputGridY) ? Color(160, 120, 0) : Color(120, 120, 120); + + if (ch > 32) + { + // Draw a normal character. + screen.DrawChar(SmallFont, colr, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, ch, DTA_CleanNoMove, true); + } + else if (ch == 32) + { + // Draw the space as a box outline. We also draw it 50% wider than it really is. + int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4; + int x2 = x1 + width * 3 * CleanXfac / 2; + int y1 = yy + top_padding; + int y2 = y1 + SmallFont.GetHeight() * CleanYfac; + screen.Clear(x1, y1, x2, y1+CleanYfac, palcolor); // top + screen.Clear(x1, y2, x2, y2+CleanYfac, palcolor); // bottom + screen.Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palcolor); // left + screen.Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palcolor); // right + } + else if (ch == 8 || ch == 0) + { + // Draw the backspace and end "characters". + String str = ch == 8 ? "BS" : "ED"; + screen.DrawText(SmallFont, colr, + xx + cell_width/2 - SmallFont.StringWidth(str)*CleanXfac/2, + yy + top_padding, str, DTA_CleanNoMove, true); + } + } + } + } + Super.Drawer(); + } + } \ No newline at end of file