diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d3fd961e..ae844f588 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -671,7 +671,6 @@ set( FASTMATH_PCH_SOURCES SkylineBinPack.cpp intermission/intermission.cpp intermission/intermission_parse.cpp - menu/colorpickermenu.cpp menu/joystickmenu.cpp menu/listmenu.cpp menu/loadsavemenu.cpp @@ -680,7 +679,6 @@ set( FASTMATH_PCH_SOURCES menu/menuinput.cpp menu/messagebox.cpp menu/optionmenu.cpp - menu/playerdisplay.cpp menu/playermenu.cpp menu/readthis.cpp menu/videomenu.cpp diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 5c1b18323..e6c76ab06 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -44,6 +44,7 @@ #include "i_system.h" #include "d_event.h" #include "w_wad.h" +#include "templates.h" #include #include @@ -261,11 +262,27 @@ static const char *ConfigKeyName(int keynum) // //============================================================================= +DEFINE_ACTION_FUNCTION(FKeyBindings, SetBind) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_INT(k); + PARAM_STRING(cmd); + self->SetBind(k, cmd); + return 0; +} + +//============================================================================= +// +// +// +//============================================================================= + void C_NameKeys (char *str, int first, int second) { int c = 0; *str = 0; + if (second == first) second = 0; if (first) { c++; @@ -284,6 +301,16 @@ void C_NameKeys (char *str, int first, int second) *str = '\0'; } +DEFINE_ACTION_FUNCTION(FKeyBindings, NameKeys) +{ + PARAM_PROLOGUE; + PARAM_INT(k1); + PARAM_INT(k2); + char buffer[120]; + C_NameKeys(buffer, k1, k2); + ACTION_RETURN_STRING(buffer); +} + //============================================================================= // // @@ -445,6 +472,17 @@ int FKeyBindings::GetKeysForCommand (const char *cmd, int *first, int *second) return c; } +DEFINE_ACTION_FUNCTION(FKeyBindings, GetKeysForCommand) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_STRING(cmd); + int k1, k2, c; + self->GetKeysForCommand(cmd.GetChars(), &k1, &k2); + if (numret > 0) ret[0].SetInt(k1); + if (numret > 1) ret[1].SetInt(k2); + return MIN(numret, 2); +} + //============================================================================= // // @@ -464,6 +502,14 @@ void FKeyBindings::UnbindACommand (const char *str) } } +DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_STRING(cmd); + self->UnbindACommand(cmd); + return 0; +} + //============================================================================= // // diff --git a/src/c_console.cpp b/src/c_console.cpp index 493625bf9..42b6034f2 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1332,6 +1332,14 @@ DEFINE_ACTION_FUNCTION(_Console, HideConsole) return 0; } +DEFINE_ACTION_FUNCTION(_Console, Printf) +{ + PARAM_PROLOGUE; + FString s = FStringFormat(param, defaultparam, numparam, ret, numret); + Printf("%s\n", s.GetChars()); + return 0; +} + static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer) { int data1 = ev->data1; diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 768392c6c..c79a76fb3 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -192,7 +192,7 @@ DEFINE_ACTION_FUNCTION(_CVar, GetFloat) { PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); auto v = self->GetGenericRep(CVAR_Float); - ACTION_RETURN_FLOAT(v.Int); + ACTION_RETURN_FLOAT(v.Float); } DEFINE_ACTION_FUNCTION(_CVar, GetString) @@ -202,6 +202,36 @@ DEFINE_ACTION_FUNCTION(_CVar, GetString) ACTION_RETURN_STRING(v.String); } +DEFINE_ACTION_FUNCTION(_CVar, SetInt) +{ + PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); + PARAM_INT(val); + UCVarValue v; + v.Int = val; + self->SetGenericRep(v, CVAR_Int); + return 0; +} + +DEFINE_ACTION_FUNCTION(_CVar, SetFloat) +{ + PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); + PARAM_FLOAT(val); + UCVarValue v; + v.Float = val; + self->SetGenericRep(v, CVAR_Float); + return 0; +} + +DEFINE_ACTION_FUNCTION(_CVar, SetString) +{ + PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); + PARAM_STRING(val); + UCVarValue v; + v.String = val.GetChars(); + self->SetGenericRep(v, CVAR_String); + return 0; +} + bool FBaseCVar::ToBool (UCVarValue value, ECVarType type) { switch (type) @@ -643,6 +673,12 @@ void FBaseCVar::DisableCallbacks () m_UseCallback = false; } +DEFINE_ACTION_FUNCTION(_CVar, GetRealType) +{ + PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); + ACTION_RETURN_INT(self->GetRealType()); +} + // // Boolean cvar implementation // @@ -1082,6 +1118,13 @@ void FBaseCVar::ResetToDefault () } } +DEFINE_ACTION_FUNCTION(_CVar, ResetToDefault) +{ + PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); + self->ResetToDefault(); + return 0; +} + // // Flag cvar implementation // @@ -1487,6 +1530,13 @@ FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev) return var; } +DEFINE_ACTION_FUNCTION(_CVar, FindCVar) +{ + PARAM_PROLOGUE; + PARAM_NAME(name); + ACTION_RETURN_POINTER(FindCVar(name, nullptr)); +} + FBaseCVar *FindCVarSub (const char *var_name, int namelen) { FBaseCVar *var; diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 52ea3d63a..8bd83ed15 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -662,6 +662,14 @@ void C_DoCommand (const char *cmd, int keynum) } } +DEFINE_ACTION_FUNCTION(_Console, DoCommand) +{ + PARAM_PROLOGUE; + PARAM_STRING(cmd); + C_DoCommand(cmd); + return 0; +} + void AddCommandString (char *cmd, int keynum) { char *brkpt; diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index 266ef47be..f7fb94510 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -614,16 +614,16 @@ int strbin (char *str) case '6': case '7': c = 0; - for (i = 0; i < 3; i++) { - c <<= 3; + for (i = 0; i < 2; i++) + { + p++; if (*p >= '0' && *p <= '7') - c += *p-'0'; + c = (c << 3) + *p - '0'; else { p--; break; } - p++; } *str++ = c; break; @@ -717,16 +717,16 @@ FString strbin1 (const char *start) case '6': case '7': c = 0; - for (i = 0; i < 3; i++) { - c <<= 3; + for (i = 0; i < 2; i++) + { + p++; if (*p >= '0' && *p <= '7') - c += *p-'0'; + c = (c << 3) + *p - '0'; else { p--; break; } - p++; } result << c; break; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 36c32bf5b..9cfd74520 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2293,7 +2293,7 @@ static int PatchStrings (int dummy) holdstring.StripRight(); if (holdstring.Len() > 0 && holdstring[holdstring.Len()-1] == '\\') { - holdstring.Truncate((long)holdstring.Len()-1); + holdstring.Truncate(holdstring.Len()-1); Line2 = igets (); } else diff --git a/src/d_main.cpp b/src/d_main.cpp index fd113d792..8eb3aa6c4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2524,7 +2524,10 @@ void D_DoomMain (void) // Create replacements for dehacked pickups FinishDehPatch(); - + + if (!batchrun) Printf("M_Init: Init menus.\n"); + M_Init(); + // clean up the compiler symbols which are not needed any longer. RemoveUnusedSymbols(); @@ -2542,9 +2545,6 @@ void D_DoomMain (void) bglobal.spawn_tries = 0; bglobal.wanted_botnum = bglobal.getspawned.Size(); - if (!batchrun) Printf ("M_Init: Init menus.\n"); - M_Init (); - if (!batchrun) Printf ("P_Init: Init Playloop state.\n"); StartScreen->LoadingStatus ("Init game engine", 0x3f); AM_StaticInit(); @@ -2831,3 +2831,10 @@ void FStartupScreen::NetMessage(char const *,...) {} void FStartupScreen::NetDone(void) {} bool FStartupScreen::NetLoop(bool (*)(void *),void *) { return false; } +DEFINE_FIELD_X(InputEvent, event_t, type) +DEFINE_FIELD_X(InputEvent, event_t, subtype) +DEFINE_FIELD_X(InputEvent, event_t, data1) +DEFINE_FIELD_X(InputEvent, event_t, data2) +DEFINE_FIELD_X(InputEvent, event_t, data3) +DEFINE_FIELD_X(InputEvent, event_t, x) +DEFINE_FIELD_X(InputEvent, event_t, y) diff --git a/src/dobject.h b/src/dobject.h index 6797d2436..670605093 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -204,6 +204,7 @@ enum EObjectFlags OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk) OF_Released = 1 << 12, // Object was released from the GC system and should not be processed by GC function + OF_Abstract = 1 << 13, // Marks a class that cannot be created with CreateNew }; template class TObjPtr; diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 25e27b846..53ca1f335 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1985,7 +1985,7 @@ void PDynArray::DestroyValue(void *addr) const void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const { - memset((char*)base + offset, 0, sizeof(FArray)); // same as constructing an empty array. + if (base != nullptr) memset((char*)base + offset, 0, sizeof(FArray)); // same as constructing an empty array. if (special != nullptr) { special->Push(std::make_pair(this, offset)); @@ -3047,6 +3047,10 @@ DObject *PClass::CreateNew() const else memset (mem, 0, Size); + if (ConstructNative == nullptr) + { + I_Error("Attempt to instantiate abstract class %s.", TypeName.GetChars()); + } ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); InitializeSpecials(mem, Defaults); @@ -3165,8 +3169,7 @@ void PClass::InitializeDefaults() { // Copy parent values from the parent defaults. assert(ParentClass != nullptr); - ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); - + if (Defaults != nullptr) ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); for (const PField *field : Fields) { if (!(field->Flags & VARF_Native)) diff --git a/src/doomstat.h b/src/doomstat.h index 26df38916..019400cbe 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -102,7 +102,7 @@ EXTERN_CVAR (Float, snd_musicvolume) // maximum volume for music // Status flags for refresh. // -enum EMenuState +enum EMenuState : int { MENU_Off, // Menu is closed MENU_On, // Menu is opened diff --git a/src/g_game.cpp b/src/g_game.cpp index dad118973..aa6b6d93f 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2308,7 +2308,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio WriteZip(filename, savegame_filenames, savegame_content); - M_NotifyNewSave (filename.GetChars(), description, okForQuicksave); + savegameManager.NotifyNewSave (filename.GetChars(), 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_mapinfo.cpp b/src/g_mapinfo.cpp index bb641b23b..cbda6568e 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -589,7 +589,7 @@ bool FMapInfoParser::ParseLookupName(FString &dest) } while (sc.CheckString(",")); // strip off the last newline - dest.Truncate(long(dest.Len()-1)); + dest.Truncate(dest.Len()-1); return false; } } diff --git a/src/menu/colorpickermenu.cpp b/src/menu/colorpickermenu.cpp deleted file mode 100644 index 7fc3e2ecd..000000000 --- a/src/menu/colorpickermenu.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* -** colorpickermenu.cpp -** The color picker menu -** -**--------------------------------------------------------------------------- -** 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 - -#include "menu/menu.h" -#include "c_dispatch.h" -#include "w_wad.h" -#include "sc_man.h" -#include "v_font.h" -#include "g_level.h" -#include "d_player.h" -#include "v_video.h" -#include "gi.h" -#include "i_system.h" -#include "c_bind.h" -#include "v_palette.h" -#include "d_event.h" -#include "d_gui.h" - -#define NO_IMP -#include "menu/optionmenuitems.h" - -class DColorPickerMenu : public DOptionMenu -{ - DECLARE_CLASS(DColorPickerMenu, DOptionMenu) - -public: - - float mRed; - float mGreen; - float mBlue; - - int mGridPosX; - int mGridPosY; - - int mStartItem; - - FColorCVar *mCVar; - - DColorPickerMenu(DMenu *parent, const char *name, DOptionMenuDescriptor *desc, FColorCVar *cvar) - { - mStartItem = desc->mItems.Size(); - mRed = (float)RPART(DWORD(*cvar)); - mGreen = (float)GPART(DWORD(*cvar)); - mBlue = (float)BPART(DWORD(*cvar)); - mGridPosX = 0; - mGridPosY = 0; - mCVar = cvar; - - // This menu uses some featurs that are hard to implement in an external control lump - // so it creates its own list of menu items. - desc->mItems.Resize(mStartItem+8); - desc->mItems[mStartItem+0] = new DOptionMenuItemStaticText(name, false); - desc->mItems[mStartItem+1] = new DOptionMenuItemStaticText(" ", false); - desc->mItems[mStartItem+2] = new DOptionMenuSliderVar("Red", &mRed, 0, 255, 15, 0); - desc->mItems[mStartItem+3] = new DOptionMenuSliderVar("Green", &mGreen, 0, 255, 15, 0); - desc->mItems[mStartItem+4] = new DOptionMenuSliderVar("Blue", &mBlue, 0, 255, 15, 0); - desc->mItems[mStartItem+5] = new DOptionMenuItemStaticText(" ", false); - desc->mItems[mStartItem+6] = new DOptionMenuItemCommand("Undo changes", "undocolorpic"); - desc->mItems[mStartItem+7] = new DOptionMenuItemStaticText(" ", false); - for (auto &p : desc->mItems) - { - GC::WriteBarrier(p); - } - desc->mSelectedItem = mStartItem + 2; - Init(parent, desc); - desc->mIndent = 0; - desc->CalcIndent(); - } - - void OnDestroy() override - { - if (mStartItem >= 0) - { - mDesc->mItems.Resize(mStartItem); - UCVarValue val; - val.Int = MAKERGB(int(mRed), int(mGreen), int(mBlue)); - if (mCVar != NULL) mCVar->SetGenericRep (val, CVAR_Int); - mStartItem = -1; - } - } - - void Reset() - { - mRed = (float)RPART(DWORD(*mCVar)); - mGreen = (float)GPART(DWORD(*mCVar)); - mBlue = (float)BPART(DWORD(*mCVar)); - } - -}; - -IMPLEMENT_CLASS(DColorPickerMenu, true, false) - -CCMD(undocolorpic) -{ - if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DColorPickerMenu))) - { - static_cast(DMenu::CurrentMenu)->Reset(); - } -} - - -DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar) -{ - DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Colorpickermenu); - if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) - { - return new DColorPickerMenu(parent, name, (DOptionMenuDescriptor*)(*desc), cvar); - } - else - { - return NULL; - } -} - - -DEFINE_FIELD(DColorPickerMenu, mRed); -DEFINE_FIELD(DColorPickerMenu, mGreen); -DEFINE_FIELD(DColorPickerMenu, mBlue); -DEFINE_FIELD(DColorPickerMenu, mGridPosX); -DEFINE_FIELD(DColorPickerMenu, mGridPosY); -DEFINE_FIELD(DColorPickerMenu, mStartItem); -DEFINE_FIELD(DColorPickerMenu, mCVar); diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index 61f81bb60..eef4aa3bf 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -51,220 +51,79 @@ #include "i_music.h" #include "m_joy.h" -#define NO_IMP -#include "optionmenuitems.h" - - static TArray Joysticks; IJoystickConfig *SELECTED_JOYSTICK; +DEFINE_ACTION_FUNCTION(DMenu, GetCurrentJoystickConfig) +{ + ACTION_RETURN_POINTER(SELECTED_JOYSTICK); +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, GetSensitivity) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + ACTION_RETURN_FLOAT(self->GetSensitivity()); +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, SetSensitivity) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_FLOAT(sens); + self->SetSensitivity((float)sens); + return 0; +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisScale) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + ACTION_RETURN_FLOAT(self->GetAxisScale(axis)); +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisScale) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + PARAM_FLOAT(sens); + self->SetAxisScale(axis, (float)sens); + return 0; +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisDeadZone) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + ACTION_RETURN_FLOAT(self->GetAxisDeadZone(axis)); +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisDeadZone) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + PARAM_FLOAT(dz); + self->SetAxisDeadZone(axis, (float)dz); + return 0; +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, GetAxisMap) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + ACTION_RETURN_INT(self->GetAxisMap(axis)); +} + +DEFINE_ACTION_FUNCTION(IJoystickConfig, SetAxisMap) +{ + PARAM_SELF_STRUCT_PROLOGUE(IJoystickConfig); + PARAM_INT(axis); + PARAM_INT(map); + self->SetAxisMap(axis, (EJoyAxis)map); + return 0; +} + + DOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy); -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderJoySensitivity : public DOptionMenuSliderBase -{ -public: - DOptionMenuSliderJoySensitivity(const char *label, double min, double max, double step, int showval) - : DOptionMenuSliderBase(label, min, max, step, showval) - { - } - - double GetSliderValue() - { - return SELECTED_JOYSTICK->GetSensitivity(); - } - - void SetSliderValue(double val) - { - SELECTED_JOYSTICK->SetSensitivity(float(val)); - } -}; - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderJoyScale : public DOptionMenuSliderBase -{ - int mAxis; - int mNeg; - -public: - DOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval) - : DOptionMenuSliderBase(label, min, max, step, showval) - { - mAxis = axis; - mNeg = 1; - } - - double GetSliderValue() - { - double d = SELECTED_JOYSTICK->GetAxisScale(mAxis); - mNeg = d < 0? -1:1; - return d; - } - - void SetSliderValue(double val) - { - SELECTED_JOYSTICK->SetAxisScale(mAxis, float(val * mNeg)); - } -}; - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderJoyDeadZone : public DOptionMenuSliderBase -{ - int mAxis; - int mNeg; - -public: - DOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval) - : DOptionMenuSliderBase(label, min, max, step, showval) - { - mAxis = axis; - mNeg = 1; - } - - double GetSliderValue() - { - double d = SELECTED_JOYSTICK->GetAxisDeadZone(mAxis); - mNeg = d < 0? -1:1; - return d; - } - - void SetSliderValue(double val) - { - SELECTED_JOYSTICK->SetAxisDeadZone(mAxis, float(val * mNeg)); - } -}; - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuItemJoyMap : public DOptionMenuItemOptionBase -{ - int mAxis; -public: - - DOptionMenuItemJoyMap(const char *label, int axis, const char *values, int center) - : DOptionMenuItemOptionBase(label, "none", values, NULL, center) - { - mAxis = axis; - } - - int GetSelection() - { - double f = SELECTED_JOYSTICK->GetAxisMap(mAxis); - FOptionValues **opt = OptionValues.CheckKey(mValues); - if (opt != NULL && *opt != NULL) - { - // Map from joystick axis to menu selection. - for(unsigned i = 0; i < (*opt)->mValues.Size(); i++) - { - if (fabs(f - (*opt)->mValues[i].Value) < FLT_EPSILON) - { - return i; - } - } - } - return -1; - } - - void SetSelection(int selection) - { - FOptionValues **opt = OptionValues.CheckKey(mValues); - // Map from menu selection to joystick axis. - if (opt == NULL || *opt == NULL || (unsigned)selection >= (*opt)->mValues.Size()) - { - selection = JOYAXIS_None; - } - else - { - selection = (int)(*opt)->mValues[selection].Value; - } - SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)selection); - } -}; - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuItemInverter : public DOptionMenuItemOptionBase -{ - int mAxis; -public: - - DOptionMenuItemInverter(const char *label, int axis, int center) - : DOptionMenuItemOptionBase(label, "none", "YesNo", NULL, center) - { - mAxis = axis; - } - - int GetSelection() - { - float f = SELECTED_JOYSTICK->GetAxisScale(mAxis); - return f > 0? 0:1; - } - - void SetSelection(int Selection) - { - float f = fabsf(SELECTED_JOYSTICK->GetAxisScale(mAxis)); - if (Selection) f*=-1; - SELECTED_JOYSTICK->SetAxisScale(mAxis, f); - } -}; - -class DJoystickConfigMenu : public DOptionMenu -{ - DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu) -}; - -IMPLEMENT_CLASS(DJoystickConfigMenu, false, false) - -//============================================================================= -// -// Executes a CCMD, action is a CCMD name -// -//============================================================================= - -class DOptionMenuItemJoyConfigMenu : public DOptionMenuItemSubmenu -{ - DECLARE_CLASS(DOptionMenuItemJoyConfigMenu, DOptionMenuItemSubmenu) - IJoystickConfig *mJoy; -public: - DOptionMenuItemJoyConfigMenu(const char *label = nullptr, IJoystickConfig *joy = nullptr) - : DOptionMenuItemSubmenu(label, "JoystickConfigMenu") - { - mJoy = joy; - } - - bool Activate() - { - UpdateJoystickConfigMenu(mJoy); - return DOptionMenuItemSubmenu::Activate(); - } -}; - -IMPLEMENT_CLASS(DOptionMenuItemJoyConfigMenu, false, false) - - /*======================================= * * Joystick Menu @@ -277,12 +136,12 @@ DOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy) if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) { DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc; - DOptionMenuItem *it; + DMenuItemBase *it; opt->mItems.Clear(); if (joy == NULL) { opt->mTitle = "Configure Controller"; - it = new DOptionMenuItemStaticText("Invalid controller specified for menu", false); + it = CreateOptionMenuItemStaticText("Invalid controller specified for menu", false); opt->mItems.Push(it); } else @@ -291,34 +150,34 @@ DOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy) SELECTED_JOYSTICK = joy; - it = new DOptionMenuSliderJoySensitivity("Overall sensitivity", 0, 2, 0.1, 3); + it = CreateOptionMenuSliderJoySensitivity("Overall sensitivity", 0, 2, 0.1, 3); opt->mItems.Push(it); - it = new DOptionMenuItemStaticText(" ", false); + it = CreateOptionMenuItemStaticText(" ", false); opt->mItems.Push(it); if (joy->GetNumAxes() > 0) { - it = new DOptionMenuItemStaticText("Axis Configuration", true); + it = CreateOptionMenuItemStaticText("Axis Configuration", true); opt->mItems.Push(it); for (int i = 0; i < joy->GetNumAxes(); ++i) { - it = new DOptionMenuItemStaticText(" ", false); + it = CreateOptionMenuItemStaticText(" ", false); opt->mItems.Push(it); - it = new DOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false); + it = CreateOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false); opt->mItems.Push(it); - it = new DOptionMenuSliderJoyScale("Overall sensitivity", i, 0, 4, 0.1, 3); + it = CreateOptionMenuSliderJoyScale("Overall sensitivity", i, 0, 4, 0.1, 3); opt->mItems.Push(it); - it = new DOptionMenuItemInverter("Invert", i, false); + it = CreateOptionMenuItemInverter("Invert", i, false); opt->mItems.Push(it); - it = new DOptionMenuSliderJoyDeadZone("Dead Zone", i, 0, 0.9, 0.05, 3); + it = CreateOptionMenuSliderJoyDeadZone("Dead Zone", i, 0, 0.9, 0.05, 3); opt->mItems.Push(it); } } else { - it = new DOptionMenuItemStaticText("No configurable axes", false); + it = CreateOptionMenuItemStaticText("No configurable axes", false); opt->mItems.Push(it); } } @@ -344,7 +203,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected) if (desc != NULL && (*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) { DOptionMenuDescriptor *opt = (DOptionMenuDescriptor *)*desc; - DOptionMenuItem *it; + DMenuItemBase *it; opt->mItems.Clear(); int i; @@ -368,40 +227,40 @@ void UpdateJoystickMenu(IJoystickConfig *selected) } // Todo: Block joystick for changing this one. - it = new DOptionMenuItemOption("Enable controller support", "use_joystick", "YesNo", NULL, false); + it = CreateOptionMenuItemOption("Enable controller support", "use_joystick", "YesNo", NULL, false); opt->mItems.Push(it); #ifdef _WIN32 - it = new DOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false); + it = CreateOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false); opt->mItems.Push(it); - it = new DOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false); + it = CreateOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false); opt->mItems.Push(it); - it = new DOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false); + it = CreateOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false); opt->mItems.Push(it); #endif - it = new DOptionMenuItemStaticText(" ", false); + it = CreateOptionMenuItemStaticText(" ", false); opt->mItems.Push(it); if (Joysticks.Size() == 0) { - it = new DOptionMenuItemStaticText("No controllers detected", false); + it = CreateOptionMenuItemStaticText("No controllers detected", false); opt->mItems.Push(it); if (!use_joystick) { - it = new DOptionMenuItemStaticText("Controller support must be", false); + it = CreateOptionMenuItemStaticText("Controller support must be", false); opt->mItems.Push(it); - it = new DOptionMenuItemStaticText("enabled to detect any", false); + it = CreateOptionMenuItemStaticText("enabled to detect any", false); opt->mItems.Push(it); } } else { - it = new DOptionMenuItemStaticText("Configure controllers:", false); + it = CreateOptionMenuItemStaticText("Configure controllers:", false); opt->mItems.Push(it); for (int i = 0; i < (int)Joysticks.Size(); ++i) { - it = new DOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]); + it = CreateOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]); opt->mItems.Push(it); if (i == itemnum) opt->mSelectedItem = opt->mItems.Size(); } @@ -429,7 +288,7 @@ void UpdateJoystickMenu(IJoystickConfig *selected) if (i == (int)Joysticks.Size()) { SELECTED_JOYSTICK = NULL; - if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DJoystickConfigMenu))) + if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf("JoystickConfigMenu")) { DMenu::CurrentMenu->Close(); } diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 2ade7d220..18c627359 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -278,309 +278,4 @@ void DListMenu::Drawer () // base class for menu items // //============================================================================= -IMPLEMENT_CLASS(DMenuItemBase, true, false) - -bool DMenuItemBase::CheckCoordinate(int x, int y) -{ - return false; -} - -void DMenuItemBase::Ticker() -{ -} - -void DMenuItemBase::Drawer(bool selected) -{ -} - -bool DMenuItemBase::Selectable() -{ - 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); - } -} - -bool DMenuItemBase::Activate() -{ - return false; // cannot be activated -} - -FName DMenuItemBase::GetAction(int *pparam) -{ - return mAction; -} - -bool DMenuItemBase::SetString(int i, const char *s) -{ - return false; -} - -bool DMenuItemBase::GetString(int i, char *s, int len) -{ - return false; -} - -bool DMenuItemBase::SetValue(int i, int value) -{ - return false; -} - -bool DMenuItemBase::GetValue(int i, int *pvalue) -{ - return false; -} - -void DMenuItemBase::Enable(bool on) -{ - mEnabled = on; -} - -bool DMenuItemBase::MenuEvent(int mkey, bool fromcontroller) -{ - return false; -} - -DEFINE_ACTION_FUNCTION(DMenuItemBase, MenuEvent) -{ - PARAM_SELF_PROLOGUE(DMenuItemBase); - PARAM_INT(key); - PARAM_BOOL(fromcontroller); - ACTION_RETURN_BOOL(self->MenuEvent(key, fromcontroller)); -} - - -bool DMenuItemBase::MouseEvent(int type, int x, int y) -{ - return false; -} - -bool DMenuItemBase::CheckHotkey(int c) -{ - return false; -} - -int DMenuItemBase::GetWidth() -{ - return 0; -} - - -//============================================================================= -// -// static patch -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemStaticPatch, false, false) - -DListMenuItemStaticPatch::DListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered) -: DMenuItemBase(x, y) -{ - mTexture = patch; - mCentered = centered; -} - -void DListMenuItemStaticPatch::Drawer(bool selected) -{ - if (!mTexture.Exists()) - { - return; - } - - int x = mXpos; - FTexture *tex = TexMan(mTexture); - if (mYpos >= 0) - { - if (mCentered) x -= tex->GetScaledWidth()/2; - screen->DrawTexture (tex, x, mYpos, DTA_Clean, true, TAG_DONE); - } - else - { - int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1); - if (mCentered) x -= (tex->GetScaledWidth()*CleanXfac)/2; - screen->DrawTexture (tex, x, -mYpos*CleanYfac, DTA_CleanNoMove, true, TAG_DONE); - } -} - -//============================================================================= -// -// static text -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemStaticText, false, false) - -DListMenuItemStaticText::DListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered) -: DMenuItemBase(x, y) -{ - mText = text; - mFont = font; - mColor = color; - mCentered = centered; -} - -void DListMenuItemStaticText::Drawer(bool selected) -{ - if (mText.IsNotEmpty()) - { - const char *text = mText; - if (*text == '$') text = GStrings(text+1); - if (mYpos >= 0) - { - int x = mXpos; - if (mCentered) x -= mFont->StringWidth(text)/2; - screen->DrawText(mFont, mColor, x, mYpos, text, DTA_Clean, true, TAG_DONE); - } - else - { - int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1); - if (mCentered) x -= (mFont->StringWidth(text)*CleanXfac)/2; - screen->DrawText (mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true, TAG_DONE); - } - } -} - -//============================================================================= -// -// base class for selectable items -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemSelectable, false, false) - -DListMenuItemSelectable::DListMenuItemSelectable(int x, int y, int height, FName action, int param) -: DMenuItemBase(x, y, action) -{ - mHeight = height; - mParam = param; - mHotkey = 0; -} - -bool DListMenuItemSelectable::CheckCoordinate(int x, int y) -{ - return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here -} - -bool DListMenuItemSelectable::Selectable() -{ - return mEnabled; -} - -bool DListMenuItemSelectable::Activate() -{ - M_SetMenu(mAction, mParam); - return true; -} - -FName DListMenuItemSelectable::GetAction(int *pparam) -{ - if (pparam != NULL) *pparam = mParam; - return mAction; -} - -bool DListMenuItemSelectable::CheckHotkey(int c) -{ - return c == tolower(mHotkey); -} - -bool DListMenuItemSelectable::MouseEvent(int type, int x, int y) -{ - if (type == DMenu::MOUSE_Release) - { - if (NULL != DMenu::CurrentMenu && DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) - { - return true; - } - } - return false; -} - -//============================================================================= -// -// text item -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemText, false, false) - -DListMenuItemText::DListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, EColorRange color2, FName child, int param) -: DListMenuItemSelectable(x, y, height, child, param) -{ - mText = ncopystring(text); - mFont = font; - mColor = color; - mColorSelected = color2; - mHotkey = hotkey; -} - -void DListMenuItemText::OnDestroy() -{ - if (mText != NULL) - { - delete [] mText; - } -} - -void DListMenuItemText::Drawer(bool selected) -{ - const char *text = mText; - if (text != NULL) - { - if (*text == '$') text = GStrings(text+1); - screen->DrawText(mFont, selected ? mColorSelected : mColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE); - } -} - -int DListMenuItemText::GetWidth() -{ - const char *text = mText; - if (text != NULL) - { - if (*text == '$') text = GStrings(text+1); - return mFont->StringWidth(text); - } - return 1; -} - - -//============================================================================= -// -// patch item -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemPatch, false, false) - -DListMenuItemPatch::DListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param) -: DListMenuItemSelectable(x, y, height, child, param) -{ - mHotkey = hotkey; - mTexture = patch; -} - -void DListMenuItemPatch::Drawer(bool selected) -{ - screen->DrawTexture (TexMan(mTexture), mXpos, mYpos, DTA_Clean, true, TAG_DONE); -} - -int DListMenuItemPatch::GetWidth() -{ - return mTexture.isValid() - ? TexMan[mTexture]->GetScaledWidth() - : 0; -} - +IMPLEMENT_CLASS(DMenuItemBase, false, false) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 8d9dc15e9..ec92a591f 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -4,7 +4,7 @@ ** **--------------------------------------------------------------------------- ** Copyright 2001-2010 Randy Heit -** Copyright 2010 Christoph Oelckers +** Copyright 2010-2017 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -50,116 +50,47 @@ #include "resourcefiles/resourcefile.h" - - -class DLoadSaveMenu : public DListMenu -{ - DECLARE_CLASS(DLoadSaveMenu, DListMenu) - friend void ClearSaveGames(); - -protected: - static TArray SaveGames; - static int LastSaved; - static int LastAccessed; - - 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. - FileReader *currentSavePic; - TArray SavePicData; - - - static int InsertSaveNode (FSaveGameNode *node); - static void ReadSaveStrings (); - - - FTexture *SavePic; - FBrokenLines *SaveComment; - bool mEntering; - char savegamestring[SAVESTRINGSIZE]; - - DLoadSaveMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL); - void OnDestroy() override; - - int RemoveSaveSlot (int index); - void UnloadSaveData (); - void ClearSaveStuff (); - void ExtractSaveData (int index); - void Drawer (); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - bool Responder(event_t *ev); - -public: - static void NotifyNewSave (const char *file, const char *title, bool okForQuicksave); - -}; - -IMPLEMENT_CLASS(DLoadSaveMenu, false, false) - -TArray DLoadSaveMenu::SaveGames; -int DLoadSaveMenu::LastSaved = -1; -int DLoadSaveMenu::LastAccessed = -1; - -FSaveGameNode *quickSaveSlot; - //============================================================================= // -// Save data maintenance (stored statically) +// This should remain native because exposing 'remove' would allow +// the creation of actual malware mods! // //============================================================================= -void ClearSaveGames() +void SavegameManager::DeleteEntry(int Selected) { - for(unsigned i=0;ibNoDelete) - delete DLoadSaveMenu::SaveGames[i]; - } - DLoadSaveMenu::SaveGames.Clear(); + remove(SaveGames[Selected]->Filename.GetChars()); } //============================================================================= // -// Save data maintenance (stored statically) +// Save data maintenance // //============================================================================= -int DLoadSaveMenu::RemoveSaveSlot (int index) +void SavegameManager::ClearSaveGames() +{ + for (unsigned i = 0; ibNoDelete) + delete SaveGames[i]; + } + SaveGames.Clear(); +} + +//============================================================================= +// +// Save data maintenance +// +//============================================================================= + +int SavegameManager::RemoveSaveSlot(int index) { FSaveGameNode *file = SaveGames[index]; if (quickSaveSlot == SaveGames[index]) { - quickSaveSlot = NULL; - } - if (Selected == index) - { - Selected = -1; + quickSaveSlot = nullptr; } if (!file->bNoDelete) delete file; SaveGames.Delete(index); @@ -173,7 +104,7 @@ int DLoadSaveMenu::RemoveSaveSlot (int index) // //============================================================================= -int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) +int SavegameManager::InsertSaveNode(FSaveGameNode *node) { if (SaveGames.Size() == 0) { @@ -187,10 +118,10 @@ int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) else { // Add node at top of list unsigned int i; - for(i = 0; i < SaveGames.Size(); i++) + for (i = 0; i < SaveGames.Size(); i++) { if (SaveGames[i]->bOldVersion || - stricmp (node->Title, SaveGames[i]->Title) <= 0) + stricmp(node->Title, SaveGames[i]->Title) <= 0) { break; } @@ -200,7 +131,6 @@ int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) } } - //============================================================================= // // M_ReadSaveStrings @@ -209,7 +139,7 @@ int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) // //============================================================================= -void DLoadSaveMenu::ReadSaveStrings () +void SavegameManager::ReadSaveStrings() { if (SaveGames.Size() == 0) { @@ -218,15 +148,15 @@ void DLoadSaveMenu::ReadSaveStrings () FString filter; LastSaved = LastAccessed = -1; - quickSaveSlot = NULL; - filter = G_BuildSaveName ("*." SAVEGAME_EXT, -1); - filefirst = I_FindFirst (filter.GetChars(), &c_file); + quickSaveSlot = nullptr; + filter = G_BuildSaveName("*." SAVEGAME_EXT, -1); + filefirst = I_FindFirst(filter.GetChars(), &c_file); if (filefirst != ((void *)(-1))) { do { // I_FindName only returns the file's name and not its full path - FString filepath = G_BuildSaveName (I_FindName(&c_file), -1); + FString filepath = G_BuildSaveName(I_FindName(&c_file), -1); FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, nullptr, true, true); if (savegame != nullptr) @@ -290,12 +220,12 @@ void DLoadSaveMenu::ReadSaveStrings () } else // check for old formats. { - FILE *file = fopen (filepath, "rb"); - if (file != NULL) + FILE *file = fopen(filepath, "rb"); + if (file != nullptr) { PNGHandle *png; char sig[16]; - char title[SAVESTRINGSIZE+1]; + char title[SAVESTRINGSIZE + 1]; bool oldVer = true; bool addIt = false; bool missing = false; @@ -310,10 +240,10 @@ void DLoadSaveMenu::ReadSaveStrings () title[SAVESTRINGSIZE] = 0; - if (NULL != (png = M_VerifyPNG (file))) + if (nullptr != (png = M_VerifyPNG(file))) { - char *ver = M_GetPNGText (png, "ZDoom Save Version"); - if (ver != NULL) + char *ver = M_GetPNGText(png, "ZDoom Save Version"); + if (ver != nullptr) { // An old version if (!M_GetPNGText(png, "Title", title, SAVESTRINGSIZE)) @@ -327,23 +257,23 @@ void DLoadSaveMenu::ReadSaveStrings () } else { - fseek (file, 0, SEEK_SET); - if (fread (sig, 1, 16, file) == 16) + fseek(file, 0, SEEK_SET); + if (fread(sig, 1, 16, file) == 16) { - if (strncmp (sig, "ZDOOMSAVE", 9) == 0) + if (strncmp(sig, "ZDOOMSAVE", 9) == 0) { - if (fread (title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) + if (fread(title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) { addIt = true; } } else { - memcpy (title, sig, 16); - if (fread (title + 16, 1, SAVESTRINGSIZE-16, file) == SAVESTRINGSIZE-16 && - fread (sig, 1, 16, file) == 16 && - strncmp (sig, "ZDOOMSAVE", 9) == 0) + memcpy(title, sig, 16); + if (fread(title + 16, 1, SAVESTRINGSIZE - 16, file) == SAVESTRINGSIZE - 16 && + fread(sig, 1, 16, file) == 16 && + strncmp(sig, "ZDOOMSAVE", 9) == 0) { addIt = true; } @@ -357,50 +287,49 @@ void DLoadSaveMenu::ReadSaveStrings () node->Filename = filepath; node->bOldVersion = true; node->bMissingWads = false; - memcpy (node->Title, title, SAVESTRINGSIZE); - InsertSaveNode (node); + memcpy(node->Title, title, SAVESTRINGSIZE); + InsertSaveNode(node); } - fclose (file); + fclose(file); } } - } while (I_FindNext (filefirst, &c_file) == 0); - I_FindClose (filefirst); + } while (I_FindNext(filefirst, &c_file) == 0); + I_FindClose(filefirst); } } } - //============================================================================= // // // //============================================================================= -void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okForQuicksave) +void SavegameManager::NotifyNewSave(const char *file, const char *title, bool okForQuicksave) { FSaveGameNode *node; - if (file == NULL) + if (file == nullptr) return; - ReadSaveStrings (); + ReadSaveStrings(); // See if the file is already in our list - for (unsigned i=0; iFilename.Compare (file) == 0) + if (node->Filename.Compare(file) == 0) #else - if (node->Filename.CompareNoCase (file) == 0) + if (node->Filename.CompareNoCase(file) == 0) #endif { - strcpy (node->Title, title); + strcpy(node->Title, title); node->bOldVersion = false; node->bMissingWads = false; if (okForQuicksave) { - if (quickSaveSlot == NULL) quickSaveSlot = node; + if (quickSaveSlot == nullptr) quickSaveSlot = node; LastAccessed = LastSaved = i; } return; @@ -408,107 +337,71 @@ void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okF } node = new FSaveGameNode; - strcpy (node->Title, title); + strcpy(node->Title, title); node->Filename = file; node->bOldVersion = false; node->bMissingWads = false; - int index = InsertSaveNode (node); + int index = InsertSaveNode(node); if (okForQuicksave) { - if (quickSaveSlot == NULL) quickSaveSlot = node; + if (quickSaveSlot == nullptr) quickSaveSlot = node; LastAccessed = LastSaved = index; } } -void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave) +//============================================================================= +// +// Loads the savegame +// +//============================================================================= + +void SavegameManager::LoadSavegame(int Selected) { - DLoadSaveMenu::NotifyNewSave(file, title, okForQuicksave); -} - -//============================================================================= -// -// End of static savegame maintenance code -// -//============================================================================= - -DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, DListMenuDescriptor *desc) -: DListMenu(parent, desc) -{ - ReadSaveStrings(); - - savepicLeft = 10; - savepicTop = 54*CleanYfac; - savepicWidth = 216*screen->GetWidth()/640; - savepicHeight = 135*screen->GetHeight()/400; - - 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; - currentSavePic = nullptr; - - 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() -{ - if (currentSavePic != nullptr) delete currentSavePic; - currentSavePic = nullptr; - ClearSaveStuff (); - Super::OnDestroy(); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DLoadSaveMenu::UnloadSaveData () -{ - if (SavePic != nullptr) - { - delete SavePic; - } - if (SaveComment != nullptr) - { - V_FreeBrokenLines (SaveComment); - } - if (currentSavePic != nullptr) - { - delete currentSavePic; - } - - SavePic = nullptr; - SaveComment = nullptr; - currentSavePic = nullptr; - SavePicData.Clear(); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DLoadSaveMenu::ClearSaveStuff () -{ - UnloadSaveData(); + G_LoadGame(SaveGames[Selected]->Filename.GetChars(), true); if (quickSaveSlot == (FSaveGameNode*)1) { - quickSaveSlot = NULL; + quickSaveSlot = SaveGames[Selected]; } + M_ClearMenus(); + V_SetBorderNeedRefresh(); + LastAccessed = Selected; +} + +//============================================================================= +// +// +// +//============================================================================= + +void SavegameManager::DoSave(int Selected, const char *savegamestring) +{ + if (Selected != 0) + { + auto node = SaveGames[Selected]; + G_SaveGame(node->Filename.GetChars(), savegamestring); + } + else + { + // Find an unused filename and save as that + FString filename; + int i; + FILE *test; + + for (i = 0;; ++i) + { + filename = G_BuildSaveName("save", i); + test = fopen(filename, "rb"); + if (test == nullptr) + { + break; + } + fclose(test); + } + G_SaveGame(filename, savegamestring); + } + M_ClearMenus(); + V_SetBorderNeedRefresh(); } //============================================================================= @@ -517,12 +410,12 @@ void DLoadSaveMenu::ClearSaveStuff () // //============================================================================= -void DLoadSaveMenu::ExtractSaveData (int index) +void SavegameManager::ExtractSaveData(int index) { FResourceFile *resf; FSaveGameNode *node; - UnloadSaveData (); + UnloadSaveData(); if ((unsigned)index < SaveGames.Size() && (node = SaveGames[index]) && @@ -548,8 +441,8 @@ void DLoadSaveMenu::ExtractSaveData (int index) comment = time; if (time.Len() > 0) comment += "\n\n"; comment += pcomment; - - SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, comment.GetChars()); + + SaveComment = V_BreakLines(SmallFont, WindowSize, comment.GetChars()); // Extract pic FResourceLump *pic = resf->FindLump("savepic.png"); @@ -592,6 +485,182 @@ void DLoadSaveMenu::ExtractSaveData (int index) // //============================================================================= +void SavegameManager::UnloadSaveData() +{ + if (SavePic != nullptr) + { + delete SavePic; + } + if (SaveComment != nullptr) + { + V_FreeBrokenLines(SaveComment); + } + if (currentSavePic != nullptr) + { + delete currentSavePic; + } + + SavePic = nullptr; + SaveComment = nullptr; + currentSavePic = nullptr; + SavePicData.Clear(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void SavegameManager::ClearSaveStuff() +{ + UnloadSaveData(); + if (quickSaveSlot == (FSaveGameNode*)1) + { + quickSaveSlot = nullptr; + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +bool SavegameManager::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; +} + +//============================================================================= +// +// +// +//============================================================================= + +void SavegameManager::SetFileInfo(int Selected) +{ + if (!SaveGames[Selected]->Filename.IsEmpty()) + { + char workbuf[512]; + + mysnprintf(workbuf, countof(workbuf), "File on disk:\n%s", SaveGames[Selected]->Filename.GetChars()); + if (SaveComment != nullptr) + { + V_FreeBrokenLines(SaveComment); + } + SaveComment = V_BreakLines(SmallFont, WindowSize, workbuf); + } +} + +SavegameManager savegameManager; + + + + +class DLoadSaveMenu : public DListMenu +{ + 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(); @@ -608,23 +677,15 @@ void DLoadSaveMenu::Drawer () } V_DrawFrame (savepicLeft, savepicTop, savepicWidth, savepicHeight); - if (SavePic != NULL) - { - screen->DrawTexture(SavePic, savepicLeft, savepicTop, - DTA_DestWidth, savepicWidth, - DTA_DestHeight, savepicHeight, - DTA_Masked, false, - TAG_DONE); - } - else + if (!manager->DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight)) { screen->Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); - if (SaveGames.Size() > 0) + if (manager->SaveGames.Size() > 0) { const char *text = - (Selected == -1 || !SaveGames[Selected]->bOldVersion) + (Selected == -1 || !manager->SaveGames[Selected]->bOldVersion) ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); const int textlen = SmallFont->StringWidth (text)*CleanXfac; @@ -637,15 +698,15 @@ void DLoadSaveMenu::Drawer () // Draw comment area V_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); screen->Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); - if (SaveComment != NULL) + if (manager->SaveComment != nullptr) { - // I'm not sure why SaveComment would go NULL in this loop, but I got - // a crash report where it was NULL when i reached 1, so now I check + // 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; SaveComment != NULL && SaveComment[i].Width >= 0 && i < 6; ++i) + 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, SaveComment[i].Text, + + SmallFont->GetHeight()*i*CleanYfac, manager->SaveComment[i].Text, DTA_CleanNoMove, true, TAG_DONE); } } @@ -654,7 +715,7 @@ void DLoadSaveMenu::Drawer () V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); - if (SaveGames.Size() == 0) + if (manager->SaveGames.Size() == 0) { const char * text = GStrings("MNU_NOFILES"); const int textlen = SmallFont->StringWidth (text)*CleanXfac; @@ -665,10 +726,10 @@ void DLoadSaveMenu::Drawer () return; } - for (i = 0, j = TopItem; i < listboxRows && j < SaveGames.Size(); i++,j++) + for (i = 0, j = TopItem; i < listboxRows && j < manager->SaveGames.Size(); i++,j++) { int color; - node = SaveGames[j]; + node = manager->SaveGames[j]; if (node->bOldVersion) { color = CR_BLUE; @@ -700,15 +761,9 @@ void DLoadSaveMenu::Drawer () } else { + FString s = mInput->GetText() + SmallFont->GetCursor(); screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, savegamestring, - DTA_CleanNoMove, true, TAG_DONE); - - char curs[2] = { SmallFont->GetCursor(), 0 }; - screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1+SmallFont->StringWidth (savegamestring)*CleanXfac, - listboxTop+rowHeight*i+CleanYfac, - curs, + listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, s, DTA_CleanNoMove, true, TAG_DONE); } } @@ -732,59 +787,59 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) switch (mkey) { case MKEY_Up: - if (SaveGames.Size() > 1) + if (manager->SaveGames.Size() > 1) { if (Selected == -1) Selected = TopItem; else { - if (--Selected < 0) Selected = SaveGames.Size()-1; + if (--Selected < 0) Selected = manager->SaveGames.Size()-1; if (Selected < TopItem) TopItem = Selected; else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); } - UnloadSaveData (); - ExtractSaveData (Selected); + manager->UnloadSaveData (); + manager->ExtractSaveData (Selected); } return true; case MKEY_Down: - if (SaveGames.Size() > 1) + if (manager->SaveGames.Size() > 1) { if (Selected == -1) Selected = TopItem; else { - if (unsigned(++Selected) >= SaveGames.Size()) Selected = 0; + if (unsigned(++Selected) >= manager->SaveGames.Size()) Selected = 0; if (Selected < TopItem) TopItem = Selected; else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); } - UnloadSaveData (); - ExtractSaveData (Selected); + manager->UnloadSaveData (); + manager->ExtractSaveData (Selected); } return true; case MKEY_PageDown: - if (SaveGames.Size() > 1) + if (manager->SaveGames.Size() > 1) { - if (TopItem >= (int)SaveGames.Size() - listboxRows) + if (TopItem >= (int)manager->SaveGames.Size() - listboxRows) { TopItem = 0; if (Selected != -1) Selected = 0; } else { - TopItem = MIN(TopItem + listboxRows, SaveGames.Size() - listboxRows); + TopItem = MIN(TopItem + listboxRows, manager->SaveGames.Size() - listboxRows); if (TopItem > Selected && Selected != -1) Selected = TopItem; } - UnloadSaveData (); - ExtractSaveData (Selected); + manager->UnloadSaveData (); + manager->ExtractSaveData (Selected); } return true; case MKEY_PageUp: - if (SaveGames.Size() > 1) + if (manager->SaveGames.Size() > 1) { if (TopItem == 0) { - TopItem = SaveGames.Size() - listboxRows; + TopItem = manager->SaveGames.Size() - listboxRows; if (Selected != -1) Selected = TopItem; } else @@ -792,8 +847,8 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) TopItem = MAX(TopItem - listboxRows, 0); if (Selected >= TopItem + listboxRows) Selected = TopItem; } - UnloadSaveData (); - ExtractSaveData (Selected); + manager->UnloadSaveData (); + manager->ExtractSaveData (Selected); } return true; @@ -802,18 +857,18 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) case MKEY_MBYes: { - if ((unsigned)Selected < SaveGames.Size()) + if ((unsigned)Selected < manager->SaveGames.Size()) { - int listindex = SaveGames[0]->bNoDelete? Selected-1 : Selected; - remove (SaveGames[Selected]->Filename.GetChars()); - UnloadSaveData (); - Selected = RemoveSaveSlot (Selected); - ExtractSaveData (Selected); + int listindex = manager->SaveGames[0]->bNoDelete? Selected-1 : Selected; + manager->DeleteEntry(Selected); + manager->UnloadSaveData (); + Selected = manager->RemoveSaveSlot (Selected); + manager->ExtractSaveData (Selected); - if (LastSaved == listindex) LastSaved = -1; - else if (LastSaved > listindex) LastSaved--; - if (LastAccessed == listindex) LastAccessed = -1; - else if (LastAccessed > listindex) LastAccessed--; + 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; } @@ -836,11 +891,11 @@ bool DLoadSaveMenu::MouseEvent(int type, int x, int y) { int lineno = (y - listboxTop) / rowHeight; - if (TopItem + lineno < (int)SaveGames.Size()) + if (TopItem + lineno < (int)manager->SaveGames.Size()) { Selected = TopItem + lineno; - UnloadSaveData (); - ExtractSaveData (Selected); + manager->UnloadSaveData (); + manager->ExtractSaveData (Selected); if (type == MOUSE_Release) { if (MenuEvent(MKEY_Enter, true)) @@ -868,22 +923,12 @@ bool DLoadSaveMenu::Responder (event_t *ev) { if (ev->subtype == EV_GUI_KeyDown) { - if ((unsigned)Selected < SaveGames.Size()) + if ((unsigned)Selected < manager->SaveGames.Size()) { switch (ev->data1) { case GK_F1: - if (!SaveGames[Selected]->Filename.IsEmpty()) - { - char workbuf[512]; - - mysnprintf (workbuf, countof(workbuf), "File on disk:\n%s", SaveGames[Selected]->Filename.GetChars()); - if (SaveComment != NULL) - { - V_FreeBrokenLines (SaveComment); - } - SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, workbuf); - } + manager->SetFileInfo(Selected); return true; case GK_DEL: @@ -891,7 +936,7 @@ bool DLoadSaveMenu::Responder (event_t *ev) { FString EndString; EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), SaveGames[Selected]->Title, GStrings("PRESSYN")); + GStrings("MNU_DELETESG"), manager->SaveGames[Selected]->Title, GStrings("PRESSYN")); M_StartMessage (EndString, 0); } return true; @@ -905,7 +950,7 @@ bool DLoadSaveMenu::Responder (event_t *ev) } else if (ev->subtype == EV_GUI_WheelDown) { - if (TopItem < (int)SaveGames.Size() - listboxRows) TopItem++; + if (TopItem < (int)manager->SaveGames.Size() - listboxRows) TopItem++; return true; } } @@ -927,9 +972,8 @@ class DSaveMenu : public DLoadSaveMenu public: - DSaveMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL); + DSaveMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); void OnDestroy() override; - void DoSave (FSaveGameNode *node); bool Responder (event_t *ev); bool MenuEvent (int mkey, bool fromcontroller); @@ -949,17 +993,17 @@ DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) { strcpy (NewSaveNode.Title, GStrings["NEWSAVE"]); NewSaveNode.bNoDelete = true; - SaveGames.Insert(0, &NewSaveNode); + manager->SaveGames.Insert(0, &NewSaveNode); TopItem = 0; - if (LastSaved == -1) + if (manager->LastSaved == -1) { Selected = 0; } else { - Selected = LastSaved + 1; + Selected = manager->LastSaved + 1; } - ExtractSaveData (Selected); + manager->ExtractSaveData (Selected); } //============================================================================= @@ -970,50 +1014,15 @@ DSaveMenu::DSaveMenu(DMenu *parent, DListMenuDescriptor *desc) void DSaveMenu::OnDestroy() { - if (SaveGames[0] == &NewSaveNode) + if (manager->SaveGames[0] == &NewSaveNode) { - SaveGames.Delete(0); + manager->SaveGames.Delete(0); if (Selected == 0) Selected = -1; else Selected--; } Super::OnDestroy(); } -//============================================================================= -// -// -// -//============================================================================= - -void DSaveMenu::DoSave (FSaveGameNode *node) -{ - if (node != &NewSaveNode) - { - G_SaveGame (node->Filename.GetChars(), savegamestring); - } - else - { - // Find an unused filename and save as that - FString filename; - int i; - FILE *test; - - for (i = 0;; ++i) - { - filename = G_BuildSaveName ("save", i); - test = fopen (filename, "rb"); - if (test == NULL) - { - break; - } - fclose (test); - } - G_SaveGame (filename, savegamestring); - } - M_ClearMenus(); - V_SetBorderNeedRefresh(); -} - //============================================================================= // // @@ -1035,24 +1044,26 @@ bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) { if (Selected != 0) { - strcpy (savegamestring, SaveGames[Selected]->Title); + strcpy (savegamestring, manager->SaveGames[Selected]->Title); } else { savegamestring[0] = 0; } - DMenu *input = new DTextEnterMenu(this, savegamestring, SAVESTRINGSIZE, 1, fromcontroller); - M_ActivateMenu(input); + mInput = new DTextEnterMenu(this, savegamestring, SAVESTRINGSIZE, 1, fromcontroller); + M_ActivateMenu(mInput); mEntering = true; } else if (mkey == MKEY_Input) { mEntering = false; - DoSave(SaveGames[Selected]); + manager->DoSave(Selected, mInput->GetText()); + mInput = nullptr; } else if (mkey == MKEY_Abort) { mEntering = false; + mInput = nullptr; } return false; } @@ -1079,7 +1090,7 @@ bool DSaveMenu::Responder (event_t *ev) case 'N': Selected = TopItem = 0; - UnloadSaveData (); + manager->UnloadSaveData (); return true; } } @@ -1099,7 +1110,7 @@ class DLoadMenu : public DLoadSaveMenu public: - DLoadMenu(DMenu *parent = NULL, DListMenuDescriptor *desc = NULL); + DLoadMenu(DMenu *parent = nullptr, DListMenuDescriptor *desc = nullptr); bool MenuEvent (int mkey, bool fromcontroller); }; @@ -1117,11 +1128,11 @@ DLoadMenu::DLoadMenu(DMenu *parent, DListMenuDescriptor *desc) : DLoadSaveMenu(parent, desc) { TopItem = 0; - if (LastAccessed != -1) + if (manager->LastAccessed != -1) { - Selected = LastAccessed; + Selected = manager->LastAccessed; } - ExtractSaveData (Selected); + manager->ExtractSaveData (Selected); } @@ -1137,21 +1148,14 @@ bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) { return true; } - if (Selected == -1 || SaveGames.Size() == 0) + if (Selected == -1 || manager->SaveGames.Size() == 0) { return false; } if (mkey == MKEY_Enter) { - G_LoadGame (SaveGames[Selected]->Filename.GetChars(), true); - if (quickSaveSlot == (FSaveGameNode*)1) - { - quickSaveSlot = SaveGames[Selected]; - } - M_ClearMenus(); - V_SetBorderNeedRefresh(); - LastAccessed = Selected; + manager->LoadSavegame(Selected); return true; } return false; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 1f3ac189e..46a0978af 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -70,8 +70,19 @@ 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); +} + int DMenu::MenuTime; +DEFINE_ACTION_FUNCTION(DMenu, MenuTime) +{ + ACTION_RETURN_INT(DMenu::MenuTime); +} + FGameStartup GameStartupInfo; EMenuState menuactive; bool M_DemoNoPlay; @@ -82,6 +93,8 @@ int BackbuttonTime; float BackbuttonAlpha; static bool MenuEnabled = true; +void M_InitVideoModes(); + #define KEY_REPEAT_DELAY (TICRATE*5/12) #define KEY_REPEAT_RATE (3) @@ -96,6 +109,14 @@ IMPLEMENT_CLASS(DMenuDescriptor, false, false) IMPLEMENT_CLASS(DListMenuDescriptor, false, false) IMPLEMENT_CLASS(DOptionMenuDescriptor, false, false) +DEFINE_ACTION_FUNCTION(DMenuDescriptor, GetDescriptor) +{ + PARAM_PROLOGUE; + PARAM_NAME(name); + DMenuDescriptor **desc = MenuDescriptors.CheckKey(name); + auto retn = desc ? *desc : nullptr; + ACTION_RETURN_OBJECT(retn); +} size_t DListMenuDescriptor::PropagateMark() { @@ -180,6 +201,26 @@ bool DMenu::Responder (event_t *ev) return false; } +DEFINE_ACTION_FUNCTION(DMenu, Responder) +{ + PARAM_SELF_PROLOGUE(DMenu); + PARAM_POINTER(ev, event_t); + ACTION_RETURN_BOOL(self->Responder(ev)); +} + +bool DMenu::CallResponder(event_t *ev) +{ + IFVIRTUAL(DMenu, Responder) + { + VMValue params[] = { (DObject*)this, ev}; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + } + else return Responder(ev); +} + //============================================================================= // // @@ -337,6 +378,24 @@ void DMenu::Ticker () { } +DEFINE_ACTION_FUNCTION(DMenu, Ticker) +{ + PARAM_SELF_PROLOGUE(DMenu); + self->Drawer(); + return 0; +} + +void DMenu::CallTicker() +{ + IFVIRTUAL(DMenu, Ticker) + { + VMValue params[] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + else Drawer(); +} + + void DMenu::Drawer () { if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) @@ -375,6 +434,20 @@ void DMenu::CallDrawer() else Drawer(); } +DEFINE_ACTION_FUNCTION(DMenu, Close) +{ + PARAM_SELF_PROLOGUE(DMenu); + self->Close(); + return 0; +} + +DEFINE_ACTION_FUNCTION(DMenu, GetItem) +{ + PARAM_SELF_PROLOGUE(DMenu); + PARAM_NAME(name); + ACTION_RETURN_OBJECT(self->GetItem(name)); +} + bool DMenu::DimAllowed() @@ -384,6 +457,14 @@ bool DMenu::DimAllowed() bool DMenu::TranslateKeyboardEvents() { + IFVIRTUAL(DMenu, TranslateKeyboardEvents) + { + VMValue params[] = { (DObject*)this }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + return !!retval; + } return true; } @@ -433,6 +514,13 @@ void M_ActivateMenu(DMenu *menu) GC::WriteBarrier(DMenu::CurrentMenu); } +DEFINE_ACTION_FUNCTION(DMenu, ActivateMenu) +{ + PARAM_SELF_PROLOGUE(DMenu); + M_ActivateMenu(self); + return 0; +} + //============================================================================= // // @@ -500,6 +588,11 @@ void M_SetMenu(FName menu, int param) M_StartMessage (GStrings("SAVEDEAD"), 1); return; } + + case NAME_VideoModeMenu: + M_InitVideoModes(); + break; + } // End of special checks @@ -533,10 +626,14 @@ void M_SetMenu(FName menu, int param) else if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) { DOptionMenuDescriptor *ld = static_cast(*desc); - const PClass *cls = ld->mClass == nullptr? RUNTIME_CLASS(DOptionMenu) : ld->mClass; + const PClass *cls = ld->mClass == nullptr? PClass::FindClass("OptionMenu") : ld->mClass; - DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew(); - newmenu->Init(DMenu::CurrentMenu, ld); + DMenu *newmenu = (DMenu*)cls->CreateNew(); + IFVIRTUALPTRNAME(newmenu, "OptionMenu", Init) + { + VMValue params[3] = { newmenu, DMenu::CurrentMenu, ld }; + GlobalVMStack.Call(func, params, 3, nullptr, 0); + } M_ActivateMenu(newmenu); } return; @@ -559,6 +656,14 @@ void M_SetMenu(FName menu, int param) M_ClearMenus(); } +DEFINE_ACTION_FUNCTION(DMenu, SetMenu) +{ + PARAM_PROLOGUE; + PARAM_NAME(menu); + PARAM_INT(mparam); + M_SetMenu(menu, mparam); + return 0; +} //============================================================================= // // @@ -612,7 +717,7 @@ bool M_Responder (event_t *ev) } // pass everything else on to the current menu - return DMenu::CurrentMenu->Responder(ev); + return DMenu::CurrentMenu->CallResponder(ev); } else if (DMenu::CurrentMenu->TranslateKeyboardEvents()) { @@ -633,7 +738,7 @@ bool M_Responder (event_t *ev) default: if (!keyup) { - return DMenu::CurrentMenu->Responder(ev); + return DMenu::CurrentMenu->CallResponder(ev); } break; } @@ -720,7 +825,7 @@ bool M_Responder (event_t *ev) return true; } } - return DMenu::CurrentMenu->Responder(ev) || !keyup; + return DMenu::CurrentMenu->CallResponder(ev) || !keyup; } else if (MenuEnabled) { @@ -764,7 +869,7 @@ void M_Ticker (void) DMenu::MenuTime++; if (DMenu::CurrentMenu != nullptr && menuactive != MENU_Off) { - DMenu::CurrentMenu->Ticker(); + DMenu::CurrentMenu->CallTicker(); for (int i = 0; i < NUM_MKEYS; ++i) { @@ -773,7 +878,7 @@ void M_Ticker (void) if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) { MenuButtonTickers[i] = KEY_REPEAT_RATE; - DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]); + DMenu::CurrentMenu->CallMenuEvent(i, MenuButtonOrigin[i]); } } } @@ -1071,10 +1176,23 @@ CCMD(reset2saved) } +// This really should be in the script but we can't do scripted CCMDs yet. +CCMD(undocolorpic) +{ + if (DMenu::CurrentMenu != NULL) + { + IFVIRTUALPTR(DMenu::CurrentMenu, DMenu, ResetColor) + { + VMValue params[] = { (DObject*)DMenu::CurrentMenu }; + GlobalVMStack.Call(func, params, countof(params), nullptr, 0, nullptr); + } + } +} -//native void OptionMenuDescriptor.CalcIndent(); -//native OptionMenuItem OptionMenuDescriptor.GetItem(Name iname); -//native void OptionMenuItem.drawLabel(int indent, int y, EColorRange color, bool grayed = false); + + + +DEFINE_FIELD(DMenu, mParentMenu) DEFINE_FIELD(DMenuDescriptor, mMenuName) DEFINE_FIELD(DMenuDescriptor, mNetgameMessage) @@ -1085,6 +1203,23 @@ DEFINE_FIELD(DMenuItemBase, mYpos) DEFINE_FIELD(DMenuItemBase, mAction) DEFINE_FIELD(DMenuItemBase, mEnabled) +DEFINE_FIELD(DListMenuDescriptor, mItems) +DEFINE_FIELD(DListMenuDescriptor, mSelectedItem) +DEFINE_FIELD(DListMenuDescriptor, mSelectOfsX) +DEFINE_FIELD(DListMenuDescriptor, mSelectOfsY) +DEFINE_FIELD(DListMenuDescriptor, mSelector) +DEFINE_FIELD(DListMenuDescriptor, mDisplayTop) +DEFINE_FIELD(DListMenuDescriptor, mXpos) +DEFINE_FIELD(DListMenuDescriptor, mYpos) +DEFINE_FIELD(DListMenuDescriptor, mWLeft) +DEFINE_FIELD(DListMenuDescriptor, mWRight) +DEFINE_FIELD(DListMenuDescriptor, mLinespacing) +DEFINE_FIELD(DListMenuDescriptor, mAutoselect) +DEFINE_FIELD(DListMenuDescriptor, mFont) +DEFINE_FIELD(DListMenuDescriptor, mFontColor) +DEFINE_FIELD(DListMenuDescriptor, mFontColor2) +DEFINE_FIELD(DListMenuDescriptor, mCenter) + DEFINE_FIELD(DOptionMenuDescriptor, mItems) DEFINE_FIELD(DOptionMenuDescriptor, mTitle) DEFINE_FIELD(DOptionMenuDescriptor, mSelectedItem) @@ -1095,15 +1230,6 @@ DEFINE_FIELD(DOptionMenuDescriptor, mIndent) DEFINE_FIELD(DOptionMenuDescriptor, mPosition) DEFINE_FIELD(DOptionMenuDescriptor, mDontDim) -DEFINE_FIELD(DOptionMenuItem, mLabel) -DEFINE_FIELD(DOptionMenuItem, mCentered) - -DEFINE_FIELD(DOptionMenu, CanScrollUp) -DEFINE_FIELD(DOptionMenu, CanScrollDown) -DEFINE_FIELD(DOptionMenu, VisBottom) -DEFINE_FIELD(DOptionMenu, mFocusControl) -DEFINE_FIELD(DOptionMenu, mDesc) - DEFINE_FIELD(FOptionMenuSettings, mTitleColor) DEFINE_FIELD(FOptionMenuSettings, mFontColor) DEFINE_FIELD(FOptionMenuSettings, mFontColorValue) @@ -1112,3 +1238,380 @@ DEFINE_FIELD(FOptionMenuSettings, mFontColorHeader) DEFINE_FIELD(FOptionMenuSettings, mFontColorHighlight) DEFINE_FIELD(FOptionMenuSettings, mFontColorSelection) DEFINE_FIELD(FOptionMenuSettings, mLinespacing) + + +struct IJoystickConfig; +DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, bool v) +{ + auto c = PClass::FindClass("OptionMenuItemStaticText"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(name), v }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuSliderVar(const char *name, int index, double min, double max, double step, int showval) +{ + auto c = PClass::FindClass("OptionMenuItemSliderVar"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(name), index, min, max, step, showval }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemCommand(const char * label, FName cmd) +{ + auto c = PClass::FindClass("OptionMenuItemCommand"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), cmd.GetIndex() }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemOption(const char * label, FName cmd, FName values, FBaseCVar *graycheck, bool center) +{ + auto c = PClass::FindClass("OptionMenuItemOption"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), cmd.GetIndex(), values.GetIndex(), graycheck, center }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy) +{ + auto c = PClass::FindClass("OptionMenuItemJoyConfigMenu"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), joy }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemJoyMap(const char *label, int axis, FName values, bool center) +{ + auto c = PClass::FindClass("OptionMenuItemJoyMap"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), axis, values.GetIndex(), center }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuSliderJoySensitivity(const char * label, double min, double max, double step, int showval) +{ + auto c = PClass::FindClass("OptionMenuSliderJoySensitivity"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), min, max, step, showval }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval) +{ + auto c = PClass::FindClass("OptionMenuSliderJoyScale"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), min, max, step, showval }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemInverter(const char *label, int axis, int center) +{ + auto c = PClass::FindClass("OptionMenuItemInverter"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), axis, center }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval) +{ + auto c = PClass::FindClass("OptionMenuSliderJoyDeadZone"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), min, max, step, showval }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center) +{ + auto c = PClass::FindClass("OptionMenuItemSubmenu"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), cmd.GetIndex(), center }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings) +{ + auto c = PClass::FindClass("OptionMenuItemControl"); + auto p = c->CreateNew(); + VMValue params[] = { p, FString(label), cmd.GetIndex(), bindings }; + auto f = dyn_cast(c->Symbols.FindSymbol("Init", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param) +{ + auto c = PClass::FindClass("ListMenuItemPatchItem"); + auto p = c->CreateNew(); + VMValue params[] = { p, x, y, height, tex.GetIndex(), FString(char(hotkey)), command.GetIndex(), param }; + auto f = dyn_cast(c->Symbols.FindSymbol("InitDirect", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + return (DMenuItemBase*)p; +} + +DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param) +{ + auto c = PClass::FindClass("ListMenuItemTextItem"); + auto p = c->CreateNew(); + VMValue params[] = { p, x, y, height, FString(char(hotkey)), text, font, int(color1.d), int(color2.d), command.GetIndex(), param }; + auto f = dyn_cast(c->Symbols.FindSymbol("InitDirect", false)); + GlobalVMStack.Call(f->Variants[0].Implementation, params, countof(params), nullptr, 0); + 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) + { + VMValue params[] = { (DObject*)this }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + return !!retval; + } + 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) +{ + IFVIRTUAL(DMenuItemBase, SetString) + { + VMValue params[] = { (DObject*)this, i, FString(s) }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + return !!retval; + } + return false; +} + +bool DMenuItemBase::GetString(int i, char *s, int len) +{ + IFVIRTUAL(DMenuItemBase, GetString) + { + VMValue params[] = { (DObject*)this, i }; + int retval; + FString retstr; + VMReturn ret[2]; ret[0].IntAt(&retval); ret[1].StringAt(&retstr); + GlobalVMStack.Call(func, params, countof(params), ret, 2, nullptr); + strncpy(s, retstr, len); + return !!retval; + } + return false; +} + + +bool DMenuItemBase::SetValue(int i, int value) +{ + IFVIRTUAL(DMenuItemBase, SetValue) + { + VMValue params[] = { (DObject*)this, i, value }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, countof(params), &ret, 1, nullptr); + return !!retval; + } + return false; +} + +bool DMenuItemBase::GetValue(int i, int *pvalue) +{ + IFVIRTUAL(DMenuItemBase, GetValue) + { + VMValue params[] = { (DObject*)this, i }; + 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); + *pvalue = retval[1]; + return !!retval[0]; + } + 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, 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); + } +} + diff --git a/src/menu/menu.h b/src/menu/menu.h index e8d5b9a82..d6edb861b 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -22,6 +22,7 @@ class FFont; enum EColorRange : int; class FPlayerClass; class FKeyBindings; +struct FBrokenLines; enum EMenuKey { @@ -66,7 +67,37 @@ struct FSaveGameNode FSaveGameNode() { bNoDelete = false; } }; +struct SavegameManager +{ + TArray SaveGames; + 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(); + int InsertSaveNode(FSaveGameNode *node); + int RemoveSaveSlot(int index); + void ReadSaveStrings(); + void NotifyNewSave(const char *file, const char *title, bool okForQuicksave); + void LoadSavegame(int Selected); + void DoSave(int Selected, const char *savegamestring); + void DeleteEntry(int Selected); + void ExtractSaveData(int index); + void UnloadSaveData(); + void ClearSaveStuff(); + bool DrawSavePic(int x, int y, int w, int h); + void SetFileInfo(int Selected); + +}; + +extern SavegameManager savegameManager; //============================================================================= // @@ -87,7 +118,6 @@ public: }; class DMenuItemBase; -class DOptionMenuItem; class DListMenuDescriptor : public DMenuDescriptor { @@ -145,7 +175,7 @@ class DOptionMenuDescriptor : public DMenuDescriptor DECLARE_CLASS(DOptionMenuDescriptor, DMenuDescriptor) public: - TArray mItems; + TArray mItems; FString mTitle; int mSelectedItem; int mDrawTop; @@ -156,7 +186,7 @@ public: bool mDontDim; void CalcIndent(); - DOptionMenuItem *GetItem(FName name); + DMenuItemBase *GetItem(FName name); void Reset() { // Reset the default settings (ignore all other values in the struct) @@ -239,12 +269,20 @@ public: virtual void Ticker (); virtual void Drawer (); virtual bool DimAllowed (); - virtual bool TranslateKeyboardEvents(); + bool TranslateKeyboardEvents(); virtual void Close(); virtual bool MouseEvent(int type, int x, int y); + virtual void SetFocus(DMenuItemBase *fc) {} + 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); + void CallTicker(); void CallDrawer(); bool MouseEventBack(int type, int x, int y); @@ -267,256 +305,35 @@ class DMenuItemBase : public DObject DECLARE_CLASS(DMenuItemBase, DObject) public: int mXpos, mYpos; - FName mAction; - + FNameNoInit mAction; bool mEnabled; - DMenuItemBase(int xpos = 0, int ypos = 0, FName action = NAME_None) - { - mXpos = xpos; - mYpos = ypos; - mAction = action; - mEnabled = true; - } - - virtual bool CheckCoordinate(int x, int y); - virtual void Ticker(); - virtual void Drawer(bool selected); - virtual bool Selectable(); - virtual bool Activate(); - virtual FName GetAction(int *pparam); - virtual bool SetString(int i, const char *s); - virtual bool GetString(int i, char *s, int len); - virtual bool SetValue(int i, int value); - virtual bool GetValue(int i, int *pvalue); - virtual void Enable(bool on); - virtual bool MenuEvent (int mkey, bool fromcontroller); - virtual bool MouseEvent(int type, int x, int y); - virtual bool CheckHotkey(int c); - virtual int GetWidth(); - void DrawSelector(int xofs, int yofs, FTextureID tex); + 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); + }; -class DListMenuItemStaticPatch : public DMenuItemBase -{ - DECLARE_CLASS(DListMenuItemStaticPatch, DMenuItemBase) -protected: - FTextureID mTexture; - bool mCentered; - - DListMenuItemStaticPatch() {} -public: - DListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered); - void Drawer(bool selected); -}; - -class DListMenuItemStaticText : public DMenuItemBase -{ - DECLARE_CLASS(DListMenuItemStaticText, DMenuItemBase) -protected: - FString mText; - FFont *mFont; - EColorRange mColor; - bool mCentered; - - DListMenuItemStaticText() {} -public: - DListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered); - void Drawer(bool selected); -}; - -//============================================================================= -// -// the player sprite window -// -//============================================================================= - -class DListMenuItemPlayerDisplay : public DMenuItemBase -{ - DECLARE_CLASS(DListMenuItemPlayerDisplay, DMenuItemBase) - - DListMenuDescriptor *mOwner; - FTexture *mBackdrop; - FRemapTable mRemap; - FPlayerClass *mPlayerClass; - FState *mPlayerState; - int mPlayerTics; - bool mNoportrait; - BYTE mRotation; - BYTE mMode; // 0: automatic (used by class selection), 1: manual (used by player setup) - BYTE mTranslate; - int mSkin; - int mRandomClass; - int mRandomTimer; - int mClassNum; - - void SetPlayerClass(int classnum, bool force = false); - bool UpdatePlayerClass(); - void UpdateRandomClass(); - void UpdateTranslation(); - - DListMenuItemPlayerDisplay() {} -public: - - enum - { - PDF_ROTATION = 0x10001, - PDF_SKIN = 0x10002, - PDF_CLASS = 0x10003, - PDF_MODE = 0x10004, - PDF_TRANSLATE = 0x10005, - }; - - DListMenuItemPlayerDisplay(DListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action); - void OnDestroy() override; - virtual void Ticker(); - virtual void Drawer(bool selected); - bool SetValue(int i, int value); -}; - - -//============================================================================= -// -// selectable items -// -//============================================================================= - -class DListMenuItemSelectable : public DMenuItemBase -{ - DECLARE_CLASS(DListMenuItemSelectable, DMenuItemBase) -protected: - int mHotkey; - int mHeight; - int mParam; - - DListMenuItemSelectable() {} -public: - DListMenuItemSelectable(int x, int y, int height, FName childmenu, int mParam = -1); - bool CheckCoordinate(int x, int y); - bool Selectable(); - bool CheckHotkey(int c); - bool Activate(); - bool MouseEvent(int type, int x, int y); - FName GetAction(int *pparam); -}; - -class DListMenuItemText : public DListMenuItemSelectable -{ - DECLARE_CLASS(DListMenuItemText, DListMenuItemSelectable) - const char *mText; - FFont *mFont; - EColorRange mColor; - EColorRange mColorSelected; - - DListMenuItemText() {} -public: - DListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, EColorRange color2, FName child, int param = 0); - void OnDestroy() override; - void Drawer(bool selected); - int GetWidth(); -}; - -class DListMenuItemPatch : public DListMenuItemSelectable -{ - DECLARE_CLASS(DListMenuItemPatch, DListMenuItemSelectable) - FTextureID mTexture; - - DListMenuItemPatch() {} -public: - DListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param = 0); - void Drawer(bool selected); - int GetWidth(); -}; - -//============================================================================= -// -// items for the player menu -// -//============================================================================= - -class DPlayerNameBox : public DListMenuItemSelectable -{ - DECLARE_CLASS(DPlayerNameBox, DListMenuItemSelectable) - FString mText; - FFont *mFont; - EColorRange mFontColor; - int mFrameSize; - char mPlayerName[MAXPLAYERNAME+1]; - char mEditName[MAXPLAYERNAME+2]; - bool mEntering; - - void DrawBorder (int x, int y, int len); - - DPlayerNameBox() {} -public: - - DPlayerNameBox(int x, int y, int height, int frameofs, const char *text, FFont *font, EColorRange color, FName action); - bool SetString(int i, const char *s); - bool GetString(int i, char *s, int len); - void Drawer(bool selected); - bool MenuEvent (int mkey, bool fromcontroller); -}; - -//============================================================================= -// -// items for the player menu -// -//============================================================================= - -class DValueTextItem : public DListMenuItemSelectable -{ - DECLARE_CLASS(DValueTextItem, DListMenuItemSelectable) - TArray mSelections; - FString mText; - int mSelection; - FFont *mFont; - EColorRange mFontColor; - EColorRange mFontColor2; - - DValueTextItem() {} -public: - - DValueTextItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, EColorRange valuecolor, FName action, FName values); - bool SetString(int i, const char *s); - bool SetValue(int i, int value); - bool GetValue(int i, int *pvalue); - bool MenuEvent (int mkey, bool fromcontroller); - void Drawer(bool selected); -}; - -//============================================================================= -// -// items for the player menu -// -//============================================================================= - -class DSliderItem : public DListMenuItemSelectable -{ - DECLARE_CLASS(DSliderItem, DListMenuItemSelectable) - FString mText; - FFont *mFont; - EColorRange mFontColor; - int mMinrange, mMaxrange; - int mStep; - int mSelection; - - void DrawSlider (int x, int y); - - DSliderItem() {} -public: - - DSliderItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, FName action, int min, int max, int step); - bool SetValue(int i, int value); - bool GetValue(int i, int *pvalue); - bool MenuEvent (int mkey, bool fromcontroller); - void Drawer(bool selected); - bool MouseEvent(int type, int x, int y); -}; - //============================================================================= // // list menu class runs a menu described by a DListMenuDescriptor @@ -556,34 +373,6 @@ public: }; -//============================================================================= -// -// base class for menu items -// -//============================================================================= - -class DOptionMenuItem : public DMenuItemBase -{ - DECLARE_ABSTRACT_CLASS(DOptionMenuItem, DMenuItemBase) -public: - FString mLabel; - bool mCentered; - - void drawLabel(int indent, int y, EColorRange color, bool grayed = false); - - DOptionMenuItem(const char *text = nullptr, FName action = NAME_None, bool center = false) - : DMenuItemBase(0, 0, action) - { - mLabel = text; - mCentered = center; - } - - virtual int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected); - virtual bool Selectable(); - virtual int GetIndent(); - virtual bool MouseEvent(int type, int x, int y); -}; - //============================================================================= // // @@ -606,50 +395,6 @@ typedef TMap< FName, FOptionValues* > FOptionMap; extern FOptionMap OptionValues; -//============================================================================= -// -// Option menu class runs a menu described by a DOptionMenuDescriptor -// -//============================================================================= - -class DOptionMenu : public DMenu -{ - DECLARE_CLASS(DOptionMenu, DMenu) - HAS_OBJECT_POINTERS; - -public: // needs to be public for script access - bool CanScrollUp; - bool CanScrollDown; - int VisBottom; - DOptionMenuItem *mFocusControl; - DOptionMenuDescriptor *mDesc; - -//public: - DOptionMenuItem *GetItem(FName name); - DOptionMenu(DMenu *parent = NULL, DOptionMenuDescriptor *desc = NULL); - virtual void Init(DMenu *parent = NULL, DOptionMenuDescriptor *desc = NULL); - int FirstSelectable(); - bool Responder (event_t *ev); - bool MenuEvent (int mkey, bool fromcontroller); - bool MouseEvent(int type, int x, int y); - void Ticker (); - void Drawer (); - const DOptionMenuDescriptor *GetDescriptor() const { return mDesc; } - void SetFocus(DOptionMenuItem *fc) - { - mFocusControl = fc; - } - bool CheckFocus(DOptionMenuItem *fc) - { - return mFocusControl == fc; - } - void ReleaseFocus() - { - mFocusControl = NULL; - } -}; - - //============================================================================= // // Input some text @@ -660,7 +405,8 @@ class DTextEnterMenu : public DMenu { DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu) - char *mEnterString; +public: + FString mEnterString; unsigned int mEnterSize; unsigned int mEnterPos; int mSizeMode; // 1: size is length in chars. 2: also check string width @@ -672,17 +418,15 @@ class DTextEnterMenu : public DMenu // [TP] bool AllowColors; -public: // [TP] Added allowcolors - DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors = false); + 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 TranslateKeyboardEvents(); bool MouseEvent(int type, int x, int y); - + FString GetText(); }; @@ -702,7 +446,6 @@ void M_StartupSkillMenu(FGameStartup *gs); int M_GetDefaultSkill(); void M_StartControlPanel (bool makeSound); void M_SetMenu(FName menu, int param = -1); -void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave); void M_StartMessage(const char *message, int messagemode, FName action = NAME_None); DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar); void M_RefreshModesList (); @@ -710,4 +453,20 @@ void M_InitVideoModesMenu (); void M_MarkMenus(); +struct IJoystickConfig; +DMenuItemBase * CreateOptionMenuItemStaticText(const char *name, bool v); +DMenuItemBase * CreateOptionMenuSliderVar(const char *name, int index, double min, double max, double step, int showval); +DMenuItemBase * CreateOptionMenuItemCommand(const char * label, FName cmd); +DMenuItemBase * CreateOptionMenuItemOption(const char * label, FName cmd, FName values, FBaseCVar *graycheck, bool center); +DMenuItemBase * CreateOptionMenuItemSubmenu(const char *label, FName cmd, int center); +DMenuItemBase * CreateOptionMenuItemControl(const char *label, FName cmd, FKeyBindings *bindings); +DMenuItemBase * CreateOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy); +DMenuItemBase * CreateOptionMenuItemJoyMap(const char *label, int axis, FName values, bool center); +DMenuItemBase * CreateOptionMenuSliderJoySensitivity(const char * label, double min, double max, double step, int showval); +DMenuItemBase * CreateOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval); +DMenuItemBase * CreateOptionMenuItemInverter(const char *label, int axis, int center); +DMenuItemBase * CreateOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval); +DMenuItemBase * CreateListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID tex, FName command, int param); +DMenuItemBase * CreateListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, PalEntry color1, PalEntry color2, FName command, int param); + #endif diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 286a3f30b..2d9a69417 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -52,7 +52,7 @@ #include "i_sound.h" #include "cmdlib.h" -#include "optionmenuitems.h" + void ClearSaveGames(); @@ -65,6 +65,71 @@ bool mustPrintErrors; void I_BuildALDeviceList(FOptionValues *opt); +DEFINE_ACTION_FUNCTION(FOptionValues, GetCount) +{ + PARAM_PROLOGUE; + PARAM_NAME(grp); + int cnt = 0; + FOptionValues **pGrp = OptionValues.CheckKey(grp); + if (pGrp != nullptr) + { + cnt = (*pGrp)->mValues.Size(); + } + ACTION_RETURN_INT(cnt); +} + +DEFINE_ACTION_FUNCTION(FOptionValues, GetValue) +{ + PARAM_PROLOGUE; + PARAM_NAME(grp); + PARAM_UINT(index); + double val = 0; + FOptionValues **pGrp = OptionValues.CheckKey(grp); + if (pGrp != nullptr) + { + if (index < (*pGrp)->mValues.Size()) + { + val = (*pGrp)->mValues[index].Value; + } + } + ACTION_RETURN_FLOAT(val); +} + +DEFINE_ACTION_FUNCTION(FOptionValues, GetTextValue) +{ + PARAM_PROLOGUE; + PARAM_NAME(grp); + PARAM_UINT(index); + FString val; + FOptionValues **pGrp = OptionValues.CheckKey(grp); + if (pGrp != nullptr) + { + if (index < (*pGrp)->mValues.Size()) + { + val = (*pGrp)->mValues[index].TextValue; + } + } + ACTION_RETURN_STRING(val); +} + +DEFINE_ACTION_FUNCTION(FOptionValues, GetText) +{ + PARAM_PROLOGUE; + PARAM_NAME(grp); + PARAM_UINT(index); + FString val; + FOptionValues **pGrp = OptionValues.CheckKey(grp); + if (pGrp != nullptr) + { + if (index < (*pGrp)->mValues.Size()) + { + val = (*pGrp)->mValues[index].Text; + } + } + ACTION_RETURN_STRING(val); +} + + static void DeinitMenus() { { @@ -81,7 +146,7 @@ static void DeinitMenus() MenuDescriptors.Clear(); OptionValues.Clear(); DMenu::CurrentMenu = nullptr; - ClearSaveGames(); + savegameManager.ClearSaveGames(); } static FTextureID GetMenuTexture(const char* const name) @@ -265,87 +330,6 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) sc.MustGetNumber(); desc->mWRight = sc.Number; } - else if (sc.Compare("StaticPatch") || sc.Compare("StaticPatchCentered")) - { - bool centered = sc.Compare("StaticPatchCentered"); - sc.MustGetNumber(); - int x = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int y = sc.Number; - sc.MustGetStringName(","); - sc.MustGetString(); - FTextureID tex = GetMenuTexture(sc.String); - - DMenuItemBase *it = new DListMenuItemStaticPatch(x, y, tex, centered); - desc->mItems.Push(it); - } - else if (sc.Compare("StaticText") || sc.Compare("StaticTextCentered")) - { - bool centered = sc.Compare("StaticTextCentered"); - sc.MustGetNumber(); - int x = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int y = sc.Number; - sc.MustGetStringName(","); - sc.MustGetString(); - FString label = sc.String; - EColorRange cr = desc->mFontColor; - if (sc.CheckString(",")) - { - sc.MustGetString(); - cr = V_FindFontColor(sc.String); - if (cr == CR_UNTRANSLATED && !sc.Compare("untranslated")) cr = desc->mFontColor; - } - DMenuItemBase *it = new DListMenuItemStaticText(x, y, label, desc->mFont, cr, centered); - desc->mItems.Push(it); - } - else if (sc.Compare("PatchItem")) - { - sc.MustGetString(); - FTextureID tex = GetMenuTexture(sc.String); - sc.MustGetStringName(","); - sc.MustGetString(); - int hotkey = sc.String[0]; - sc.MustGetStringName(","); - sc.MustGetString(); - FName action = sc.String; - int param = 0; - if (sc.CheckString(",")) - { - sc.MustGetNumber(); - param = sc.Number; - } - - DMenuItemBase *it = new DListMenuItemPatch(desc->mXpos, desc->mYpos, desc->mLinespacing, hotkey, tex, action, param); - desc->mItems.Push(it); - desc->mYpos += desc->mLinespacing; - if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size()-1; - } - else if (sc.Compare("TextItem")) - { - sc.MustGetString(); - FString text = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - int hotkey = sc.String[0]; - sc.MustGetStringName(","); - sc.MustGetString(); - FName action = sc.String; - int param = 0; - if (sc.CheckString(",")) - { - sc.MustGetNumber(); - param = sc.Number; - } - - DMenuItemBase *it = new DListMenuItemText(desc->mXpos, desc->mYpos, desc->mLinespacing, hotkey, text, desc->mFont, desc->mFontColor, desc->mFontColor2, action, param); - desc->mItems.Push(it); - desc->mYpos += desc->mLinespacing; - if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size()-1; - - } else if (sc.Compare("Font")) { sc.MustGetString(); @@ -372,90 +356,134 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc) sc.MustGetString(); desc->mNetgameMessage = sc.String; } - else if (sc.Compare("PlayerDisplay")) - { - bool noportrait = false; - FName action = NAME_None; - sc.MustGetNumber(); - int x = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int y = sc.Number; - sc.MustGetStringName(","); - sc.MustGetString(); - PalEntry c1 = V_GetColor(nullptr, sc); - sc.MustGetStringName(","); - sc.MustGetString(); - PalEntry c2 = V_GetColor(nullptr, sc); - if (sc.CheckString(",")) - { - sc.MustGetNumber(); - noportrait = !!sc.Number; - if (sc.CheckString(",")) - { - sc.MustGetString(); - action = sc.String; - } - } - DListMenuItemPlayerDisplay *it = new DListMenuItemPlayerDisplay(desc, x, y, c1, c2, noportrait, action); - desc->mItems.Push(it); - } - else if (sc.Compare("PlayerNameBox")) - { - sc.MustGetString(); - FString text = sc.String; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int ofs = sc.Number; - sc.MustGetStringName(","); - sc.MustGetString(); - DMenuItemBase *it = new DPlayerNameBox(desc->mXpos, desc->mYpos, desc->mLinespacing, ofs, text, desc->mFont, desc->mFontColor, sc.String); - desc->mItems.Push(it); - desc->mYpos += desc->mLinespacing; - if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size()-1; - } - else if (sc.Compare("ValueText")) - { - sc.MustGetString(); - FString text = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FName action = sc.String; - FName values; - if (sc.CheckString(",")) - { - sc.MustGetString(); - values = sc.String; - } - DMenuItemBase *it = new DValueTextItem(desc->mXpos, desc->mYpos, desc->mLinespacing, text, desc->mFont, desc->mFontColor, desc->mFontColor2, action, values); - desc->mItems.Push(it); - desc->mYpos += desc->mLinespacing; - if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size()-1; - } - else if (sc.Compare("Slider")) - { - sc.MustGetString(); - FString text = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString action = sc.String; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int min = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int max = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - int step = sc.Number; - DMenuItemBase *it = new DSliderItem(desc->mXpos, desc->mYpos, desc->mLinespacing, text, desc->mFont, desc->mFontColor, action, min, max, step); - desc->mItems.Push(it); - desc->mYpos += desc->mLinespacing; - if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size()-1; - } else { - sc.ScriptError("Unknown keyword '%s'", sc.String); + bool success = false; + FStringf buildname("ListMenuItem%s", sc.String); + PClass *cls = PClass::FindClass(buildname); + if (cls != nullptr && cls->IsDescendantOf("ListMenuItem")) + { + auto func = dyn_cast(cls->Symbols.FindSymbol("Init", false)); + if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. + { + auto &args = func->Variants[0].Proto->ArgumentTypes; + TArray params; + int start = 1; + + params.Push(0); + if (args.Size() > 1 && args[1] == NewPointer(PClass::FindClass("ListMenuDescriptor"))) + { + params.Push(desc); + start = 2; + } + auto TypeCVar = NewPointer(NewNativeStruct("CVar", nullptr)); + + for (unsigned i = start; i < args.Size(); i++) + { + sc.MustGetString(); + if (args[i] == TypeString) + { + params.Push(FString(sc.String)); + } + else if (args[i] == TypeName) + { + params.Push(FName(sc.String).GetIndex()); + } + else if (args[i] == TypeColor) + { + params.Push(V_GetColor(nullptr, sc)); + } + else if (args[i] == TypeFont) + { + auto f = FFont::FindFont(sc.String); + if (f == nullptr) + { + sc.ScriptError("Unknown font %s", sc.String); + } + params.Push(f); + } + else if (args[i] == TypeTextureID) + { + auto f = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + if (!f.isValid()) + { + sc.ScriptError("Unknown texture %s", sc.String); + } + params.Push(f.GetIndex()); + } + else if (args[i]->IsKindOf(RUNTIME_CLASS(PInt))) + { + char *endp; + int v = (int)strtoll(sc.String, &endp, 0); + if (*endp != 0) + { + // special check for font color ranges. + v = V_FindFontColor(sc.String); + if (v == CR_UNTRANSLATED && !sc.Compare("untranslated")) + { + // todo: check other data types that may get used. + sc.ScriptError("Integer expected, got %s", sc.String); + } + } + if (args[i] == TypeBool) v = !!v; + params.Push(v); + } + else if (args[i]->IsKindOf(RUNTIME_CLASS(PFloat))) + { + char *endp; + double v = strtod(sc.String, &endp); + if (*endp != 0) + { + sc.ScriptError("Float expected, got %s", sc.String); + } + params.Push(v); + } + else if (args[i] == TypeCVar) + { + auto cv = FindCVar(sc.String, nullptr); + if (cv == nullptr && *sc.String) + { + sc.ScriptError("Unknown CVar %s", sc.String); + } + params.Push(cv); + } + else + { + sc.ScriptError("Invalid parameter type %s for menu item", args[i]->DescriptiveName()); + } + if (sc.CheckString(",")) + { + if (i == args.Size() - 1) + { + sc.ScriptError("Too many parameters for %s", cls->TypeName.GetChars()); + } + } + else + { + if (i < args.Size() - 1 && !(func->Variants[0].ArgFlags[i + 1] & VARF_Optional)) + { + sc.ScriptError("Insufficient parameters for %s", cls->TypeName.GetChars()); + } + break; + } + } + DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); + params[0] = item; + GlobalVMStack.Call(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); + desc->mItems.Push((DMenuItemBase*)item); + + if (cls->IsDescendantOf("ListMenuItemSelectable")) + { + desc->mYpos += desc->mLinespacing; + if (desc->mSelectedItem == -1) desc->mSelectedItem = desc->mItems.Size() - 1; + } + success = true; + } + } + if (!success) + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } } } for (auto &p : desc->mItems) @@ -638,21 +666,6 @@ static void ParseOptionSettings(FScanner &sc) // //============================================================================= -static EColorRange ParseOptionColor(FScanner &sc, DOptionMenuDescriptor *desc) -{ - EColorRange cr = OptionSettings.mFontColor; - if (sc.CheckString(",")) - { - sc.MustGetString(); - cr = V_FindFontColor(sc.String); - if (cr == CR_UNTRANSLATED && !sc.Compare("untranslated") && isdigit(sc.String[0])) - { - if (strtoll(sc.String, nullptr, 0)) cr = OptionSettings.mFontColorHeader; - } - } - return cr; -} - static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) { sc.MustGetStringName("{"); @@ -683,7 +696,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) { sc.MustGetString(); const PClass *cls = PClass::FindClass(sc.String); - if (cls == nullptr || !cls->IsDescendantOf(RUNTIME_CLASS(DOptionMenu))) + if (cls == nullptr || !cls->IsDescendantOf("OptionMenu")) { sc.ScriptError("Unknown menu class '%s'", sc.String); } @@ -714,198 +727,108 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc) sc.MustGetNumber(); desc->mIndent = sc.Number; } - else if (sc.Compare("Submenu")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - DOptionMenuItem *it = new DOptionMenuItemSubmenu(label, sc.String); - desc->mItems.Push(it); - } - else if (sc.Compare("Option")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString cvar = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString values = sc.String; - FString check; - int center = 0; - if (sc.CheckString(",")) - { - sc.MustGetString(); - if (*sc.String != 0) check = sc.String; - if (sc.CheckString(",")) - { - sc.MustGetNumber(); - center = sc.Number; - } - } - DOptionMenuItem *it = new DOptionMenuItemOption(label, cvar, values, check, center); - desc->mItems.Push(it); - } - else if (sc.Compare("Command")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - DOptionMenuItem *it = new DOptionMenuItemCommand(label, sc.String); - desc->mItems.Push(it); - } - else if (sc.Compare("SafeCommand")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString command = sc.String; - FString prompt; - // Check for optional custom prompt - if (sc.CheckString(",")) - { - sc.MustGetString(); - prompt = sc.String; - } - DOptionMenuItem *it = new DOptionMenuItemSafeCommand(label, command, prompt); - desc->mItems.Push(it); - } - else if (sc.Compare("Control") || sc.Compare("MapControl")) - { - bool map = sc.Compare("MapControl"); - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - DOptionMenuItem *it = new DOptionMenuItemControl(label, sc.String, map? &AutomapBindings : &Bindings); - desc->mItems.Push(it); - } - else if (sc.Compare("ColorPicker")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - DOptionMenuItem *it = new DOptionMenuItemColorPicker(label, sc.String); - desc->mItems.Push(it); - } - else if (sc.Compare("StaticText")) - { - sc.MustGetString(); - FString label = sc.String; - EColorRange cr = ParseOptionColor(sc, desc); - DOptionMenuItem *it = new DOptionMenuItemStaticText(label, cr); - desc->mItems.Push(it); - } - else if (sc.Compare("StaticTextSwitchable")) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString label2 = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FName action = sc.String; - EColorRange cr = ParseOptionColor(sc, desc); - DOptionMenuItem *it = new DOptionMenuItemStaticTextSwitchable(label, label2, action, cr); - desc->mItems.Push(it); - } - else if (sc.Compare("Slider")) - { - sc.MustGetString(); - FString text = sc.String; - sc.MustGetStringName(","); - sc.MustGetString(); - FString action = sc.String; - sc.MustGetStringName(","); - sc.MustGetFloat(); - double min = sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - double max = sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - double step = sc.Float; - int showvalue = 1; - if (sc.CheckString(",")) - { - sc.MustGetNumber(); - showvalue = sc.Number; - } - DOptionMenuItem *it = new DOptionMenuSliderCVar(text, action, min, max, step, showvalue); - desc->mItems.Push(it); - } - else if (sc.Compare("screenresolution")) - { - sc.MustGetString(); - DOptionMenuItem *it = new DOptionMenuScreenResolutionLine(sc.String); - desc->mItems.Push(it); - } - // [TP] -- Text input widget - else if ( sc.Compare( "TextField" )) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName( "," ); - sc.MustGetString(); - FString cvar = sc.String; - FString check; - - if ( sc.CheckString( "," )) - { - sc.MustGetString(); - check = sc.String; - } - - DOptionMenuItem* it = new DOptionMenuTextField( label, cvar, check ); - desc->mItems.Push( it ); - } - // [TP] -- Number input widget - else if ( sc.Compare( "NumberField" )) - { - sc.MustGetString(); - FString label = sc.String; - sc.MustGetStringName( "," ); - sc.MustGetString(); - FString cvar = sc.String; - float minimum = 0.0f; - float maximum = 100.0f; - float step = 1.0f; - FString check; - - if ( sc.CheckString( "," )) - { - sc.MustGetFloat(); - minimum = (float) sc.Float; - sc.MustGetStringName( "," ); - sc.MustGetFloat(); - maximum = (float) sc.Float; - - if ( sc.CheckString( "," )) - { - sc.MustGetFloat(); - step = (float) sc.Float; - - if ( sc.CheckString( "," )) - { - sc.MustGetString(); - check = sc.String; - } - } - } - - DOptionMenuItem* it = new DOptionMenuNumberField( label, cvar, - minimum, maximum, step, check ); - desc->mItems.Push( it ); - } else { - sc.ScriptError("Unknown keyword '%s'", sc.String); + bool success = false; + FStringf buildname("OptionMenuItem%s", sc.String); + // Handle one special case: MapControl maps to Control with one parameter different + PClass *cls = PClass::FindClass(buildname); + if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) + { + auto func = dyn_cast(cls->Symbols.FindSymbol("Init", false)); + if (func != nullptr && !(func->Variants[0].Flags & (VARF_Protected | VARF_Private))) // skip internal classes which have a protexted init method. + { + auto &args = func->Variants[0].Proto->ArgumentTypes; + TArray params; + + params.Push(0); + auto TypeCVar = NewPointer(NewNativeStruct("CVar", nullptr)); + for (unsigned i = 1; i < args.Size(); i++) + { + sc.MustGetString(); + if (args[i] == TypeString) + { + params.Push(FString(sc.String)); + } + else if (args[i] == TypeName) + { + params.Push(FName(sc.String).GetIndex()); + } + else if (args[i] == TypeColor) + { + params.Push(V_GetColor(nullptr, sc)); + } + else if (args[i]->IsKindOf(RUNTIME_CLASS(PInt))) + { + char *endp; + int v = (int)strtoll(sc.String, &endp, 0); + if (*endp != 0) + { + // special check for font color ranges. + v = V_FindFontColor(sc.String); + if (v == CR_UNTRANSLATED && !sc.Compare("untranslated")) + { + // todo: check other data types that may get used. + sc.ScriptError("Integer expected, got %s", sc.String); + } + // Color ranges need to be marked for option menu items to support an older feature where a boolean number could be passed instead. + v |= 0x12340000; + } + if (args[i] == TypeBool) v = !!v; + params.Push(v); + } + else if (args[i]->IsKindOf(RUNTIME_CLASS(PFloat))) + { + char *endp; + double v = strtod(sc.String, &endp); + if (*endp != 0) + { + sc.ScriptError("Float expected, got %s", sc.String); + } + params.Push(v); + } + else if (args[i] == TypeCVar) + { + auto cv = FindCVar(sc.String, nullptr); + if (cv == nullptr && *sc.String) + { + sc.ScriptError("Unknown CVar %s", sc.String); + } + params.Push(cv); + } + else + { + sc.ScriptError("Invalid parameter type %s for menu item", args[i]->DescriptiveName()); + } + if (sc.CheckString(",")) + { + if (i == args.Size() - 1) + { + sc.ScriptError("Too many parameters for %s", cls->TypeName.GetChars()); + } + } + else + { + if (i < args.Size() - 1 && !(func->Variants[0].ArgFlags[i + 1] & VARF_Optional)) + { + sc.ScriptError("Insufficient parameters for %s", cls->TypeName.GetChars()); + } + break; + } + } + + DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); + params[0] = item; + GlobalVMStack.Call(func->Variants[0].Implementation, ¶ms[0], params.Size(), nullptr, 0); + desc->mItems.Push((DMenuItemBase*)item); + + success = true; + } + } + if (!success) + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } } } for (auto &p : desc->mItems) @@ -1073,12 +996,11 @@ static void BuildEpisodeMenu() if (AllEpisodes[i].mPicName.IsNotEmpty()) { FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); - it = new DListMenuItemPatch(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, - tex, NAME_Skillmenu, i); + it = CreateListMenuItemPatch(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, tex, NAME_Skillmenu, i); } else { - it = new DListMenuItemText(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, + it = CreateListMenuItemText(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, AllEpisodes[i].mEpisodeName, ld->mFont, ld->mFontColor, ld->mFontColor2, NAME_Skillmenu, i); } ld->mItems.Push(it); @@ -1114,7 +1036,7 @@ static void BuildEpisodeMenu() GC::WriteBarrier(od); for(unsigned i = 0; i < AllEpisodes.Size(); i++) { - DOptionMenuItemSubmenu *it = new DOptionMenuItemSubmenu(AllEpisodes[i].mEpisodeName, "Skillmenu", i); + auto it = CreateOptionMenuItemSubmenu(AllEpisodes[i].mEpisodeName, "Skillmenu", i); od->mItems.Push(it); GC::WriteBarrier(od, it); } @@ -1171,7 +1093,7 @@ static void BuildPlayerclassMenu() if (numclassitems <= 1) { // create a dummy item that auto-chooses the default class. - DListMenuItemText *it = new DListMenuItemText(0, 0, 0, 'p', "player", + auto it = CreateListMenuItemText(0, 0, 0, 'p', "player", ld->mFont,ld->mFontColor, ld->mFontColor2, NAME_Episodemenu, -1000); ld->mAutoselect = ld->mItems.Push(it); success = true; @@ -1197,7 +1119,7 @@ static void BuildPlayerclassMenu() const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); if (pname != nullptr) { - DListMenuItemText *it = new DListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, i); ld->mItems.Push(it); ld->mYpos += ld->mLinespacing; @@ -1207,7 +1129,7 @@ static void BuildPlayerclassMenu() } if (n > 1 && !gameinfo.norandomplayerclass) { - DListMenuItemText *it = new DListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, 'r', + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, 'r', "$MNU_RANDOM", ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, -1); ld->mItems.Push(it); } @@ -1216,7 +1138,7 @@ static void BuildPlayerclassMenu() const char *pname = GetPrintableDisplayName(PlayerClasses[0].Type); if (pname != nullptr) { - DListMenuItemText *it = new DListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, 0); ld->mItems.Push(it); } @@ -1253,13 +1175,13 @@ static void BuildPlayerclassMenu() const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); if (pname != nullptr) { - DOptionMenuItemSubmenu *it = new DOptionMenuItemSubmenu(pname, "Episodemenu", i); + auto it = CreateOptionMenuItemSubmenu(pname, "Episodemenu", i); od->mItems.Push(it); GC::WriteBarrier(od, it); } } } - DOptionMenuItemSubmenu *it = new DOptionMenuItemSubmenu("Random", "Episodemenu", -1); + auto it = CreateOptionMenuItemSubmenu("Random", "Episodemenu", -1); od->mItems.Push(it); GC::WriteBarrier(od, it); } @@ -1340,14 +1262,14 @@ static void InitKeySections() for (unsigned i = 0; i < KeySections.Size(); i++) { FKeySection *sect = &KeySections[i]; - DOptionMenuItem *item = new DOptionMenuItemStaticText(" ", false); + DMenuItemBase *item = CreateOptionMenuItemStaticText(" ", false); menu->mItems.Push(item); - item = new DOptionMenuItemStaticText(sect->mTitle, true); + item = CreateOptionMenuItemStaticText(sect->mTitle, true); menu->mItems.Push(item); for (unsigned j = 0; j < sect->mActions.Size(); j++) { FKeyAction *act = §->mActions[j]; - item = new DOptionMenuItemControl(act->mTitle, act->mAction, &Bindings); + item = CreateOptionMenuItemControl(act->mTitle, act->mAction, &Bindings); menu->mItems.Push(item); } } @@ -1477,13 +1399,13 @@ void M_StartupSkillMenu(FGameStartup *gs) if (skill.PicName.Len() != 0 && pItemText == nullptr) { FTextureID tex = GetMenuTexture(skill.PicName); - li = new DListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i); + li = CreateListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i); } else { EColorRange color = (EColorRange)skill.GetTextColor(); if (color == CR_UNTRANSLATED) color = ld->mFontColor; - li = new DListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut, + li = CreateListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut, pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, i); } ld->mItems.Push(li); @@ -1528,7 +1450,7 @@ fail: for(unsigned int i = 0; i < AllSkills.Size(); i++) { FSkillInfo &skill = AllSkills[i]; - DOptionMenuItem *li; + DMenuItemBase *li; // Using a different name for skills that must be confirmed makes handling this easier. const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? "StartgameConfirm" : "Startgame"; @@ -1538,7 +1460,7 @@ fail: { pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); } - li = new DOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, i); + li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, i); od->mItems.Push(li); GC::WriteBarrier(od, li); if (!done) diff --git a/src/menu/menuinput.cpp b/src/menu/menuinput.cpp index 67f34635e..9a0b717b5 100644 --- a/src/menu/menuinput.cpp +++ b/src/menu/menuinput.cpp @@ -61,6 +61,8 @@ static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] = CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +DEFINE_FIELD(DTextEnterMenu, mInputGridOkay) + //============================================================================= // // @@ -68,15 +70,14 @@ CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) //============================================================================= // [TP] Added allowcolors -DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors) +DTextEnterMenu::DTextEnterMenu(DMenu *parent, const char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors) : DMenu(parent) { mEnterString = textbuffer; - mEnterSize = maxlen; - mEnterPos = (unsigned)strlen(textbuffer); + mEnterSize = maxlen < 0 ? UINT_MAX : unsigned(maxlen); mSizeMode = sizemode; mInputGridOkay = showgrid || m_showinputgrid; - if (mEnterPos > 0) + if (mEnterString.IsNotEmpty()) { InputGridX = INPUTGRID_WIDTH - 1; InputGridY = INPUTGRID_HEIGHT - 1; @@ -96,11 +97,18 @@ DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int // //============================================================================= -bool DTextEnterMenu::TranslateKeyboardEvents() +FString DTextEnterMenu::GetText() { - return mInputGridOkay; + return mEnterString; } +//============================================================================= +// +// +// +//============================================================================= + + //============================================================================= // // @@ -115,21 +123,19 @@ bool DTextEnterMenu::Responder(event_t *ev) if (ev->subtype == EV_GUI_Char) { mInputGridOkay = false; - if (mEnterPos < mEnterSize && + if (mEnterString.Len() < mEnterSize && (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) { - mEnterString[mEnterPos] = (char)ev->data1; - mEnterString[++mEnterPos] = 0; + 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 (mEnterPos > 0) + if (mEnterString.IsNotEmpty()) { - mEnterPos--; - mEnterString[mEnterPos] = 0; + mEnterString.Truncate(mEnterString.Len() - 1); } } else if (ev->subtype == EV_GUI_KeyDown) @@ -143,15 +149,15 @@ bool DTextEnterMenu::Responder(event_t *ev) } else if (ch == '\r') { - if (mEnterString[0]) + if (mEnterString.IsNotEmpty()) { // [TP] If we allow color codes, colorize the string now. if (AllowColors) - strbin(mEnterString); + mEnterString = strbin1(mEnterString); DMenu *parent = mParentMenu; - Close(); parent->CallMenuEvent(MKEY_Input, false); + Close(); return true; } } @@ -245,9 +251,9 @@ bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller) return true; case MKEY_Clear: - if (mEnterPos > 0) + if (mEnterString.IsNotEmpty()) { - mEnterString[--mEnterPos] = 0; + mEnterString.Truncate(mEnterString.Len() - 1); } return true; @@ -258,7 +264,7 @@ bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller) ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH]; if (ch == 0) // end { - if (mEnterString[0] != '\0') + if (mEnterString.IsNotEmpty()) { DMenu *parent = mParentMenu; Close(); @@ -268,16 +274,15 @@ bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller) } else if (ch == '\b') // bs { - if (mEnterPos > 0) + if (mEnterString.IsNotEmpty()) { - mEnterString[--mEnterPos] = 0; + mEnterString.Truncate(mEnterString.Len() - 1); } } - else if (mEnterPos < mEnterSize && + else if (mEnterString.Len() < mEnterSize && (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) { - mEnterString[mEnterPos] = ch; - mEnterString[++mEnterPos] = 0; + mEnterString += char(ch); } } return true; @@ -368,4 +373,24 @@ void DTextEnterMenu::Drawer () } } Super::Drawer(); -} \ No newline at end of file +} + + +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 8494d2192..e15940acb 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -47,7 +47,6 @@ #include "g_game.h" -extern FSaveGameNode *quickSaveSlot; EXTERN_CVAR (Bool, saveloadconfirmation) // [mxd] class DMessageBoxMenu : public DMenu @@ -549,7 +548,7 @@ DQuickSaveMenu::DQuickSaveMenu(bool playsound) { FString tempstring; - tempstring.Format(GStrings("QSPROMPT"), quickSaveSlot->Title); + tempstring.Format(GStrings("QSPROMPT"), savegameManager.quickSaveSlot->Title); Init(NULL, tempstring, 0, playsound); } @@ -563,7 +562,7 @@ void DQuickSaveMenu::HandleResult(bool res) { if (res) { - G_SaveGame (quickSaveSlot->Filename.GetChars(), quickSaveSlot->Title); + G_SaveGame (savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title); S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); M_ClearMenus(); } @@ -591,7 +590,7 @@ CCMD (quicksave) if (gamestate != GS_LEVEL) return; - if (quickSaveSlot == NULL) + if (savegameManager.quickSaveSlot == NULL) { S_Sound(CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); M_StartControlPanel(false); @@ -602,7 +601,7 @@ CCMD (quicksave) // [mxd]. Just save the game, no questions asked. if (!saveloadconfirmation) { - G_SaveGame(quickSaveSlot->Filename.GetChars(), quickSaveSlot->Title); + G_SaveGame(savegameManager.quickSaveSlot->Filename.GetChars(), savegameManager.quickSaveSlot->Title); return; } @@ -645,7 +644,7 @@ DQuickLoadMenu::DQuickLoadMenu(bool playsound) { FString tempstring; - tempstring.Format(GStrings("QLPROMPT"), quickSaveSlot->Title); + tempstring.Format(GStrings("QLPROMPT"), savegameManager.quickSaveSlot->Title); Init(NULL, tempstring, 0, playsound); } @@ -659,7 +658,7 @@ void DQuickLoadMenu::HandleResult(bool res) { if (res) { - G_LoadGame (quickSaveSlot->Filename.GetChars()); + G_LoadGame (savegameManager.quickSaveSlot->Filename.GetChars()); S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); M_ClearMenus(); } @@ -685,11 +684,11 @@ CCMD (quickload) return; } - if (quickSaveSlot == NULL) + if (savegameManager.quickSaveSlot == NULL) { M_StartControlPanel(true); // signal that whatever gets loaded should be the new quicksave - quickSaveSlot = (FSaveGameNode *)1; + savegameManager.quickSaveSlot = (FSaveGameNode *)1; M_SetMenu(NAME_Loadgamemenu); return; } @@ -697,7 +696,7 @@ CCMD (quickload) // [mxd]. Just load the game, no questions asked. if (!saveloadconfirmation) { - G_LoadGame(quickSaveSlot->Filename.GetChars()); + G_LoadGame(savegameManager.quickSaveSlot->Filename.GetChars()); return; } @@ -725,3 +724,12 @@ void M_StartMessage(const char *message, int messagemode, FName action) M_ActivateMenu(newmenu); } +DEFINE_ACTION_FUNCTION(DMenu, StartMessage) +{ + PARAM_PROLOGUE; + PARAM_STRING(msg); + PARAM_INT(mode); + 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 40524ac0c..acbb3c58e 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -3,7 +3,7 @@ ** Handler class for the option menus and associated items ** **--------------------------------------------------------------------------- -** Copyright 2010 Christoph Oelckers +** Copyright 2010-2017 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -49,485 +49,12 @@ #include "menu/menu.h" -//============================================================================= -// -// Draws a string in the console font, scaled to the 8x8 cells -// used by the default console font. -// -//============================================================================= - -void M_DrawConText (int color, int x, int y, const char *str) -{ - screen->DrawText (ConFont, color, x, y, str, - DTA_CellX, 8 * CleanXfac_1, - DTA_CellY, 8 * CleanYfac_1, - TAG_DONE); -} - -IMPLEMENT_CLASS(DOptionMenu, false, false) - -IMPLEMENT_POINTERS_START(DOptionMenu) -IMPLEMENT_POINTER(mFocusControl) -IMPLEMENT_POINTERS_END - //============================================================================= // // // //============================================================================= -DOptionMenu::DOptionMenu(DMenu *parent, DOptionMenuDescriptor *desc) -: DMenu(parent) -{ - CanScrollUp = false; - CanScrollDown = false; - VisBottom = 0; - mFocusControl = NULL; - Init(parent, desc); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Init(DMenu *parent, DOptionMenuDescriptor *desc) -{ - mParentMenu = parent; - GC::WriteBarrier(this, parent); - mDesc = desc; - if (mDesc != NULL && mDesc->mSelectedItem == -1) mDesc->mSelectedItem = FirstSelectable(); - -} - -//============================================================================= -// -// -// -//============================================================================= - -int DOptionMenu::FirstSelectable() -{ - if (mDesc != NULL) - { - // Go down to the first selectable item - int i = -1; - do - { - i++; - } - while (i < (int)mDesc->mItems.Size() && !mDesc->mItems[i]->Selectable()); - if (i>=0 && i < (int)mDesc->mItems.Size()) return i; - } - return -1; -} - -//============================================================================= -// -// -// -//============================================================================= -IMPLEMENT_CLASS(DOptionMenuItem, true, false) - - -DOptionMenuItem *DOptionMenu::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 DOptionMenu::Responder (event_t *ev) -{ - if (ev->type == EV_GUI_Event) - { - if (ev->subtype == EV_GUI_WheelUp) - { - int scrollamt = MIN(2, mDesc->mScrollPos); - mDesc->mScrollPos -= scrollamt; - return true; - } - else if (ev->subtype == EV_GUI_WheelDown) - { - if (CanScrollDown) - { - if (VisBottom < (int)(mDesc->mItems.Size()-2)) - { - mDesc->mScrollPos += 2; - VisBottom += 2; - } - else - { - mDesc->mScrollPos++; - VisBottom++; - } - } - return true; - } - } - return Super::Responder(ev); -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) -{ - int startedAt = mDesc->mSelectedItem; - - switch (mkey) - { - case MKEY_Up: - if (mDesc->mSelectedItem == -1) - { - mDesc->mSelectedItem = FirstSelectable(); - break; - } - do - { - --mDesc->mSelectedItem; - - if (mDesc->mScrollPos > 0 && - mDesc->mSelectedItem <= mDesc->mScrollTop + mDesc->mScrollPos) - { - mDesc->mScrollPos = MAX(mDesc->mSelectedItem - mDesc->mScrollTop - 1, 0); - } - - if (mDesc->mSelectedItem < 0) - { - // Figure out how many lines of text fit on the menu - int y = mDesc->mPosition; - - if (y <= 0) - { - if (BigFont && mDesc->mTitle.IsNotEmpty()) - { - y = -y + BigFont->GetHeight(); - } - else - { - y = -y; - } - } - y *= CleanYfac_1; - int rowheight = OptionSettings.mLinespacing * CleanYfac_1; - int maxitems = (screen->GetHeight() - rowheight - y) / rowheight + 1; - - mDesc->mScrollPos = MAX (0, (int)mDesc->mItems.Size() - maxitems + mDesc->mScrollTop); - mDesc->mSelectedItem = mDesc->mItems.Size()-1; - } - } - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt); - break; - - case MKEY_Down: - if (mDesc->mSelectedItem == -1) - { - mDesc->mSelectedItem = FirstSelectable(); - break; - } - do - { - ++mDesc->mSelectedItem; - - if (CanScrollDown && mDesc->mSelectedItem == VisBottom) - { - mDesc->mScrollPos++; - VisBottom++; - } - if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) - { - if (startedAt == -1) - { - mDesc->mSelectedItem = -1; - mDesc->mScrollPos = -1; - break; - } - else - { - mDesc->mSelectedItem = 0; - mDesc->mScrollPos = 0; - } - } - } - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt); - break; - - case MKEY_PageUp: - if (mDesc->mScrollPos > 0) - { - mDesc->mScrollPos -= VisBottom - mDesc->mScrollPos - mDesc->mScrollTop; - if (mDesc->mScrollPos < 0) - { - mDesc->mScrollPos = 0; - } - if (mDesc->mSelectedItem != -1) - { - mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1; - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) - { - if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) - { - mDesc->mSelectedItem = 0; - } - } - if (mDesc->mScrollPos > mDesc->mSelectedItem) - { - mDesc->mScrollPos = mDesc->mSelectedItem; - } - } - } - break; - - case MKEY_PageDown: - if (CanScrollDown) - { - int pagesize = VisBottom - mDesc->mScrollPos - mDesc->mScrollTop; - mDesc->mScrollPos += pagesize; - if (mDesc->mScrollPos + mDesc->mScrollTop + pagesize > (int)mDesc->mItems.Size()) - { - mDesc->mScrollPos = mDesc->mItems.Size() - mDesc->mScrollTop - pagesize; - } - if (mDesc->mSelectedItem != -1) - { - mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos; - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) - { - if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) - { - mDesc->mSelectedItem = 0; - } - } - if (mDesc->mScrollPos > mDesc->mSelectedItem) - { - mDesc->mScrollPos = mDesc->mSelectedItem; - } - } - } - break; - - case MKEY_Enter: - if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate()) - { - return true; - } - // fall through to default - default: - if (mDesc->mSelectedItem >= 0 && - mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true; - return Super::MenuEvent(mkey, fromcontroller); - } - - if (mDesc->mSelectedItem != startedAt) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - return true; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DOptionMenu::MouseEvent(int type, int x, int y) -{ - y = (y / CleanYfac_1) - mDesc->mDrawTop; - - if (mFocusControl) - { - mFocusControl->MouseEvent(type, x, y); - return true; - } - else - { - int yline = (y / OptionSettings.mLinespacing); - if (yline >= mDesc->mScrollTop) - { - yline += mDesc->mScrollPos; - } - if ((unsigned)yline < mDesc->mItems.Size() && mDesc->mItems[yline]->Selectable()) - { - if (yline != mDesc->mSelectedItem) - { - mDesc->mSelectedItem = yline; - //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - mDesc->mItems[yline]->MouseEvent(type, x, y); - return true; - } - } - mDesc->mSelectedItem = -1; - return Super::MouseEvent(type, x, y); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Ticker () -{ - Super::Ticker(); - for(unsigned i=0;imItems.Size(); i++) - { - mDesc->mItems[i]->Ticker(); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DOptionMenu::Drawer () -{ - int y = mDesc->mPosition; - - if (y <= 0) - { - if (BigFont && mDesc->mTitle.IsNotEmpty()) - { - const char *tt = mDesc->mTitle; - if (*tt == '$') tt = GStrings(tt+1); - screen->DrawText (BigFont, OptionSettings.mTitleColor, - (screen->GetWidth() - BigFont->StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1, - tt, DTA_CleanNoMove_1, true, TAG_DONE); - y = -y + BigFont->GetHeight(); - } - else - { - y = -y; - } - } - mDesc->mDrawTop = y; - int fontheight = OptionSettings.mLinespacing * CleanYfac_1; - y *= CleanYfac_1; - - int indent = mDesc->mIndent; - if (indent > 280) - { // kludge for the compatibility options with their extremely long labels - if (indent + 40 <= CleanWidth_1) - { - indent = (screen->GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1; - } - else - { - indent = screen->GetWidth() - 40 * CleanXfac_1; - } - } - else - { - indent = (indent - 160) * CleanXfac_1 + screen->GetWidth() / 2; - } - - int ytop = y + mDesc->mScrollTop * 8 * CleanYfac_1; - int lastrow = screen->GetHeight() - SmallFont->GetHeight() * CleanYfac_1; - - unsigned i; - for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight) - { - // Don't scroll the uppermost items - if ((int)i == mDesc->mScrollTop) - { - i += mDesc->mScrollPos; - if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu - } - bool isSelected = mDesc->mSelectedItem == (int)i; - int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, isSelected); - if (cur_indent >= 0 && isSelected && mDesc->mItems[i]->Selectable()) - { - if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this) - { - M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd"); - } - } - } - - CanScrollUp = (mDesc->mScrollPos > 0); - CanScrollDown = (i < mDesc->mItems.Size()); - VisBottom = i - 1; - - if (CanScrollUp) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a"); - } - if (CanScrollDown) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b"); - } - Super::Drawer(); -} - - -//============================================================================= -// -// base class for menu items -// -//============================================================================= - -int DOptionMenuItem::Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) -{ - return indent; -} - -bool DOptionMenuItem::Selectable() -{ - return true; -} - -bool DOptionMenuItem::MouseEvent(int type, int x, int y) -{ - if (Selectable() && type == DMenu::MOUSE_Release) - { - return DMenu::CurrentMenu->CallMenuEvent(MKEY_Enter, true); - } - return false; -} - -int DOptionMenuItem::GetIndent() -{ - if (mCentered) - { - return 0; - } - const char *label = mLabel.GetChars(); - if (*label == '$') label = GStrings(label+1); - return SmallFont->StringWidth(label); -} - -void DOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed) -{ - const char *label = mLabel.GetChars(); - if (*label == '$') label = GStrings(label+1); - - int overlay = grayed? MAKEARGB(96,48,0,0) : 0; - - int x; - int w = SmallFont->StringWidth(label) * CleanXfac_1; - if (!mCentered) x = indent - w; - else x = (screen->GetWidth() - w) / 2; - screen->DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); -} - - - void DOptionMenuDescriptor::CalcIndent() { // calculate the menu indent @@ -541,13 +68,20 @@ void DOptionMenuDescriptor::CalcIndent() mIndent = widest + 4; } +DEFINE_ACTION_FUNCTION(DOptionMenuDescriptor, CalcIndent) +{ + PARAM_SELF_PROLOGUE(DOptionMenuDescriptor); + self->CalcIndent(); + return 0; +} + //============================================================================= // // // //============================================================================= -DOptionMenuItem *DOptionMenuDescriptor::GetItem(FName name) +DMenuItemBase *DOptionMenuDescriptor::GetItem(FName name) { for(unsigned i=0;iDrawText (SmallFont, OptionSettings.mFontColorValue, - (screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text, - DTA_CleanNoMove_1, true, TAG_DONE); - } -}; - -IMPLEMENT_CLASS(DGameplayMenu, false, false) - -class DCompatibilityMenu : public DOptionMenu -{ - DECLARE_CLASS(DCompatibilityMenu, DOptionMenu) - -public: - DCompatibilityMenu() - {} - - void Drawer () - { - Super::Drawer(); - - char text[64]; - mysnprintf(text, 64, "compatflags = %d compatflags2 = %d", *compatflags, *compatflags2); - screen->DrawText (SmallFont, OptionSettings.mFontColorValue, - (screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text, - DTA_CleanNoMove_1, true, TAG_DONE); - } -}; - -IMPLEMENT_CLASS(DCompatibilityMenu, false, false) diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h deleted file mode 100644 index a7c16ec8c..000000000 --- a/src/menu/optionmenuitems.h +++ /dev/null @@ -1,1312 +0,0 @@ -/* -** optionmenuitems.h -** Control items for option menus -** -**--------------------------------------------------------------------------- -** Copyright 2010 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ -#include "v_text.h" -#include "gstrings.h" - - -void M_DrawConText (int color, int x, int y, const char *str); -void M_SetVideoMode(); - - - -//============================================================================= -// -// opens a submenu, action is a submenu name -// -//============================================================================= - -class DOptionMenuItemSubmenu : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuItemSubmenu, DOptionMenuItem) - - int mParam; -protected: - DOptionMenuItemSubmenu() { } -public: - DOptionMenuItemSubmenu(const char *label, const char *menu, int param = 0) - : DOptionMenuItem(label, menu) - { - mParam = param; - } - - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColorMore); - return indent; - } - - bool Activate() - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - M_SetMenu(mAction, mParam); - return true; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemSubmenu, false, false) -#endif - -//============================================================================= -// -// Executes a CCMD, action is a CCMD name -// -//============================================================================= - -class DOptionMenuItemCommand : public DOptionMenuItemSubmenu -{ - DECLARE_CLASS(DOptionMenuItemCommand, DOptionMenuItem) -protected: - DOptionMenuItemCommand() { } -public: - DOptionMenuItemCommand(const char *label, const char *menu) - : DOptionMenuItemSubmenu(label, menu) - { - } - - bool Activate() - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - C_DoCommand(mAction); - return true; - } - -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemCommand, false, false) -#endif - -//============================================================================= -// -// Executes a CCMD after confirmation, action is a CCMD name -// -//============================================================================= - -class DOptionMenuItemSafeCommand : public DOptionMenuItemCommand -{ - DECLARE_CLASS(DOptionMenuItemSafeCommand, DOptionMenuItemCommand) - // action is a CCMD - DOptionMenuItemSafeCommand() { } -protected: - FString mPrompt; - -public: - DOptionMenuItemSafeCommand(const char *label, const char *menu, const char *prompt) - : DOptionMenuItemCommand(label, menu) - , mPrompt(prompt) - { - } - - bool MenuEvent (int mkey, bool fromcontroller) - { - if (mkey == MKEY_MBYes) - { - C_DoCommand(mAction); - return true; - } - return DOptionMenuItemCommand::MenuEvent(mkey, fromcontroller); - } - - bool Activate() - { - const char *msg = mPrompt.IsNotEmpty() ? mPrompt.GetChars() : "$SAFEMESSAGE"; - if (*msg == '$') - { - msg = GStrings(msg + 1); - } - - const char *actionLabel = mLabel.GetChars(); - if (actionLabel != NULL) - { - if (*actionLabel == '$') - { - actionLabel = GStrings(actionLabel + 1); - } - } - - FString FullString; - FullString.Format(TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "\n\n" "%s", actionLabel != NULL ? actionLabel : "", msg); - - if (msg && FullString) M_StartMessage(FullString, 0); - return true; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemSafeCommand, false, false) -#endif - -//============================================================================= -// -// Base class for option lists -// -//============================================================================= - -class DOptionMenuItemOptionBase : public DOptionMenuItem -{ - DECLARE_ABSTRACT_CLASS(DOptionMenuItemOptionBase, DOptionMenuItem) -protected: - // action is a CVAR - FName mValues; // Entry in OptionValues table - FBaseCVar *mGrayCheck; - int mCenter; - - DOptionMenuItemOptionBase() {} -public: - - enum - { - OP_VALUES = 0x11001 - }; - - DOptionMenuItemOptionBase(const char *label, const char *menu, const char *values, const char *graycheck, int center) - : DOptionMenuItem(label, menu) - { - mValues = values; - mGrayCheck = (FBoolCVar*)FindCVar(graycheck, NULL); - mCenter = center; - } - - bool SetString(int i, const char *newtext) - { - if (i == OP_VALUES) - { - FOptionValues **opt = OptionValues.CheckKey(newtext); - mValues = newtext; - if (opt != NULL && *opt != NULL) - { - int s = GetSelection(); - if (s >= (int)(*opt)->mValues.Size()) s = 0; - SetSelection(s); // readjust the CVAR if its value is outside the range now - return true; - } - } - return false; - } - - //============================================================================= - virtual int GetSelection() = 0; - virtual void SetSelection(int Selection) = 0; - - //============================================================================= - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - bool grayed = mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool); - - if (mCenter) - { - indent = (screen->GetWidth() / 2); - } - drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed); - - int overlay = grayed? MAKEARGB(96,48,0,0) : 0; - const char *text; - int Selection = GetSelection(); - FOptionValues **opt = OptionValues.CheckKey(mValues); - if (Selection < 0 || opt == NULL || *opt == NULL) - { - text = "Unknown"; - } - else - { - text = (*opt)->mValues[Selection].Text; - } - if (*text == '$') text = GStrings(text + 1); - screen->DrawText (SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y, - text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); - return indent; - } - - //============================================================================= - bool MenuEvent (int mkey, bool fromcontroller) - { - FOptionValues **opt = OptionValues.CheckKey(mValues); - if (opt != NULL && *opt != NULL && (*opt)->mValues.Size() > 0) - { - int Selection = GetSelection(); - if (mkey == MKEY_Left) - { - if (Selection == -1) Selection = 0; - else if (--Selection < 0) Selection = (*opt)->mValues.Size()-1; - } - else if (mkey == MKEY_Right || mkey == MKEY_Enter) - { - if (++Selection >= (int)(*opt)->mValues.Size()) Selection = 0; - } - else - { - return DOptionMenuItem::MenuEvent(mkey, fromcontroller); - } - SetSelection(Selection); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - else - { - return DOptionMenuItem::MenuEvent(mkey, fromcontroller); - } - return true; - } - - bool Selectable() - { - return !(mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool)); - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemOptionBase, true, false) -#endif - -//============================================================================= -// -// Change a CVAR, action is the CVAR name -// -//============================================================================= - -class DOptionMenuItemOption : public DOptionMenuItemOptionBase -{ - DECLARE_CLASS(DOptionMenuItemOption, DOptionMenuItemOptionBase) - // action is a CVAR - FBaseCVar *mCVar; - - DOptionMenuItemOption() {} -public: - - DOptionMenuItemOption(const char *label, const char *menu, const char *values, const char *graycheck, int center) - : DOptionMenuItemOptionBase(label, menu, values, graycheck, center) - { - mCVar = FindCVar(mAction, NULL); - } - - //============================================================================= - int GetSelection() - { - int Selection = -1; - FOptionValues **opt = OptionValues.CheckKey(mValues); - if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0) - { - if ((*opt)->mValues[0].TextValue.IsEmpty()) - { - UCVarValue cv = mCVar->GetGenericRep(CVAR_Float); - for(unsigned i = 0; i < (*opt)->mValues.Size(); i++) - { - if (fabs(cv.Float - (*opt)->mValues[i].Value) < FLT_EPSILON) - { - Selection = i; - break; - } - } - } - else - { - const char *cv = mCVar->GetHumanString(); - for(unsigned i = 0; i < (*opt)->mValues.Size(); i++) - { - if ((*opt)->mValues[i].TextValue.CompareNoCase(cv) == 0) - { - Selection = i; - break; - } - } - } - } - return Selection; - } - - void SetSelection(int Selection) - { - UCVarValue value; - FOptionValues **opt = OptionValues.CheckKey(mValues); - if (opt != NULL && *opt != NULL && mCVar != NULL && (*opt)->mValues.Size() > 0) - { - if ((*opt)->mValues[0].TextValue.IsEmpty()) - { - value.Float = (float)(*opt)->mValues[Selection].Value; - mCVar->SetGenericRep (value, CVAR_Float); - } - else - { - value.String = (*opt)->mValues[Selection].TextValue.LockBuffer(); - mCVar->SetGenericRep (value, CVAR_String); - (*opt)->mValues[Selection].TextValue.UnlockBuffer(); - } - } - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemOption, false, false) -#endif - -//============================================================================= -// -// This class is used to capture the key to be used as the new key binding -// for a control item -// -//============================================================================= - -class DEnterKey : public DMenu -{ - DECLARE_CLASS(DEnterKey, DMenu) - - int *pKey; - -public: - DEnterKey(DMenu *parent, int *keyptr) - : DMenu(parent) - { - pKey = keyptr; - SetMenuMessage(1); - menuactive = MENU_WaitKey; // There should be a better way to disable GUI capture... - } - - bool TranslateKeyboardEvents() - { - return false; - } - - void SetMenuMessage(int which) - { - if (mParentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu))) - { - DOptionMenu *m = barrier_cast(mParentMenu); - DMenuItemBase *it = m->GetItem(NAME_Controlmessage); - if (it != NULL) - { - it->SetValue(0, which); - } - } - } - - bool Responder(event_t *ev) - { - if (ev->type == EV_KeyDown) - { - *pKey = ev->data1; - menuactive = MENU_On; - SetMenuMessage(0); - Close(); - mParentMenu->CallMenuEvent((ev->data1 == KEY_ESCAPE)? MKEY_Abort : MKEY_Input, 0); - return true; - } - return false; - } - - void Drawer() - { - mParentMenu->CallDrawer(); - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DEnterKey, true, false) -#endif - -//============================================================================= -// -// // Edit a key binding, Action is the CCMD to bind -// -//============================================================================= - -class DOptionMenuItemControl : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuItemControl, DOptionMenuItemOption) - FKeyBindings *mBindings; - int mInput; - bool mWaiting; - - DOptionMenuItemControl() {} -public: - - DOptionMenuItemControl(const char *label, const char *menu, FKeyBindings *bindings) - : DOptionMenuItem(label, menu) - { - mBindings = bindings; - mWaiting = false; - } - - - //============================================================================= - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - drawLabel(indent, y, mWaiting? OptionSettings.mFontColorHighlight: - (selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor)); - - char description[64]; - int Key1, Key2; - - mBindings->GetKeysForCommand(mAction, &Key1, &Key2); - C_NameKeys (description, Key1, Key2); - if (description[0]) - { - M_DrawConText(CR_WHITE, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, description); - } - else - { - screen->DrawText(SmallFont, CR_BLACK, indent + CURSORSPACE, y + (OptionSettings.mLinespacing-8)*CleanYfac_1, "---", - DTA_CleanNoMove_1, true, TAG_DONE); - } - return indent; - } - - //============================================================================= - bool MenuEvent(int mkey, bool fromcontroller) - { - if (mkey == MKEY_Input) - { - mWaiting = false; - mBindings->SetBind(mInput, mAction); - return true; - } - else if (mkey == MKEY_Clear) - { - mBindings->UnbindACommand(mAction); - return true; - } - else if (mkey == MKEY_Abort) - { - mWaiting = false; - return true; - } - return false; - } - - bool Activate() - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - mWaiting = true; - DMenu *input = new DEnterKey(DMenu::CurrentMenu, &mInput); - M_ActivateMenu(input); - return true; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemControl, false, false) -#endif -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuItemStaticText : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuItemStaticText, DOptionMenuItem) - EColorRange mColor; - - DOptionMenuItemStaticText() {} -public: - DOptionMenuItemStaticText(const char *label, bool header) - : DOptionMenuItem(label, NAME_None, true) - { - mColor = header ? OptionSettings.mFontColorHeader : OptionSettings.mFontColor; - } - - DOptionMenuItemStaticText(const char *label, EColorRange cr) - : DOptionMenuItem(label, NAME_None, true) - { - mColor = cr; - } - - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - drawLabel(indent, y, mColor); - return -1; - } - - bool Selectable() - { - return false; - } - -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemStaticText, false, false) -#endif - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuItemStaticTextSwitchable : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuItemStaticTextSwitchable, DOptionMenuItem) - EColorRange mColor; - FString mAltText; - int mCurrent; - - DOptionMenuItemStaticTextSwitchable() {} -public: - DOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, EColorRange cr) - : DOptionMenuItem(label, action, true) - { - mColor = cr; - mAltText = label2; - mCurrent = 0; - } - - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - const char *txt = mCurrent? mAltText.GetChars() : mLabel.GetChars(); - if (*txt == '$') txt = GStrings(txt + 1); - int w = SmallFont->StringWidth(txt) * CleanXfac_1; - int x = (screen->GetWidth() - w) / 2; - screen->DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true, TAG_DONE); - return -1; - } - - bool SetValue(int i, int val) - { - if (i == 0) - { - mCurrent = val; - return true; - } - return false; - } - - bool SetString(int i, const char *newtext) - { - if (i == 0) - { - mAltText = newtext; - return true; - } - return false; - } - - bool Selectable() - { - return false; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemStaticTextSwitchable, false, false) -#endif - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderBase : public DOptionMenuItem -{ - DECLARE_ABSTRACT_CLASS(DOptionMenuSliderBase, DOptionMenuItem) - // action is a CVAR - double mMin, mMax, mStep; - int mShowValue; - int mDrawX; - int mSliderShort; - -protected: - DOptionMenuSliderBase() {} -public: - DOptionMenuSliderBase(const char *label, double min, double max, double step, int showval) - : DOptionMenuItem(label, NAME_None) - { - mMin = min; - mMax = max; - mStep = step; - mShowValue = showval; - mDrawX = 0; - mSliderShort = 0; - } - - virtual double GetSliderValue() = 0; - virtual void SetSliderValue(double val) = 0; - - //============================================================================= - // - // Draw a slider. Set fracdigits negative to not display the current value numerically. - // - //============================================================================= - - void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent) - { - char textbuf[16]; - double range; - int maxlen = 0; - int right = x + (12*8 + 4) * CleanXfac_1; - int cy = y + (OptionSettings.mLinespacing-8)*CleanYfac_1; - - range = max - min; - double ccur = clamp(cur, min, max) - min; - - if (fracdigits >= 0) - { - mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, max); - maxlen = SmallFont->StringWidth(textbuf) * CleanXfac_1; - } - - mSliderShort = right + maxlen > screen->GetWidth(); - - if (!mSliderShort) - { - M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12"); - M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13"); - } - else - { - // On 320x200 we need a shorter slider - M_DrawConText(CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12"); - M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13"); - right -= 5*8*CleanXfac_1; - } - - if (fracdigits >= 0 && right + maxlen <= screen->GetWidth()) - { - mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); - screen->DrawText(SmallFont, CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); - } - } - - - //============================================================================= - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor); - mDrawX = indent + CURSORSPACE; - DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent); - return indent; - } - - //============================================================================= - bool MenuEvent (int mkey, bool fromcontroller) - { - double value = GetSliderValue(); - - if (mkey == MKEY_Left) - { - value -= mStep; - } - else if (mkey == MKEY_Right) - { - value += mStep; - } - else - { - return DOptionMenuItem::MenuEvent(mkey, fromcontroller); - } - if (fabs(value) < FLT_EPSILON) value = 0; - SetSliderValue(clamp(value, mMin, mMax)); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - return true; - } - - bool MouseEvent(int type, int x, int y) - { - DOptionMenu *lm = static_cast(DMenu::CurrentMenu); - if (type != DMenu::MOUSE_Click) - { - if (!lm->CheckFocus(this)) return false; - } - if (type == DMenu::MOUSE_Release) - { - lm->ReleaseFocus(); - } - - int slide_left = mDrawX+8*CleanXfac_1; - int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each. - - if (type == DMenu::MOUSE_Click) - { - if (x < slide_left || x >= slide_right) return true; - } - - x = clamp(x, slide_left, slide_right); - double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left); - if (v != GetSliderValue()) - { - SetSliderValue(v); - //S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - if (type == DMenu::MOUSE_Click) - { - lm->SetFocus(this); - } - return true; - } - -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuSliderBase, true, false) -#endif - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderCVar : public DOptionMenuSliderBase -{ - DECLARE_CLASS(DOptionMenuSliderCVar, DOptionMenuSliderBase) - FBaseCVar *mCVar; - - DOptionMenuSliderCVar() {} -public: - DOptionMenuSliderCVar(const char *label, const char *menu, double min, double max, double step, int showval) - : DOptionMenuSliderBase(label, min, max, step, showval) - { - mCVar = FindCVar(menu, NULL); - } - - double GetSliderValue() - { - if (mCVar != NULL) - { - return mCVar->GetGenericRep(CVAR_Float).Float; - } - else - { - return 0; - } - } - - void SetSliderValue(double val) - { - if (mCVar != NULL) - { - UCVarValue value; - value.Float = (float)val; - mCVar->SetGenericRep(value, CVAR_Float); - } - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuSliderCVar, false, false) -#endif - -//============================================================================= -// -// -// -//============================================================================= - -class DOptionMenuSliderVar : public DOptionMenuSliderBase -{ - DECLARE_CLASS(DOptionMenuSliderVar, DOptionMenuSliderBase) - float *mPVal; - - DOptionMenuSliderVar() {} -public: - - DOptionMenuSliderVar(const char *label, float *pVal, double min, double max, double step, int showval) - : DOptionMenuSliderBase(label, min, max, step, showval) - { - mPVal = pVal; - } - - double GetSliderValue() - { - return *mPVal; - } - - void SetSliderValue(double val) - { - *mPVal = (float)val; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuSliderVar, false, false) -#endif - -//============================================================================= -// -// // Edit a key binding, Action is the CCMD to bind -// -//============================================================================= - -class DOptionMenuItemColorPicker : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuItemColorPicker, DOptionMenuItem) - FColorCVar *mCVar; - - DOptionMenuItemColorPicker() {} -public: - - enum - { - CPF_RESET = 0x20001, - }; - - DOptionMenuItemColorPicker(const char *label, const char *menu) - : DOptionMenuItem(label, menu) - { - FBaseCVar *cv = FindCVar(menu, NULL); - if (cv != NULL && cv->GetRealType() == CVAR_Color) - { - mCVar = (FColorCVar*)cv; - } - else mCVar = NULL; - } - - //============================================================================= - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor); - - if (mCVar != NULL) - { - int box_x = indent + CURSORSPACE; - int box_y = y + CleanYfac_1; - screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + OptionSettings.mLinespacing*CleanYfac_1, - -1, (uint32)*mCVar | 0xff000000); - } - return indent; - } - - bool SetValue(int i, int v) - { - if (i == CPF_RESET && mCVar != NULL) - { - mCVar->ResetToDefault(); - return true; - } - return false; - } - - bool Activate() - { - if (mCVar != NULL) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - DMenu *picker = StartPickerMenu(DMenu::CurrentMenu, mLabel, mCVar); - if (picker != NULL) - { - M_ActivateMenu(picker); - return true; - } - } - return false; - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuItemColorPicker, false, false) -#endif - -class DOptionMenuScreenResolutionLine : public DOptionMenuItem -{ - DECLARE_CLASS(DOptionMenuScreenResolutionLine, DOptionMenuItem) - - FString mResTexts[3]; - int mSelection; - int mHighlight; - int mMaxValid; - - DOptionMenuScreenResolutionLine() {} -public: - - enum - { - SRL_INDEX = 0x30000, - SRL_SELECTION = 0x30003, - SRL_HIGHLIGHT = 0x30004, - }; - - DOptionMenuScreenResolutionLine(const char *action) - : DOptionMenuItem("", action) - { - mSelection = 0; - mHighlight = -1; - } - - bool SetValue(int i, int v) - { - if (i == SRL_SELECTION) - { - mSelection = v; - return true; - } - else if (i == SRL_HIGHLIGHT) - { - mHighlight = v; - return true; - } - return false; - } - - bool GetValue(int i, int *v) - { - if (i == SRL_SELECTION) - { - *v = mSelection; - return true; - } - return false; - } - - bool SetString(int i, const char *newtext) - { - if (i >= SRL_INDEX && i <= SRL_INDEX+2) - { - mResTexts[i-SRL_INDEX] = newtext; - if (mResTexts[0].IsEmpty()) mMaxValid = -1; - else if (mResTexts[1].IsEmpty()) mMaxValid = 0; - else if (mResTexts[2].IsEmpty()) mMaxValid = 1; - else mMaxValid = 2; - return true; - } - return false; - } - - bool GetString(int i, char *s, int len) - { - if (i >= SRL_INDEX && i <= SRL_INDEX+2) - { - strncpy(s, mResTexts[i-SRL_INDEX], len-1); - s[len-1] = 0; - return true; - } - return false; - } - - bool MenuEvent (int mkey, bool fromcontroller) - { - if (mkey == MKEY_Left) - { - if (--mSelection < 0) mSelection = mMaxValid; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - } - else if (mkey == MKEY_Right) - { - if (++mSelection > mMaxValid) mSelection = 0; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - } - else - { - return DOptionMenuItem::MenuEvent(mkey, fromcontroller); - } - return false; - } - - bool MouseEvent(int type, int x, int y) - { - int colwidth = screen->GetWidth() / 3; - mSelection = x / colwidth; - return DOptionMenuItem::MouseEvent(type, x, y); - } - - bool Activate() - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - M_SetVideoMode(); - return true; - } - - int Draw(DOptionMenuDescriptor *desc, int y, int indent, bool selected) - { - int colwidth = screen->GetWidth() / 3; - EColorRange color; - - for (int x = 0; x < 3; x++) - { - if (selected && mSelection == x) - color = OptionSettings.mFontColorSelection; - else if (x == mHighlight) - color = OptionSettings.mFontColorHighlight; - else - color = OptionSettings.mFontColorValue; - - screen->DrawText (SmallFont, color, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true, TAG_DONE); - } - return colwidth * mSelection + 20 * CleanXfac_1 - CURSORSPACE; - } - - bool Selectable() - { - return mMaxValid >= 0; - } - - void Ticker() - { - if (Selectable() && mSelection > mMaxValid) - { - mSelection = mMaxValid; - } - } -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuScreenResolutionLine, false, false) -#endif - -//============================================================================= -// -// [TP] DOptionMenuFieldBase -// -// Base class for input fields -// -//============================================================================= - -class DOptionMenuFieldBase : public DOptionMenuItem -{ - DECLARE_ABSTRACT_CLASS(DOptionMenuFieldBase, DOptionMenuItem) - -protected: - DOptionMenuFieldBase() {} -public: - DOptionMenuFieldBase ( const char* label, const char* menu, const char* graycheck ) : - DOptionMenuItem ( label, menu ), - mCVar ( FindCVar( mAction, NULL )), - mGrayCheck (( graycheck && strlen( graycheck )) ? FindCVar( graycheck, NULL ) : NULL ) {} - - const char* GetCVarString() - { - if ( mCVar == NULL ) - return ""; - - return mCVar->GetHumanString(); - } - - virtual FString Represent() - { - return GetCVarString(); - } - - int Draw ( DOptionMenuDescriptor*, int y, int indent, bool selected ) - { - bool grayed = mGrayCheck != NULL && !( mGrayCheck->GetGenericRep( CVAR_Bool ).Bool ); - drawLabel( indent, y, selected ? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, grayed ); - int overlay = grayed? MAKEARGB( 96, 48, 0, 0 ) : 0; - - screen->DrawText( SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y, - Represent().GetChars(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE ); - return indent; - } - - bool GetString ( int i, char* s, int len ) - { - if ( i == 0 ) - { - strncpy( s, GetCVarString(), len ); - s[len - 1] = '\0'; - return true; - } - - return false; - } - - bool SetString ( int i, const char* s ) - { - if ( i == 0 ) - { - if ( mCVar ) - { - UCVarValue vval; - vval.String = s; - mCVar->SetGenericRep( vval, CVAR_String ); - } - - return true; - } - - return false; - } - -protected: - // Action is a CVar in this class and derivatives. - FBaseCVar* mCVar; - FBaseCVar* mGrayCheck; -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuFieldBase, true, false) -#endif - -//============================================================================= -// -// [TP] DOptionMenuTextField -// -// A text input field widget, for use with string CVars. -// -//============================================================================= - -class DOptionMenuTextField : public DOptionMenuFieldBase -{ - DECLARE_CLASS(DOptionMenuTextField, DOptionMenuFieldBase) - - DOptionMenuTextField() {} -public: - DOptionMenuTextField ( const char *label, const char* menu, const char* graycheck ) : - DOptionMenuFieldBase ( label, menu, graycheck ), - mEntering ( false ) {} - - FString Represent() - { - FString text = mEntering ? mEditName : GetCVarString(); - - if ( mEntering ) - text += ( gameinfo.gametype & GAME_DoomStrifeChex ) ? '_' : '['; - - return text; - } - - int Draw(DOptionMenuDescriptor*desc, int y, int indent, bool selected) - { - if (mEntering) - { - // reposition the text so that the cursor is visible when in entering mode. - FString text = Represent(); - int tlen = SmallFont->StringWidth(text) * CleanXfac_1; - int newindent = screen->GetWidth() - tlen - CURSORSPACE; - if (newindent < indent) indent = newindent; - } - return DOptionMenuFieldBase::Draw(desc, y, indent, selected); - } - - bool MenuEvent ( int mkey, bool fromcontroller ) - { - if ( mkey == MKEY_Enter ) - { - S_Sound( CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE ); - strcpy( mEditName, GetCVarString() ); - mEntering = true; - DMenu* input = new DTextEnterMenu ( DMenu::CurrentMenu, mEditName, sizeof mEditName, 2, fromcontroller ); - M_ActivateMenu( input ); - return true; - } - else if ( mkey == MKEY_Input ) - { - if ( mCVar ) - { - UCVarValue vval; - vval.String = mEditName; - mCVar->SetGenericRep( vval, CVAR_String ); - } - - mEntering = false; - return true; - } - else if ( mkey == MKEY_Abort ) - { - mEntering = false; - return true; - } - - return DOptionMenuItem::MenuEvent( mkey, fromcontroller ); - } - -private: - bool mEntering; - char mEditName[128]; -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuTextField, false, false) -#endif - -//============================================================================= -// -// [TP] FOptionMenuNumberField -// -// A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e. -// where the user is interested in the exact value specifically) -// -//============================================================================= - -class DOptionMenuNumberField : public DOptionMenuFieldBase -{ - DECLARE_CLASS(DOptionMenuNumberField, DOptionMenuFieldBase) - - DOptionMenuNumberField() {} -public: - DOptionMenuNumberField ( const char *label, const char* menu, float minimum, float maximum, - float step, const char* graycheck ) - : DOptionMenuFieldBase ( label, menu, graycheck ), - mMinimum ( minimum ), - mMaximum ( maximum ), - mStep ( step ) - { - if ( mMaximum <= mMinimum ) - swapvalues( mMinimum, mMaximum ); - - if ( mStep <= 0 ) - mStep = 1; - } - - bool MenuEvent ( int mkey, bool fromcontroller ) - { - if ( mCVar ) - { - float value = mCVar->GetGenericRep( CVAR_Float ).Float; - - if ( mkey == MKEY_Left ) - { - value -= mStep; - - if ( value < mMinimum ) - value = mMaximum; - } - else if ( mkey == MKEY_Right || mkey == MKEY_Enter ) - { - value += mStep; - - if ( value > mMaximum ) - value = mMinimum; - } - else - return DOptionMenuItem::MenuEvent( mkey, fromcontroller ); - - UCVarValue vval; - vval.Float = value; - mCVar->SetGenericRep( vval, CVAR_Float ); - S_Sound( CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE ); - } - - return true; - } - -private: - float mMinimum; - float mMaximum; - float mStep; -}; - -#ifndef NO_IMP -IMPLEMENT_CLASS(DOptionMenuNumberField, false, false) -#endif diff --git a/src/menu/playerdisplay.cpp b/src/menu/playerdisplay.cpp deleted file mode 100644 index 4b01ab398..000000000 --- a/src/menu/playerdisplay.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* -** playerdisplay.cpp -** The player display for the player setup and class selection screen -** -**--------------------------------------------------------------------------- -** 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 "doomtype.h" -#include "doomstat.h" -#include "d_player.h" -#include "templates.h" -#include "menu/menu.h" -#include "colormatcher.h" -#include "textures/textures.h" -#include "w_wad.h" -#include "v_font.h" -#include "v_video.h" -#include "g_level.h" -#include "gi.h" -#include "r_defs.h" -#include "r_state.h" -#include "r_data/r_translate.h" - - -//============================================================================= -// -// -// -//============================================================================= -IMPLEMENT_CLASS(DListMenuItemPlayerDisplay, false, false) -DListMenuItemPlayerDisplay::DListMenuItemPlayerDisplay(DListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action) -: DMenuItemBase(x, y, action) -{ - mOwner = menu; - - FRemapTable *bdremap = translationtables[TRANSLATION_Players][MAXPLAYERS + 1]; - for (int i = 0; i < 256; i++) - { - int r = c1.r + c2.r * i / 255; - int g = c1.g + c2.g * i / 255; - int b = c1.b + c2.b * i / 255; - bdremap->Remap[i] = ColorMatcher.Pick (r, g, b); - bdremap->Palette[i] = PalEntry(255, r, g, b); - } - auto id = TexMan.CheckForTexture("PlayerBackdrop", FTexture::TEX_MiscPatch); - mBackdrop = TexMan[id]; - mPlayerClass = NULL; - mPlayerState = NULL; - mNoportrait = np; - mMode = 0; - mRotation = 0; - mTranslate = false; - mSkin = 0; - mRandomClass = 0; - mRandomTimer = 0; - mClassNum = -1; -} - - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::OnDestroy() -{ -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::UpdateRandomClass() -{ - if (--mRandomTimer < 0) - { - if (++mRandomClass >= (int)PlayerClasses.Size ()) mRandomClass = 0; - mPlayerClass = &PlayerClasses[mRandomClass]; - mPlayerState = GetDefaultByType (mPlayerClass->Type)->SeeState; - if (mPlayerState == NULL) - { // No see state, so try spawn state. - mPlayerState = GetDefaultByType (mPlayerClass->Type)->SpawnState; - } - mPlayerTics = mPlayerState != NULL ? mPlayerState->GetTics() : -1; - mRandomTimer = 6; - - // Since the newly displayed class may used a different translation - // range than the old one, we need to update the translation, too. - UpdateTranslation(); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::UpdateTranslation() -{ - int PlayerColor = players[consoleplayer].userinfo.GetColor(); - int PlayerSkin = players[consoleplayer].userinfo.GetSkin(); - int PlayerColorset = players[consoleplayer].userinfo.GetColorSet(); - - if (mPlayerClass != NULL) - { - PlayerSkin = R_FindSkin (skins[PlayerSkin].name, int(mPlayerClass - &PlayerClasses[0])); - R_GetPlayerTranslation(PlayerColor, GetColorSet(mPlayerClass->Type, PlayerColorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::SetPlayerClass(int classnum, bool force) -{ - if (classnum < 0 || classnum >= (int)PlayerClasses.Size ()) - { - if (mClassNum != -1) - { - mClassNum = -1; - mRandomTimer = 0; - UpdateRandomClass(); - } - } - else if (mPlayerClass != &PlayerClasses[classnum] || force) - { - mPlayerClass = &PlayerClasses[classnum]; - mPlayerState = GetDefaultByType (mPlayerClass->Type)->SeeState; - if (mPlayerState == NULL) - { // No see state, so try spawn state. - mPlayerState = GetDefaultByType (mPlayerClass->Type)->SpawnState; - } - mPlayerTics = mPlayerState != NULL ? mPlayerState->GetTics() : -1; - mClassNum = classnum; - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DListMenuItemPlayerDisplay::UpdatePlayerClass() -{ - if (mOwner->mSelectedItem >= 0) - { - int classnum; - FName seltype = mOwner->mItems[mOwner->mSelectedItem]->GetAction(&classnum); - - if (seltype != NAME_Episodemenu) return false; - if (PlayerClasses.Size() == 0) return false; - - SetPlayerClass(classnum); - return true; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DListMenuItemPlayerDisplay::SetValue(int i, int value) -{ - switch (i) - { - case PDF_MODE: - mMode = value; - return true; - - case PDF_ROTATION: - mRotation = value; - return true; - - case PDF_TRANSLATE: - mTranslate = value; - - case PDF_CLASS: - SetPlayerClass(value, true); - break; - - case PDF_SKIN: - mSkin = value; - break; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::Ticker() -{ - if (mClassNum < 0) UpdateRandomClass(); - - if (mPlayerState != NULL && mPlayerState->GetTics () != -1 && mPlayerState->GetNextState () != NULL) - { - if (--mPlayerTics <= 0) - { - mPlayerState = mPlayerState->GetNextState(); - mPlayerTics = mPlayerState->GetTics(); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DListMenuItemPlayerDisplay::Drawer(bool selected) -{ - if (mMode == 0 && !UpdatePlayerClass()) - { - return; - } - - FName portrait = ((APlayerPawn*)GetDefaultByType(mPlayerClass->Type))->Portrait; - - if (portrait != NAME_None && !mNoportrait) - { - FTextureID texid = TexMan.CheckForTexture(portrait.GetChars(), FTexture::TEX_MiscPatch); - if (texid.isValid()) - { - FTexture *tex = TexMan(texid); - if (tex != NULL) - { - screen->DrawTexture (tex, mXpos, mYpos, DTA_Clean, true, TAG_DONE); - return; - } - } - } - int x = (mXpos - 160) * CleanXfac + (SCREENWIDTH>>1); - int y = (mYpos - 100) * CleanYfac + (SCREENHEIGHT>>1); - - screen->DrawTexture(mBackdrop, x, y - 1, - DTA_DestWidth, 72 * CleanXfac, - DTA_DestHeight, 80 * CleanYfac, - DTA_TranslationIndex, TRANSLATION(TRANSLATION_Players, MAXPLAYERS + 1), - DTA_Masked, true, - TAG_DONE); - - V_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); - - spriteframe_t *sprframe = NULL; - DVector2 Scale; - - if (mPlayerState != NULL) - { - if (mSkin == 0) - { - sprframe = &SpriteFrames[sprites[mPlayerState->sprite].spriteframes + mPlayerState->GetFrame()]; - Scale = GetDefaultByType(mPlayerClass->Type)->Scale; - } - else - { - sprframe = &SpriteFrames[sprites[skins[mSkin].sprite].spriteframes + mPlayerState->GetFrame()]; - Scale = skins[mSkin].Scale; - } - } - - if (sprframe != NULL) - { - FTexture *tex = TexMan(sprframe->Texture[mRotation]); - if (tex != NULL && tex->UseType != FTexture::TEX_Null) - { - int trans = mTranslate? TRANSLATION(TRANSLATION_Players, MAXPLAYERS) : 0; - screen->DrawTexture (tex, - x + 36*CleanXfac, y + 71*CleanYfac, - DTA_DestWidthF, tex->GetScaledWidthDouble() * CleanXfac * Scale.X, - DTA_DestHeightF, tex->GetScaledHeightDouble() * CleanYfac * Scale.Y, - DTA_TranslationIndex, trans, - DTA_FlipX, sprframe->Flip & (1 << mRotation), - TAG_DONE); - } - } -} - diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index ee8ea1853..3a1376052 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -55,428 +55,6 @@ EXTERN_CVAR (Float, autoaim) EXTERN_CVAR(Bool, neverswitchonpickup) EXTERN_CVAR (Bool, cl_run) -//============================================================================= -// -// Player's name -// -//============================================================================= -IMPLEMENT_CLASS(DPlayerNameBox, false, false) - -DPlayerNameBox::DPlayerNameBox(int x, int y, int height, int frameofs, const char *text, FFont *font, EColorRange color, FName action) -: DListMenuItemSelectable(x, y, height, action) -{ - mText = text; - mFont = font; - mFontColor = color; - mFrameSize = frameofs; - mPlayerName[0] = 0; - mEntering = false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DPlayerNameBox::SetString(int i, const char *s) -{ - if (i == 0) - { - strncpy(mPlayerName, s, MAXPLAYERNAME); - mPlayerName[MAXPLAYERNAME] = 0; - return true; - } - return false; -} - -bool DPlayerNameBox::GetString(int i, char *s, int len) -{ - if (i == 0) - { - strncpy(s, mPlayerName, len); - s[len] = 0; - return true; - } - return false; -} - -//============================================================================= -// -// [RH] Width of the border is variable -// -//============================================================================= - -void DPlayerNameBox::DrawBorder (int x, int y, int len) -{ - FTexture *left = TexMan[TexMan.CheckForTexture("M_LSLEFT", FTexture::TEX_MiscPatch)]; - FTexture *mid = TexMan[TexMan.CheckForTexture("M_LSCNTR", FTexture::TEX_MiscPatch)]; - FTexture *right = TexMan[TexMan.CheckForTexture("M_LSRGHT", FTexture::TEX_MiscPatch)]; - if (left != NULL && right != NULL && mid != NULL) - { - int i; - - screen->DrawTexture (left, x-8, y+7, DTA_Clean, true, TAG_DONE); - - for (i = 0; i < len; i++) - { - screen->DrawTexture (mid, x, y+7, DTA_Clean, true, TAG_DONE); - x += 8; - } - - screen->DrawTexture (right, x, y+7, DTA_Clean, true, TAG_DONE); - } - else - { - FTexture *slot = TexMan[TexMan.CheckForTexture("M_FSLOT", FTexture::TEX_MiscPatch)]; - if (slot != NULL) - { - screen->DrawTexture (slot, x, y+1, DTA_Clean, true, TAG_DONE); - } - else - { - screen->Clear(x, y, x + len, y + SmallFont->GetHeight() * 3/2, -1, 0); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -void DPlayerNameBox::Drawer(bool selected) -{ - const char *text = mText; - if (text != NULL) - { - if (*text == '$') text = GStrings(text+1); - screen->DrawText(mFont, selected? OptionSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE); - } - - // Draw player name box - int x = mXpos + mFont->StringWidth(text) + 16 + mFrameSize; - DrawBorder (x, mYpos - mFrameSize, MAXPLAYERNAME+1); - if (!mEntering) - { - screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, mPlayerName, - DTA_Clean, true, TAG_DONE); - } - else - { - size_t l = strlen(mEditName); - mEditName[l] = SmallFont->GetCursor(); - mEditName[l+1] = 0; - - screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, mEditName, - DTA_Clean, true, TAG_DONE); - - mEditName[l] = 0; - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DPlayerNameBox::MenuEvent(int mkey, bool fromcontroller) -{ - if (mkey == MKEY_Enter) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - strcpy(mEditName, mPlayerName); - mEntering = true; - DMenu *input = new DTextEnterMenu(DMenu::CurrentMenu, mEditName, MAXPLAYERNAME, 2, fromcontroller); - M_ActivateMenu(input); - return true; - } - else if (mkey == MKEY_Input) - { - strcpy(mPlayerName, mEditName); - mEntering = false; - return true; - } - else if (mkey == MKEY_Abort) - { - mEntering = false; - return true; - } - return false; -} - -//============================================================================= -// -// items for the player menu -// -//============================================================================= -IMPLEMENT_CLASS(DValueTextItem, false, false) - -DValueTextItem::DValueTextItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, EColorRange valuecolor, FName action, FName values) -: DListMenuItemSelectable(x, y, height, action) -{ - mText = text; - mFont = font; - mFontColor = color; - mFontColor2 = valuecolor; - mSelection = 0; - if (values != NAME_None) - { - FOptionValues **opt = OptionValues.CheckKey(values); - if (opt != NULL) - { - for(unsigned i=0;i<(*opt)->mValues.Size(); i++) - { - SetString(i, (*opt)->mValues[i].Text); - } - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DValueTextItem::SetString(int i, const char *s) -{ - // should actually use the index... - FString str = s; - if (i==0) mSelections.Clear(); - mSelections.Push(str); - return true; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DValueTextItem::SetValue(int i, int value) -{ - if (i == 0) - { - mSelection = value; - return true; - } - return false; -} - -bool DValueTextItem::GetValue(int i, int *pvalue) -{ - if (i == 0) - { - *pvalue = mSelection; - return true; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DValueTextItem::MenuEvent (int mkey, bool fromcontroller) -{ - if (mSelections.Size() > 1) - { - if (mkey == MKEY_Left) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - if (--mSelection < 0) mSelection = mSelections.Size() - 1; - return true; - } - else if (mkey == MKEY_Right || mkey == MKEY_Enter) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - if (++mSelection >= (int)mSelections.Size()) mSelection = 0; - return true; - } - } - return (mkey == MKEY_Enter); // needs to eat enter keys so that Activate won't get called -} - -//============================================================================= -// -// -// -//============================================================================= - -void DValueTextItem::Drawer(bool selected) -{ - const char *text = mText; - - if (*text == '$') text = GStrings(text+1); - screen->DrawText(mFont, selected? OptionSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE); - - int x = mXpos + mFont->StringWidth(text) + 8; - if (mSelections.Size() > 0) - { - const char *mOptValue = mSelections[mSelection]; - if (*mOptValue == '$') mOptValue = GStrings(mOptValue + 1); - screen->DrawText(mFont, mFontColor2, x, mYpos, mOptValue, DTA_Clean, true, TAG_DONE); - } -} - -//============================================================================= -// -// items for the player menu -// -//============================================================================= -IMPLEMENT_CLASS(DSliderItem, false, false) - -DSliderItem::DSliderItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, FName action, int min, int max, int step) -: DListMenuItemSelectable(x, y, height, action) -{ - mText = text; - mFont = font; - mFontColor = color; - mSelection = 0; - mMinrange = min; - mMaxrange = max; - mStep = step; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSliderItem::SetValue(int i, int value) -{ - if (i == 0) - { - mSelection = value; - return true; - } - return false; -} - -bool DSliderItem::GetValue(int i, int *pvalue) -{ - if (i == 0) - { - *pvalue = mSelection; - return true; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSliderItem::MenuEvent (int mkey, bool fromcontroller) -{ - if (mkey == MKEY_Left) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - if ((mSelection -= mStep) < mMinrange) mSelection = mMinrange; - return true; - } - else if (mkey == MKEY_Right || mkey == MKEY_Enter) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - if ((mSelection += mStep) > mMaxrange) mSelection = mMaxrange; - return true; - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool DSliderItem::MouseEvent(int type, int x, int y) -{ - DListMenu *lm = static_cast(DMenu::CurrentMenu); - if (type != DMenu::MOUSE_Click) - { - if (!lm->CheckFocus(this)) return false; - } - if (type == DMenu::MOUSE_Release) - { - lm->ReleaseFocus(); - } - - int slide_left = SmallFont->StringWidth ("Green") + 8 + mXpos; - int slide_right = slide_left + 12*8; // 12 char cells with 8 pixels each. - - if (type == DMenu::MOUSE_Click) - { - if (x < slide_left || x >= slide_right) return true; - } - - x = clamp(x, slide_left, slide_right); - int v = mMinrange + Scale(x - slide_left, mMaxrange - mMinrange, slide_right - slide_left); - if (v != mSelection) - { - mSelection = v; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - if (type == DMenu::MOUSE_Click) - { - lm->SetFocus(this); - } - return true; -} - -//============================================================================= -// -// -// -//============================================================================= - -void DSliderItem::DrawSlider (int x, int y) -{ - int range = mMaxrange - mMinrange; - int cur = mSelection - mMinrange; - - x = (x - 160) * CleanXfac + screen->GetWidth() / 2; - y = (y - 100) * CleanYfac + screen->GetHeight() / 2; - - screen->DrawText (ConFont, CR_WHITE, x, y, - "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, - TAG_DONE); - screen->DrawText (ConFont, CR_ORANGE, x + (5 + (int)((cur * 78) / range)) * CleanXfac, y, - "\x13", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, - TAG_DONE); -} - -//============================================================================= -// -// -// -//============================================================================= - -void DSliderItem::Drawer(bool selected) -{ - const char *text = mText; - - if (*text == '$') text = GStrings(text+1); - screen->DrawText(mFont, selected? OptionSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE); - - int x = SmallFont->StringWidth ("Green") + 8 + mXpos; - int x2 = SmallFont->StringWidth (text) + 8 + mXpos; - DrawSlider (MAX(x2, x), mYpos); -} - - //============================================================================= // // @@ -524,6 +102,14 @@ IMPLEMENT_CLASS(DPlayerMenu, false, false) // // //============================================================================= +enum EPDFlags +{ + ListMenuItemPlayerDisplay_PDF_ROTATION = 0x10001, + ListMenuItemPlayerDisplay_PDF_SKIN = 0x10002, + ListMenuItemPlayerDisplay_PDF_CLASS = 0x10003, + ListMenuItemPlayerDisplay_PDF_MODE = 0x10004, + ListMenuItemPlayerDisplay_PDF_TRANSLATE = 0x10005, +}; void DPlayerMenu::Init(DMenu *parent, DListMenuDescriptor *desc) { @@ -536,14 +122,14 @@ void DPlayerMenu::Init(DMenu *parent, DListMenuDescriptor *desc) li = GetItem(NAME_Playerdisplay); if (li != NULL) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_ROTATION, 0); - li->SetValue(DListMenuItemPlayerDisplay::PDF_MODE, 1); - li->SetValue(DListMenuItemPlayerDisplay::PDF_TRANSLATE, 1); - li->SetValue(DListMenuItemPlayerDisplay::PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum()); + li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, 0); + li->SetValue(ListMenuItemPlayerDisplay_PDF_MODE, 1); + li->SetValue(ListMenuItemPlayerDisplay_PDF_TRANSLATE, 1); + li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum()); if (PlayerClass != NULL && !(GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN) && players[consoleplayer].userinfo.GetPlayerClassNum() != -1) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_SKIN, players[consoleplayer].userinfo.GetSkin()); + li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, players[consoleplayer].userinfo.GetSkin()); } } @@ -658,7 +244,7 @@ bool DPlayerMenu::Responder (event_t *ev) DMenuItemBase *li = GetItem(NAME_Playerdisplay); if (li != NULL) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_ROTATION, mRotation); + li->SetValue(ListMenuItemPlayerDisplay_PDF_ROTATION, mRotation); } return true; } @@ -812,7 +398,7 @@ void DPlayerMenu::UpdateSkins() li = GetItem(NAME_Playerdisplay); if (li != NULL) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_SKIN, skin); + li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, skin); } } UpdateTranslation(); @@ -908,7 +494,7 @@ void DPlayerMenu::ClassChanged (DMenuItemBase *li) li = GetItem(NAME_Playerdisplay); if (li != NULL) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum()); + li->SetValue(ListMenuItemPlayerDisplay_PDF_CLASS, players[consoleplayer].userinfo.GetPlayerClassNum()); } } } @@ -939,7 +525,7 @@ void DPlayerMenu::SkinChanged (DMenuItemBase *li) li = GetItem(NAME_Playerdisplay); if (li != NULL) { - li->SetValue(DListMenuItemPlayerDisplay::PDF_SKIN, sel); + li->SetValue(ListMenuItemPlayerDisplay_PDF_SKIN, sel); } } } diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp index 6737f832e..dfb08f13c 100644 --- a/src/menu/videomenu.cpp +++ b/src/menu/videomenu.cpp @@ -54,10 +54,6 @@ #include "sbar.h" #include "hardware.h" -#define NO_IMP -#include "optionmenuitems.h" - - /*======================================= * * Video Modes Menu @@ -101,19 +97,21 @@ CUSTOM_CVAR (Int, menu_screenratios, -1, CVAR_ARCHIVE) CUSTOM_CVAR (Bool, vid_tft, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { + const int OptionMenuItemOptionBase_OP_VALUES = 0x11001; + DOptionMenuDescriptor *opt = GetVideoModeMenu(); if (opt != NULL) { - DOptionMenuItem *it = opt->GetItem("menu_screenratios"); + DMenuItemBase *it = opt->GetItem("menu_screenratios"); if (it != NULL) { if (self) { - it->SetString(DOptionMenuItemOptionBase::OP_VALUES, "RatiosTFT"); + it->SetString(OptionMenuItemOptionBase_OP_VALUES, "RatiosTFT"); } else { - it->SetString(DOptionMenuItemOptionBase::OP_VALUES, "Ratios"); + it->SetString(OptionMenuItemOptionBase_OP_VALUES, "Ratios"); } } } @@ -131,61 +129,16 @@ CUSTOM_CVAR (Bool, vid_tft, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //============================================================================= -class DVideoModeMenu : public DOptionMenu +struct OptionMenuItemScreenResolution // temporary workaround { - DECLARE_CLASS(DVideoModeMenu, DOptionMenu) - -public: - - DVideoModeMenu() + enum EValues { - SetModesMenu (screen->VideoWidth, screen->VideoHeight, DisplayBits); - } - - bool MenuEvent(int mkey, bool fromcontroller) - { - if ((mkey == MKEY_Up || mkey == MKEY_Down) && mDesc->mSelectedItem >= 0 && - mDesc->mSelectedItem < (int)mDesc->mItems.Size()) - { - int sel; - bool selected = mDesc->mItems[mDesc->mSelectedItem]->GetValue(DOptionMenuScreenResolutionLine::SRL_SELECTION, &sel); - bool res = Super::MenuEvent(mkey, fromcontroller); - if (selected) mDesc->mItems[mDesc->mSelectedItem]->SetValue(DOptionMenuScreenResolutionLine::SRL_SELECTION, sel); - return res; - } - return Super::MenuEvent(mkey, fromcontroller); - } - - bool Responder(event_t *ev) - { - if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown && - (ev->data1 == 't' || ev->data1 == 'T')) - { - if (!GetSelectedSize (&NewWidth, &NewHeight)) - { - NewWidth = screen->VideoWidth; - NewHeight = screen->VideoHeight; - } - else - { - OldWidth = screen->VideoWidth; - OldHeight = screen->VideoHeight; - OldBits = DisplayBits; - NewBits = BitTranslate[DummyDepthCvar]; - setmodeneeded = true; - testingmode = I_GetTime(false) + 5 * TICRATE; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - SetModesMenu (NewWidth, NewHeight, NewBits); - return true; - } - } - return Super::Responder(ev); - } + SRL_INDEX = 0x30000, + SRL_SELECTION = 0x30003, + SRL_HIGHLIGHT = 0x30004, + }; }; -IMPLEMENT_CLASS(DVideoModeMenu, false, false) - - //============================================================================= // // @@ -236,10 +189,10 @@ static void BuildModesList (int hiwidth, int hiheight, int hi_bits) { for (i = NAME_res_0; i<= NAME_res_9; i++) { - DOptionMenuItem *it = opt->GetItem((ENamedName)i); + DMenuItemBase *it = opt->GetItem((ENamedName)i); if (it != NULL) { - it->SetValue(DOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, -1); + it->SetValue(OptionMenuItemScreenResolution::SRL_HIGHLIGHT, -1); for (c = 0; c < 3; c++) { bool haveMode = false; @@ -260,16 +213,16 @@ static void BuildModesList (int hiwidth, int hiheight, int hi_bits) { if (width == hiwidth && height == hiheight) { - it->SetValue(DOptionMenuScreenResolutionLine::SRL_SELECTION, c); - it->SetValue(DOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, c); + it->SetValue(OptionMenuItemScreenResolution::SRL_SELECTION, c); + it->SetValue(OptionMenuItemScreenResolution::SRL_HIGHLIGHT, c); } mysnprintf (strtemp, countof(strtemp), "%dx%d%s", width, height, letterbox?TEXTCOLOR_BROWN" LB":""); - it->SetString(DOptionMenuScreenResolutionLine::SRL_INDEX+c, strtemp); + it->SetString(OptionMenuItemScreenResolution::SRL_INDEX+c, strtemp); } else { - it->SetString(DOptionMenuScreenResolutionLine::SRL_INDEX+c, ""); + it->SetString(OptionMenuItemScreenResolution::SRL_INDEX+c, ""); } } } @@ -359,12 +312,12 @@ static bool GetSelectedSize (int *width, int *height) { int line = opt->mSelectedItem; int hsel; - DOptionMenuItem *it = opt->mItems[line]; - if (it->GetValue(DOptionMenuScreenResolutionLine::SRL_SELECTION, &hsel)) + DMenuItemBase *it = opt->mItems[line]; + if (it->GetValue(OptionMenuItemScreenResolution::SRL_SELECTION, &hsel)) { char buffer[32]; char *breakpt; - if (it->GetString(DOptionMenuScreenResolutionLine::SRL_INDEX+hsel, buffer, sizeof(buffer))) + if (it->GetString(OptionMenuItemScreenResolution::SRL_INDEX+hsel, buffer, sizeof(buffer))) { *width = (int)strtoll (buffer, &breakpt, 10); *height = (int)strtoll (breakpt+1, NULL, 10); @@ -375,6 +328,27 @@ static bool GetSelectedSize (int *width, int *height) return false; } +DEFINE_ACTION_FUNCTION(DVideoModeMenu, SetSelectedSize) +{ + if (!GetSelectedSize (&NewWidth, &NewHeight)) + { + NewWidth = screen->VideoWidth; + NewHeight = screen->VideoHeight; + ACTION_RETURN_BOOL(false); + } + else + { + OldWidth = screen->VideoWidth; + OldHeight = screen->VideoHeight; + OldBits = DisplayBits; + NewBits = BitTranslate[DummyDepthCvar]; + setmodeneeded = true; + testingmode = I_GetTime(false) + 5 * TICRATE; + SetModesMenu (NewWidth, NewHeight, NewBits); + ACTION_RETURN_BOOL(true); + } +} + //============================================================================= // // @@ -397,6 +371,11 @@ void M_SetVideoMode() SetModesMenu (NewWidth, NewHeight, NewBits); } +DEFINE_ACTION_FUNCTION(DMenu, SetVideoMode) +{ + M_SetVideoMode(); + return 0; +} //============================================================================= // // @@ -423,7 +402,7 @@ static void SetModesMenu (int w, int h, int bits) DOptionMenuDescriptor *opt = GetVideoModeMenu(); if (opt != NULL) { - DOptionMenuItem *it; + DMenuItemBase *it; if (testingmode <= 1) { it = opt->GetItem(NAME_VMEnterText); @@ -448,3 +427,8 @@ static void SetModesMenu (int w, int h, int bits) } BuildModesList (w, h, bits); } + +void M_InitVideoModes() +{ + SetModesMenu (screen->VideoWidth, screen->VideoHeight, DisplayBits); +} \ No newline at end of file diff --git a/src/p_states.cpp b/src/p_states.cpp index 221ff6ced..3602cfc81 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -51,6 +51,40 @@ FStateLabelStorage StateLabels; // actor. States are archived by recording the actor they belong // to and the index into that actor's list of states. + +//========================================================================== +// +// This wraps everything needed to get a current sprite from a state into +// one single script function. +// +//========================================================================== + +DEFINE_ACTION_FUNCTION(FState, GetSpriteTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(FState); + PARAM_INT(rotation); + PARAM_INT_DEF(skin); + PARAM_FLOAT_DEF(scalex); + PARAM_FLOAT_DEF(scaley); + + spriteframe_t *sprframe; + if (skin == 0) + { + sprframe = &SpriteFrames[sprites[self->sprite].spriteframes + self->GetFrame()]; + } + else + { + sprframe = &SpriteFrames[sprites[skins[skin].sprite].spriteframes + self->GetFrame()]; + scalex = skins[skin].Scale.X; + scaley = skins[skin].Scale.Y; + } + if (numret > 0) ret[0].SetInt(sprframe->Texture[rotation].GetIndex()); + if (numret > 1) ret[1].SetInt(!!(sprframe->Flip & (1 << rotation))); + if (numret > 2) ret[2].SetVector2(DVector2(scalex, scaley)); + return MIN(3, numret); +} + + //========================================================================== // // Find the actor that a state belongs to. diff --git a/src/p_user.cpp b/src/p_user.cpp index dd7f181a8..d5e8afdaf 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -61,6 +61,7 @@ #include "p_spec.h" #include "virtual.h" #include "g_levellocals.h" +#include "r_data/r_translate.h" static FRandom pr_skullpop ("SkullPop"); @@ -143,6 +144,13 @@ bool FPlayerClass::CheckSkin (int skin) return false; } +DEFINE_ACTION_FUNCTION(FPlayerClass, CheckSkin) +{ + PARAM_SELF_STRUCT_PROLOGUE(FPlayerClass); + PARAM_INT(skin); + ACTION_RETURN_BOOL(self->CheckSkin(skin)); +} + //=========================================================================== // // GetDisplayName @@ -3342,3 +3350,7 @@ DEFINE_FIELD_X(PlayerInfo, player_t, cmd) DEFINE_FIELD_X(PlayerInfo, player_t, original_cmd) DEFINE_FIELD_X(PlayerInfo, player_t, userinfo) DEFINE_FIELD_X(PlayerInfo, player_t, weapons) + +DEFINE_FIELD(FPlayerClass, Type) +DEFINE_FIELD(FPlayerClass, Flags) +DEFINE_FIELD(FPlayerClass, Skins) diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 64f490b30..0f4d58e6d 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -740,8 +740,6 @@ void R_InitTranslationTables () } // The menu player also gets a separate translation table PushIdentityTable(TRANSLATION_Players); - // This one is for the backdrop in the menu - PushIdentityTable(TRANSLATION_Players); // The three standard translations from Doom or Heretic (seven for Strife), // plus the generic ice translation. @@ -1200,6 +1198,33 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer R_CreatePlayerTranslation (h, s, v, colorset, skin, table, NULL, NULL); } + +DEFINE_ACTION_FUNCTION(_Translation, SetPlayerTranslation) +{ + PARAM_PROLOGUE; + PARAM_UINT(tgroup); + PARAM_UINT(tnum); + PARAM_UINT(pnum); + PARAM_POINTER(cls, FPlayerClass); + + if (pnum >= MAXPLAYERS || tgroup >= NUM_TRANSLATION_TABLES || tnum >= translationtables[tgroup].Size()) + { + ACTION_RETURN_BOOL(false); + } + auto self = &players[pnum]; + int PlayerColor = self->userinfo.GetColor(); + int PlayerSkin = self->userinfo.GetSkin(); + int PlayerColorset = self->userinfo.GetColorSet(); + + if (cls != nullptr) + { + PlayerSkin = R_FindSkin(skins[PlayerSkin].name, int(cls - &PlayerClasses[0])); + R_GetPlayerTranslation(PlayerColor, GetColorSet(cls->Type, PlayerColorset), + &skins[PlayerSkin], translationtables[tgroup][tnum]); + } + ACTION_RETURN_BOOL(true); +} + //---------------------------------------------------------------------------- // // @@ -1307,3 +1332,29 @@ void R_ParseTrnslate() } } } + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +struct FTranslation +{ + PalEntry colors[256]; +}; + +DEFINE_ACTION_FUNCTION(_Translation, AddTranslation) +{ + PARAM_SELF_STRUCT_PROLOGUE(FTranslation); + + FRemapTable NewTranslation; + memcpy(&NewTranslation.Palette[0], self->colors, 256 * sizeof(PalEntry)); + for (int i = 0; i < 256; i++) + { + NewTranslation.Remap[i] = ColorMatcher.Pick(self->colors[i]); + } + int trans = NewTranslation.StoreTranslation(TRANSLATION_Custom); + ACTION_RETURN_INT(trans); +} + diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 87374bf78..b2ccdeb33 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -697,6 +697,7 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, else { listenpos.Zero(); + pos->Zero(); pgroup = 0; } @@ -716,7 +717,6 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, { case SOURCE_None: default: - pos->Zero(); break; case SOURCE_Actor: diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index ae35935f5..d4a1254d6 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -142,8 +142,6 @@ std2: 'true' { RET(TK_True); } 'false' { RET(TK_False); } 'none' { RET(TK_None); } - 'new' { RET(TK_New); } - 'instanceof' { RET(TK_InstanceOf); } 'auto' { RET(TK_Auto); } 'exec' { RET(TK_Exec); } 'property' { RET(TK_Property); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 9f88e2242..0227dffa9 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -81,7 +81,6 @@ xx(TK_ForEach, "'foreach'") xx(TK_True, "'true'") xx(TK_False, "'false'") xx(TK_None, "'none'") -xx(TK_New, "'new'") xx(TK_InstanceOf, "'instanceof'") xx(TK_Auto, "'auto'") xx(TK_Exec, "'exec'") diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 1307693cf..d991d1459 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -154,6 +154,7 @@ void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos) { if (ReturnProto->ReturnTypes[i] != proto->ReturnTypes[i]) { // Incompatible + Printf("Return type %s mismatch with %s\n", ReturnProto->ReturnTypes[i]->DescriptiveName(), proto->ReturnTypes[i]->DescriptiveName()); fail = true; break; } @@ -2455,7 +2456,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) ExpEmit FxAssign::Emit(VMFunctionBuilder *build) { static const BYTE loadops[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP }; - assert(ValueType->GetRegType() == Right->ValueType->GetRegType()); + assert(Base->ValueType->GetRegType() == Right->ValueType->GetRegType()); ExpEmit pointer = Base->Emit(build); Address = pointer; @@ -2973,6 +2974,12 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) delete this; return nullptr; } + if (!left->ValueType || !right->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "ValueType not set"); + delete this; + return nullptr; + } if (left->IsVector() || right->IsVector()) { @@ -3551,6 +3558,16 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) goto error; } } + else if (left->IsPointer() && static_cast(left->ValueType)->PointedType == right->ValueType) + { + bool writable; + if (!right->RequestAddress(ctx, &writable)) goto error; + } + else if (right->IsPointer() && static_cast(right->ValueType)->PointedType == left->ValueType) + { + bool writable; + if (!left->RequestAddress(ctx, &writable)) goto error; + } else { goto error; @@ -5052,7 +5069,6 @@ FxExpression *FxNew::Resolve(FCompileContext &ctx) ExpEmit FxNew::Emit(VMFunctionBuilder *build) { - assert(ValueType == val->ValueType); ExpEmit from = val->Emit(build); from.Free(build); ExpEmit to(build, REGT_POINTER); @@ -10150,6 +10166,8 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx) ScriptPosition.Message(MSG_OPTERROR, "Unknown class name '%s' of type '%s'", clsname.GetChars(), desttype->TypeName.GetChars()); + delete this; + return nullptr; } else { diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 8cd10b75d..f257c7c2d 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -55,6 +55,7 @@ #include "r_sky.h" #include "v_font.h" #include "v_video.h" +#include "c_bind.h" #include "menu/menu.h" static TArray properties; @@ -754,6 +755,10 @@ void InitThingdef() sectorportalstruct->Size = sizeof(FSectorPortal); sectorportalstruct->Align = alignof(FSectorPortal); + auto playerclassstruct = NewNativeStruct("PlayerClass", nullptr); + playerclassstruct->Size = sizeof(FPlayerClass); + playerclassstruct->Align = alignof(FPlayerClass); + // set up the lines array in the sector struct. This is a bit messy because the type system is not prepared to handle a pointer to an array of pointers to a native struct even remotely well... // As a result, the size has to be set to something large and arbritrary because it can change between maps. This will need some serious improvement when things get cleaned up. sectorstruct->AddNativeField("lines", NewPointer(NewResizableArray(NewPointer(linestruct, false)), false), myoffsetof(sector_t, Lines), VARF_Native); @@ -789,6 +794,16 @@ void InitThingdef() PField *aacf = new PField("AllActorClasses", aact, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PClassActor::AllActorClasses); Namespaces.GlobalNamespace->Symbols.AddSymbol(aacf); + auto plrcls = NewPointer(NewResizableArray(playerclassstruct), false); + PField *plrclsf = new PField("PlayerClasses", plrcls, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&PlayerClasses); + Namespaces.GlobalNamespace->Symbols.AddSymbol(plrclsf); + + auto bindcls = NewNativeStruct("KeyBindings", nullptr); + PField *binding = new PField("Bindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&Bindings); + Namespaces.GlobalNamespace->Symbols.AddSymbol(binding); + binding = new PField("AutomapBindings", bindcls, VARF_Native | VARF_Static, (intptr_t)&AutomapBindings); + Namespaces.GlobalNamespace->Symbols.AddSymbol(binding); + // set up a variable for the DEH data PStruct *dstruct = NewNativeStruct("DehInfo", nullptr); PField *dehf = new PField("deh", dstruct, VARF_Native | VARF_Static, (intptr_t)&deh); @@ -867,6 +882,9 @@ void InitThingdef() fieldptr = new PField("CleanHeight_1", TypeSInt32, VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&CleanHeight_1); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + fieldptr = new PField("menuactive", TypeSInt32, VARF_Native | VARF_Static, (intptr_t)&menuactive); + Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); + fieldptr = new PField("OptionMenuSettings", NewStruct("FOptionMenuSettings", nullptr), VARF_Native | VARF_Static | VARF_ReadOnly, (intptr_t)&OptionSettings); Namespaces.GlobalNamespace->Symbols.AddSymbol(fieldptr); @@ -994,7 +1012,7 @@ DEFINE_ACTION_FUNCTION(FStringStruct, Replace) return 0; } -static FString FStringFormat(VM_ARGS) +FString FStringFormat(VM_ARGS) { assert(param[0].Type == REGT_STRING); FString fmtstring = param[0].s().GetChars(); diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index 36619ef5b..aa191c34e 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -1213,6 +1213,6 @@ class PFunction; VMFunction *FindVMFunction(PClass *cls, const char *name); #define DECLARE_VMFUNC(cls, name) static VMFunction *name; if (name == nullptr) name = FindVMFunction(RUNTIME_CLASS(cls), #name); - +FString FStringFormat(VM_ARGS); #endif diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index b9f8e81ca..8da2c4d8d 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -785,8 +785,11 @@ begin: OP(NEW_K): OP(NEW): { - PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[C] : konsta[C].v); - reg.a[B] = cls->CreateNew(); + b = B; + PClass *cls = (PClass*)(pc->op == OP_NEW ? reg.a[b] : konsta[b].v); + if (cls->ObjectFlags & OF_Abstract) ThrowAbortException(X_OTHER, "Cannot instantiate abstract class %s", cls->TypeName.GetChars()); + reg.a[a] = cls->CreateNew(); + reg.atag[a] = ATAG_OBJECT; NEXTOP; } diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index 7128f1e43..6f3931531 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -70,8 +70,8 @@ xx(SS, ss, RPRSKI, SS_R, 4, REGT_INT), // store string xx(SS_R, ss, RPRSRI, NOP, 0, 0), xx(SP, sp, RPRPKI, SP_R, 4, REGT_INT), // store pointer xx(SP_R, sp, RPRPRI, NOP, 0, 0), -xx(SO, sp, RPRPKI, SO_R, 4, REGT_INT), // store object pointer with write barrier (only needed for non thinkers and non types -xx(SO_R, sp, RPRPRI, NOP, 0, 0), +xx(SO, so, RPRPKI, SO_R, 4, REGT_INT), // store object pointer with write barrier (only needed for non thinkers and non types) +xx(SO_R, so, RPRPRI, NOP, 0, 0), xx(SV2, sv2, RPRVKI, SV2_R, 4, REGT_INT), // store vector2 xx(SV2_R, sv2, RPRVRI, NOP, 0, 0), xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT), // store vector3 diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index c4c8a12a5..81c7ff6d3 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -112,7 +112,7 @@ public: } else { // Move hanging ( characters to the new line - Str.Truncate(long(Str.Len() - ConsecOpens)); + Str.Truncate(Str.Len() - ConsecOpens); NestDepth -= ConsecOpens; } Str << '\n'; diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index bae828493..6d3e8a856 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -12,6 +12,10 @@ static void SetNodeLine(ZCC_TreeNode *name, ZCCToken &tok) static void SetNodeLine(ZCC_TreeNode *name, ZCC_TreeNode *node) { + if (name == nullptr || node == nullptr) + { + I_Error("Fatal parse error"); + } name->SourceLoc = node->SourceLoc; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index b4a97c3cf..65d08fb92 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -589,6 +589,7 @@ void ZCCCompiler::CreateClassTypes() { Error(c->cls, "Class name %s already exists", c->NodeName().GetChars()); } + else DPrintf(DMSG_SPAMMY, "Created class %s with parent %s\n", c->Type()->TypeName.GetChars(), c->Type()->ParentClass->TypeName.GetChars()); } catch (CRecoverableError &err) { @@ -597,6 +598,10 @@ void ZCCCompiler::CreateClassTypes() c->cls->Type = nullptr; } } + if (c->cls->Flags & ZCC_Abstract) + { + c->Type()->ObjectFlags |= OF_Abstract; + } if (c->Type() == nullptr) c->cls->Type = parent->FindClassTentative(c->NodeName()); c->Type()->bExported = true; // this class is accessible to script side type casts. (The reason for this flag is that types like PInt need to be skipped.) c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type()); @@ -2593,7 +2598,7 @@ void ZCCCompiler::CompileStates() statename << FName(part->Id) << '.'; part = static_cast(part->SiblingNext); } while (part != sg->Label); - statename.Truncate((long)statename.Len() - 1); // remove the last '.' in the label name + statename.Truncate(statename.Len() - 1); // remove the last '.' in the label name if (sg->Offset != nullptr) { int offset = IntConstFromNode(sg->Offset, c->Type()); diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 114919df9..bbbb016ea 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1189,6 +1189,40 @@ int FTextureManager::CountLumpTextures (int lumpnum) return 0; } +//========================================================================== +// +// +// +//========================================================================== + +DEFINE_ACTION_FUNCTION(_TexMan, GetSize) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + auto tex = TexMan[FSetTextureID(texid)]; + int x, y; + if (tex != nullptr) + { + x = tex->GetWidth(); + y = tex->GetHeight(); + } + else x = y = -1; + if (numret > 0) ret[0].SetInt(x); + if (numret > 1) ret[1].SetInt(x); + return MIN(numret, 2); +} + +DEFINE_ACTION_FUNCTION(_TexMan, GetScaledSize) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + auto tex = TexMan[FSetTextureID(texid)]; + if (tex != nullptr) + { + ACTION_RETURN_VEC2(DVector2(tex->GetScaledWidthDouble(), tex->GetScaledHeightDouble())); + } + ACTION_RETURN_VEC2(DVector2(-1, -1)); +} //========================================================================== // diff --git a/src/v_draw.cpp b/src/v_draw.cpp index bfc8f5a2b..d301c9d33 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -1711,6 +1711,17 @@ void V_DrawFrame (int left, int top, int width, int height) screen->DrawTexture (TexMan[border->br], left+width, top+height, TAG_DONE); } +DEFINE_ACTION_FUNCTION(_Screen, DrawFrame) +{ + PARAM_PROLOGUE; + PARAM_INT(x); + PARAM_INT(y); + PARAM_INT(w); + PARAM_INT(h); + V_DrawFrame(x, y, w, h); + return 0; +} + //========================================================================== // // V_DrawBorder diff --git a/src/v_font.cpp b/src/v_font.cpp index 669054001..a82bd16c1 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -2739,3 +2739,8 @@ void V_ClearFonts() SmallFont = SmallFont2 = BigFont = ConFont = IntermissionFont = NULL; } +DEFINE_ACTION_FUNCTION(FFont, GetCursor) +{ + PARAM_SELF_STRUCT_PROLOGUE(FFont); + ACTION_RETURN_STRING(FString(self->GetCursor())); +} \ No newline at end of file diff --git a/src/v_text.cpp b/src/v_text.cpp index 85b3de7db..a80a566ab 100644 --- a/src/v_text.cpp +++ b/src/v_text.cpp @@ -141,6 +141,8 @@ void DCanvas::DrawTextCommon(FFont *font, int normalcolor, double x, double y, c int kerning; FTexture *pic; + assert(string[0] != '$'); + if (parms.celly == 0) parms.celly = font->GetHeight() + 1; parms.celly *= parms.scaley; @@ -240,7 +242,7 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawText) PARAM_STRING(chr); VMVa_List args = { param + 5, 0, numparam - 5 }; - const char *txt = chr[0] == '$' ? GStrings(chr) : chr.GetChars(); + const char *txt = chr[0] == '$' ? GStrings(&chr[1]) : chr.GetChars(); screen->DrawText(font, cr, x, y, txt, args); return 0; } diff --git a/src/virtual.h b/src/virtual.h index f0479e663..a25e543ac 100644 --- a/src/virtual.h +++ b/src/virtual.h @@ -22,7 +22,7 @@ inline unsigned GetVirtualIndex(PClass *cls, const char *funcname) #define IFVIRTUALPTRNAME(self, cls, funcname) \ static unsigned VIndex = ~0u; \ if (VIndex == ~0u) { \ - VIndex = GetVirtualIndex(PClass::FindActor(cls), #funcname); \ + VIndex = GetVirtualIndex(PClass::FindClass(cls), #funcname); \ assert(VIndex != ~0u); \ } \ auto clss = self->GetClass(); \ diff --git a/src/zstring.cpp b/src/zstring.cpp index 665b9312e..83a2a20d8 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -368,15 +368,15 @@ FString &FString::CopyCStrPart(const char *tail, size_t tailLen) return *this; } -void FString::Truncate(long newlen) +void FString::Truncate(size_t newlen) { - if (newlen <= 0) + if (newlen == 0) { Data()->Release(); NullString.RefCount++; Chars = &NullString.Nothing[0]; } - else if (newlen < (long)Len()) + else if (newlen < Len()) { ReallocBuffer (newlen); Chars[newlen] = '\0'; diff --git a/src/zstring.h b/src/zstring.h index c58fb111a..b925a39b1 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -301,7 +301,7 @@ public: bool IsEmpty() const { return Len() == 0; } bool IsNotEmpty() const { return Len() != 0; } - void Truncate (long newlen); + void Truncate (size_t newlen); void Remove(size_t index, size_t remlen); int Compare (const FString &other) const { return strcmp (Chars, other.Chars); } diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index ea6d581e1..f0f462880 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -6,9 +6,17 @@ #include "zscript/actor.txt" #include "zscript/actor_checks.txt" +#include "zscript/menu/menuitembase.txt" #include "zscript/menu/menu.txt" -//#include "zscript/menu/menuitembase.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/playerdisplay.txt" +#include "zscript/menu/playermenu.txt" +#include "zscript/menu/textentermenu.txt" +#include "zscript/menu/videomenu.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 8e77c3212..eee20a04f 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -1,3 +1,55 @@ +struct InputEvent native +{ + native uint8 type; + native uint8 subtype; + native int16 data1; // keys / mouse/joystick buttons + native int16 data2; + native int16 data3; + native int x; // mouse/joystick x move + native int y; // mouse/joystick y move + + enum EGenericEvent + { + NoEvent, + KeyDown, // data1: scan code, data2: Qwerty ASCII code + KeyUp, // same + Mouse, // x, y: mouse movement deltas + GUI_Event, // subtype specifies actual event + DeviceChange,// a device has been connected or removed + } + + enum EGUIEvent + { + GUI_None, + GUI_KeyDown, // data1: unshifted ASCII, data2: shifted ASCII, data3: modifiers + GUI_KeyRepeat, // same + GUI_KeyUp, // same + GUI_Char, // data1: translated character (for user text input), data2: alt down? + GUI_FirstMouseEvent, + GUI_MouseMove, + GUI_LButtonDown, + GUI_LButtonUp, + GUI_LButtonDblClick, + GUI_MButtonDown, + GUI_MButtonUp, + GUI_MButtonDblClick, + GUI_RButtonDown, + GUI_RButtonUp, + GUI_RButtonDblClick, + GUI_WheelUp, // data3: shift/ctrl/alt + GUI_WheelDown, // " + GUI_WheelRight, // " + GUI_WheelLeft, // " + GUI_BackButtonDown, + GUI_BackButtonUp, + GUI_FwdButtonDown, + GUI_FwdButtonUp, + GUI_LastMouseEvent, + }; + + const KEY_ESCAPE = 0x01; +} + struct TexMan { enum EUseTypes @@ -42,6 +94,8 @@ struct TexMan native static TextureID CheckForTexture(String name, int usetype, int flags = TryAny); native static void ReplaceTextures(String from, String to, int flags); + native static int, int GetSize(TextureID tex); + native static Vector2 GetScaledSize(TextureID tex); } enum DrawTextureTags @@ -118,7 +172,7 @@ struct Screen native native static vararg void DrawTexture(TextureID tex, bool animate, double x, double y, ...); native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); - + native static void DrawFrame(int x, int y, int w, int h); } class BrokenLines : Object native @@ -158,10 +212,42 @@ struct Font native CR_CYAN, NUM_TEXT_COLORS }; + + const TEXTCOLOR_BRICK = "\034A"; + const TEXTCOLOR_TAN = "\034B"; + const TEXTCOLOR_GRAY = "\034C"; + const TEXTCOLOR_GREY = "\034C"; + const TEXTCOLOR_GREEN = "\034D"; + const TEXTCOLOR_BROWN = "\034E"; + const TEXTCOLOR_GOLD = "\034F"; + const TEXTCOLOR_RED = "\034G"; + const TEXTCOLOR_BLUE = "\034H"; + const TEXTCOLOR_ORANGE = "\034I"; + const TEXTCOLOR_WHITE = "\034J"; + const TEXTCOLOR_YELLOW = "\034K"; + const TEXTCOLOR_UNTRANSLATED = "\034L"; + const TEXTCOLOR_BLACK = "\034M"; + const TEXTCOLOR_LIGHTBLUE = "\034N"; + const TEXTCOLOR_CREAM = "\034O"; + const TEXTCOLOR_OLIVE = "\034P"; + const TEXTCOLOR_DARKGREEN = "\034Q"; + const TEXTCOLOR_DARKRED = "\034R"; + const TEXTCOLOR_DARKBROWN = "\034S"; + const TEXTCOLOR_PURPLE = "\034T"; + const TEXTCOLOR_DARKGRAY = "\034U"; + const TEXTCOLOR_CYAN = "\034V"; + + const TEXTCOLOR_NORMAL = "\034-"; + const TEXTCOLOR_BOLD = "\034+"; + + const TEXTCOLOR_CHAT = "\034*"; + const TEXTCOLOR_TEAMCHAT = "\034!"; + native int GetCharWidth(int code); native int StringWidth(String code); native int GetHeight(); + native String GetCursor(); native static int FindFontColor(Name color); native static Font FindFont(Name fontname); @@ -169,10 +255,24 @@ struct Font native native static BrokenLines BreakLines(String text, int maxlen); } +struct Translation +{ + Color colors[256]; + + native int AddTranslation(); + native static bool SetPlayerTranslation(int group, int num, int plrnum, PlayerClass pclass); + static int MakeID(int group, int num) + { + return (group << 16) + num; + } +} + struct Console native { native static void HideConsole(); native static void MidPrint(Font fontname, string textlabel, bool bold = false); + native static vararg void Printf(string fmt, ...); + native static void DoCommand(String cmd); } struct DamageTypeDefinition native @@ -182,9 +282,24 @@ struct DamageTypeDefinition native struct CVar native { + enum ECVarType + { + CVAR_Bool, + CVAR_Int, + CVAR_Float, + CVAR_String, + CVAR_Color, + }; + + native static CVar FindCVar(Name name); native int GetInt(); native double GetFloat(); native String GetString(); + native void SetInt(int v); + native void SetFloat(double v); + native void SetString(String s); + native int GetRealType(); + native int ResetToDefault(); } struct GameInfoStruct native @@ -347,7 +462,7 @@ struct LevelLocals native struct StringTable native { - native static String Localize(String val, bool prefixed = false); + native static String Localize(String val, bool prefixed = true); } // a few values of this need to be readable by the play code. @@ -382,6 +497,7 @@ struct State native native int DistanceTo(state other); native bool ValidateSpriteFrame(); + native TextureID, bool, Vector2 GetSpriteTexture(int rotation, int skin = 0, Vector2 scale = (0,0)); } struct F3DFloor native @@ -476,7 +592,7 @@ class Floor : Thinker native floorRaiseInstant, floorMoveToValue, floorRaiseToLowestCeiling, - floorRaiseByTexture, + floorRaiseuint8xture, floorLowerAndChange, floorRaiseAndChange, @@ -484,7 +600,7 @@ class Floor : Thinker native floorRaiseToLowest, floorRaiseToCeiling, floorLowerToLowestCeiling, - floorLowerByTexture, + floorLoweruint8xture, floorLowerToCeiling, donutRaise, @@ -493,7 +609,7 @@ class Floor : Thinker native waitStair, resetStair, - // Not to be used as parameters to EV_DoFloor() + // Not to be used as parameters to DoFloor() genFloorChg0, genFloorChgT, genFloorChg @@ -527,8 +643,8 @@ class Ceiling : Thinker native ceilLowerToNearest, ceilRaiseToHighestFloor, ceilRaiseToFloor, - ceilRaiseByTexture, - ceilLowerByTexture, + ceilRaiseuint8xture, + ceilLoweruint8xture, genCeilingChg0, genCeilingChgT, diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index f31493077..ef2c4521f 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -1,6 +1,7 @@ // for flag changer functions. const FLAG_NO_CHANGE = -1; const MAXPLAYERS = 8; +const MAXPLAYERNAME = 15; enum EStateUseFlags { diff --git a/wadsrc/static/zscript/menu/colorpickermenu.txt b/wadsrc/static/zscript/menu/colorpickermenu.txt index 30c2a183e..93a2399a5 100644 --- a/wadsrc/static/zscript/menu/colorpickermenu.txt +++ b/wadsrc/static/zscript/menu/colorpickermenu.txt @@ -32,18 +32,93 @@ ** */ -class ColorpickerMenu : Menu native + +//============================================================================= +// +// This is only used by the color picker +// +//============================================================================= + +class OptionMenuSliderVar : OptionMenuSliderBase { - native float mRed; - native float mGreen; - native float mBlue; + int mIndex; - native int mGridPosX; - native int mGridPosY; + OptionMenuSliderVar Init(String label, int index, double min, double max, double step, int showval) + { + Super.Init(label, min, max, step, showval); + mIndex = index; + return self; + } - native int mStartItem; + override double GetSliderValue() + { + return ColorpickerMenu(Menu.GetCurrentMenu()).GetColor(mIndex); + } - native CVar mCVar; + override void SetSliderValue(double val) + { + ColorpickerMenu(Menu.GetCurrentMenu()).setColor(mIndex, val); + } +} + +class ColorpickerMenu : OptionMenu +{ + float mRed; + float mGreen; + float mBlue; + + int mGridPosX; + int mGridPosY; + + int mStartItem; + + CVar mCVar; + + double GetColor(int index) + { + double v = index == 0? mRed : index == 1? mGreen : mBlue; + return v; + } + + void SetColor(int index, double val) + { + if (index == 0) mRed = val; + else if (index == 1) mGreen = val; + else mBlue = val; + } + + //============================================================================= + // + // + // + //============================================================================= + + void Init(Menu parent, String name, OptionMenuDescriptor desc, CVar cv) + { + Super.Init(parent, desc); + + mStartItem = mDesc.mItems.Size(); + mCVar = cv; + + ResetColor(); + mGridPosX = 0; + mGridPosY = 0; + + // This menu uses some features that are hard to implement in an external control lump + // so it creates its own list of menu items. + mDesc.mItems.Resize(mStartItem+8); + mDesc.mItems[mStartItem+0] = new ("OptionMenuItemStaticText").Init(name, false); + mDesc.mItems[mStartItem+1] = new ("OptionMenuItemStaticText").Init(" ", false); + mDesc.mItems[mStartItem+2] = new ("OptionMenuSliderVar").Init("Red", 0, 0, 255, 15, 0); + mDesc.mItems[mStartItem+3] = new ("OptionMenuSliderVar").Init("Green", 1, 0, 255, 15, 0); + mDesc.mItems[mStartItem+4] = new ("OptionMenuSliderVar").Init("Blue", 2, 0, 255, 15, 0); + mDesc.mItems[mStartItem+5] = new ("OptionMenuItemStaticText").Init(" ", false); + mDesc.mItems[mStartItem+6] = new ("OptionMenuItemCommand").Init("Undo changes", "undocolorpic"); + mDesc.mItems[mStartItem+7] = new ("OptionMenuItemStaticText").Init(" ", false); + mDesc.mSelectedItem = mStartItem + 2; + mDesc.mIndent = 0; + mDesc.CalcIndent(); + } //============================================================================= // @@ -256,5 +331,24 @@ class ColorpickerMenu : Menu native screen.DrawText (SmallFont, Font.CR_WHITE, x+(48+24-SmallFont.StringWidth("New")/2)*CleanXfac_1, y, "New", DTA_CleanNoMove_1, true); } - + override void OnDestroy() + { + if (mStartItem >= 0) + { + mDesc.mItems.Resize(mStartItem); + if (mCVar != null) mCVar.SetInt(Color(int(mRed), int(mGreen), int(mBlue))); + mStartItem = -1; + } + } + + override void ResetColor() + { + if (mCVar != null) + { + Color clr = Color(mCVar.GetInt()); + mRed = clr.r; + mGreen = clr.g; + mBlue = clr.b; + } + } } \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/joystickmenu.txt b/wadsrc/static/zscript/menu/joystickmenu.txt new file mode 100644 index 000000000..2e37aadf6 --- /dev/null +++ b/wadsrc/static/zscript/menu/joystickmenu.txt @@ -0,0 +1,225 @@ +/* +** joystickmenu.cpp +** The joystick configuration menus +** +**--------------------------------------------------------------------------- +** 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 OptionMenuSliderJoySensitivity : OptionMenuSliderBase +{ + void Init(String label, double min, double max, double step, int showval) + { + Super.Init(label, min, max, step, showval); + } + + override double GetSliderValue() + { + return Menu.GetCurrentJoystickConfig().GetSensitivity(); + } + + override void SetSliderValue(double val) + { + Menu.GetCurrentJoystickConfig().SetSensitivity(val); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuSliderJoyScale : OptionMenuSliderBase +{ + int mAxis; + int mNeg; + + void Init(String label, int axis, double min, double max, double step, int showval) + { + Super.Init(label, min, max, step, showval); + mAxis = axis; + mNeg = 1; + } + + override double GetSliderValue() + { + double d = Menu.GetCurrentJoystickConfig().GetAxisScale(mAxis); + mNeg = d < 0? -1:1; + return d; + } + + override void SetSliderValue(double val) + { + Menu.GetCurrentJoystickConfig().SetAxisScale(mAxis, val * mNeg); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuSliderJoyDeadZone : OptionMenuSliderBase +{ + int mAxis; + int mNeg; + + + void Init(String label, int axis, double min, double max, double step, int showval) + { + Super.Init(label, min, max, step, showval); + mAxis = axis; + mNeg = 1; + } + + override double GetSliderValue() + { + double d = Menu.GetCurrentJoystickConfig().GetAxisDeadZone(mAxis); + mNeg = d < 0? -1:1; + return d; + } + + override void SetSliderValue(double val) + { + Menu.GetCurrentJoystickConfig().SetAxisDeadZone(mAxis, val * mNeg); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuItemJoyMap : OptionMenuItemOptionBase +{ + int mAxis; + + void Init(String label, int axis, Name values, int center) + { + Super.Init(label, 'none', values, null, center); + mAxis = axis; + } + + override int GetSelection() + { + double f = Menu.GetCurrentJoystickConfig().GetAxisMap(mAxis); + let opt = OptionValues.GetCount(mValues); + if (opt > 0) + { + // Map from joystick axis to menu selection. + for(int i = 0; i < opt; i++) + { + if (f ~== OptionValues.GetValue(mValues, i)) + { + return i; + } + } + } + return -1; + } + + override void SetSelection(int selection) + { + let opt = OptionValues.GetCount(mValues); + // Map from menu selection to joystick axis. + if (opt == 0 || selection >= opt) + { + selection = JoystickConfig.JOYAXIS_None; + } + else + { + selection = int(OptionValues.GetValue(mValues, selection)); + } + Menu.GetCurrentJoystickConfig().SetAxisMap(mAxis, selection); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuItemInverter : OptionMenuItemOptionBase +{ + int mAxis; + + + void Init(String label, int axis, int center) + { + Super.Init(label, "none", "YesNo", NULL, center); + mAxis = axis; + } + + override int GetSelection() + { + float f = Menu.GetCurrentJoystickConfig().GetAxisScale(mAxis); + return f > 0? 0:1; + } + + override void SetSelection(int Selection) + { + let f = abs(Menu.GetCurrentJoystickConfig().GetAxisScale(mAxis)); + if (Selection) f*=-1; + Menu.GetCurrentJoystickConfig().SetAxisScale(mAxis, f); + } +} + +//============================================================================= +// +// Executes a CCMD, action is a CCMD name +// +//============================================================================= + +class OptionMenuItemJoyConfigMenu : OptionMenuItemSubmenu +{ + JoystickConfig mJoy; + + void Init(String label = "", JoystickConfig joy = null) + { + Super.Init(label, "JoystickConfigMenu"); + mJoy = joy; + } + + override bool Activate() + { + //UpdateJoystickConfigMenu(mJoy); + return Super.Activate(); + } +} + diff --git a/wadsrc/static/zscript/menu/listmenuitems.txt b/wadsrc/static/zscript/menu/listmenuitems.txt new file mode 100644 index 000000000..8920e617b --- /dev/null +++ b/wadsrc/static/zscript/menu/listmenuitems.txt @@ -0,0 +1,309 @@ +/* +** listmenu.cpp +** A simple menu consisting of a list of items +** +**--------------------------------------------------------------------------- +** Copyright 2010-2017 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +class ListMenuItem : MenuItemBase +{ + void DrawSelector(int xofs, int yofs, TextureID tex) + { + if (tex.isNull()) + { + if ((Menu.MenuTime() % 8) < 6) + { + screen.DrawText(ConFont, OptionMenuSettings.mFontColorSelection, + (mXpos + xofs - 160) * CleanXfac + screen.GetWidth() / 2, + (mYpos + yofs - 100) * CleanYfac + screen.GetHeight() / 2, + "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac + ); + } + } + else + { + screen.DrawTexture (tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true); + } + } +} + +//============================================================================= +// +// static patch +// +//============================================================================= + +class ListMenuItemStaticPatch : ListMenuItem +{ + TextureID mTexture; + bool mCentered; + + void Init(int x, int y, TextureID patch, bool centered = false) + { + Super.Init(x, y); + mTexture = patch; + mCentered = centered; + } + + override void Drawer(bool selected) + { + if (!mTexture.Exists()) + { + return; + } + + int x = mXpos; + Vector2 vec = TexMan.GetScaledSize(mTexture); + if (mYpos >= 0) + { + if (mCentered) x -= int(vec.X) / 2; + screen.DrawTexture (mTexture, true, x, mYpos, DTA_Clean, true); + } + else + { + int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1); + if (mCentered) x -= (int(vec.X) * CleanXfac)/2; + screen.DrawTexture (mTexture, true, x, -mYpos*CleanYfac, DTA_CleanNoMove, true); + } + } +} + +class ListMenuItemStaticPatchCentered : ListMenuItemStaticPatch +{ + void Init(int x, int y, TextureID patch) + { + Super.Init(x, y, patch, true); + } +} + +//============================================================================= +// +// static text +// +//============================================================================= + +class ListMenuItemStaticText : ListMenuItem +{ + String mText; + Font mFont; + int mColor; + bool mCentered; + + void Init(ListMenuDescriptor desc, int x, int y, String text, int color = Font.CR_UNTRANSLATED) + { + Super.Init(x, y); + mText = text; + mFont = desc.mFont; + mColor = color >= 0? color : desc.mFontColor; + mCentered = false; + } + + void InitDirect(int x, int y, String text, Font font, int color = Font.CR_UNTRANSLATED, bool centered = false) + { + Super.Init(x, y); + mText = text; + mFont = font; + mColor = color; + mCentered = centered; + } + + override void Drawer(bool selected) + { + if (mText.Length() != 0) + { + String text = Stringtable.Localize(mText); + if (mYpos >= 0) + { + int x = mXpos; + if (mCentered) x -= mFont.StringWidth(text)/2; + screen.DrawText(mFont, mColor, x, mYpos, text, DTA_Clean, true); + } + else + { + int x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1); + if (mCentered) x -= (mFont.StringWidth(text) * CleanXfac)/2; + screen.DrawText (mFont, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true); + } + } + } +} + +class ListMenuItemStaticTextCentered : ListMenuItemStaticText +{ + void Init(ListMenuDescriptor desc, int x, int y, String text, int color = -1) + { + Super.Init(desc, x, y, text, color); + mCentered = true; + } +} + +//============================================================================= +// +// selectable items +// +//============================================================================= + +class ListMenuItemSelectable : ListMenuItem +{ + int mHotkey; + int mHeight; + int mParam; + + protected void Init(int x, int y, int height, Name childmenu, int param = -1) + { + Super.Init(x, y, childmenu); + mHeight = height; + mParam = param; + mHotkey = 0; + } + + override bool CheckCoordinate(int x, int y) + { + return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here + } + + override bool Selectable() + { + return mEnabled; + } + + override bool CheckHotkey(int c) + { + return c == mHotkey; + } + + override bool Activate() + { + Menu.SetMenu(mAction, mParam); + return true; + } + + override bool MouseEvent(int type, int x, int y) + { + if (type == Menu.MOUSE_Release) + { + let m = Menu.GetCurrentMenu(); + if (m != NULL && m.MenuEvent(Menu.MKEY_Enter, true)) + { + return true; + } + } + return false; + } + + override Name, int GetAction() + { + return mAction, mParam; + } +} + +//============================================================================= +// +// text item +// +//============================================================================= + +class ListMenuItemTextItem : ListMenuItemSelectable +{ + String mText; + Font mFont; + int mColor; + int mColorSelected; + + void Init(ListMenuDescriptor desc, String text, String hotkey, Name child, int param = 0) + { + Super.Init(desc.mXpos, desc.mYpos, desc.mLinespacing, child, param); + mText = text; + mFont = desc.mFont; + mColor = desc.mFontColor; + mColorSelected = desc.mFontcolor2; + mHotkey = hotkey.CharCodeAt(0); + } + + void InitDirect(int x, int y, int height, String hotkey, String text, Font font, int color, int color2, Name child, int param = 0) + { + Super.Init(x, y, height, child, param); + mText = text; + mFont = font; + mColor = color; + mColorSelected = color2; + mHotkey = hotkey.CharCodeAt(0); + } + + override void Drawer(bool selected) + { + screen.DrawText(mFont, selected ? mColorSelected : mColor, mXpos, mYpos, mText, DTA_Clean, true); + } + + override int GetWidth() + { + return min(1, mFont.StringWidth(StringTable.Localize(mText))); + } +} + +//============================================================================= +// +// patch item +// +//============================================================================= + +class ListMenuItemPatchItem : ListMenuItemSelectable +{ + TextureID mTexture; + + void Init(ListMenuDescriptor desc, TextureID patch, String hotkey, Name child, int param = 0) + { + Super.Init(desc.mXpos, desc.mYpos, desc.mLinespacing, child, param); + mHotkey = hotkey.CharCodeAt(0); + mTexture = patch; + } + + void InitDirect(int x, int y, int height, TextureID patch, String hotkey, Name child, int param = 0) + { + Super.Init(x, y, height, child, param); + mHotkey = hotkey.CharCodeAt(0); + mTexture = patch; + } + + override void Drawer(bool selected) + { + screen.DrawTexture (mTexture, true, mXpos, mYpos, DTA_Clean, true); + } + + override int GetWidth() + { + return TexMan.GetSize(mTexture); + } + +} + diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index c759e8351..1d878e112 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -1,4 +1,48 @@ +struct KeyBindings native +{ + native static String NameKeys(int k1, int k2); + + native int, int GetKeysForCommand(String cmd); + native void SetBind(int key, String cmd); + native void UnbindACommand (String str); +} + +struct OptionValues native +{ + native static int GetCount(Name group); + native static String GetText(Name group, int index); + native static double GetValue(Name group, int index); + native static String GetTextValue(Name group, int index); +} + +struct JoystickConfig native +{ + enum EJoyAxis + { + JOYAXIS_None = -1, + JOYAXIS_Yaw, + JOYAXIS_Pitch, + JOYAXIS_Forward, + JOYAXIS_Side, + JOYAXIS_Up, + // JOYAXIS_Roll, // Ha ha. No roll for you. + NUM_JOYAXIS, + }; + + native float GetSensitivity(); + native void SetSensitivity(float scale); + + native float GetAxisScale(int axis); + native void SetAxisScale(int axis, float scale); + + native float GetAxisDeadZone(int axis); + native void SetAxisDeadZone(int axis, float zone); + + native int GetAxisMap(int axis); + native void SetAxisMap(int axis, int gameaxis); +} + class Menu : Object native { enum EMenuKey @@ -29,17 +73,53 @@ class Menu : Object native MOUSE_Release }; - //native static int MenuTime(); + enum EMenuState + { + Off, // Menu is closed + On, // Menu is opened + WaitKey, // Menu is opened and waiting for a key in the controls menu + OnNoPause, // Menu is opened but does not pause the game + }; + + native Menu mParentMenu; + + void Init(Menu parent) + { + mParentMenu = parent; + } + native static int MenuTime(); + native static void SetVideoMode(); + native static Menu GetCurrentMenu(); + native static JoystickConfig GetCurrentJoystickConfig(); + native static void SetMenu(Name mnu, int param = 0); + native static void StartMessage(String msg, int mode = 0, Name command = 'none'); + + virtual bool TranslateKeyboardEvents() { return true; } + virtual void SetFocus(MenuItemBase fc) {} + virtual bool CheckFocus(MenuItemBase fc) { return false; } + virtual void ReleaseFocus() {} + virtual void ResetColor() {} + + native virtual bool Responder(InputEvent ev); native virtual bool MenuEvent (int mkey, bool fromcontroller); native virtual bool MouseEvent(int type, int mx, int my); + native virtual void Ticker(); native virtual void Drawer(); + native void Close(); + native MenuItemBase GetItem(Name n); + native void ActivateMenu(); - void MenuSound(Sound snd) + static void MenuSound(Sound snd) { S_Sound (snd, CHAN_VOICE | CHAN_UI, snd_menuvolume, ATTN_NONE); } + static void DrawConText (int color, int x, int y, String str) + { + screen.DrawText (ConFont, color, x, y, str, DTA_CellX, 8 * CleanXfac_1, DTA_CellY, 8 * CleanYfac_1); + } + } class MenuDescriptor : Object native @@ -47,17 +127,42 @@ class MenuDescriptor : Object native native Name mMenuName; native String mNetgameMessage; native Class mClass; + + native static MenuDescriptor GetDescriptor(Name n); } -class MenuItemBase : object native +class ListMenuDescriptor : MenuDescriptor native { + native Array mItems; + native int mSelectedItem; + native int mSelectOfsX; + native int mSelectOfsY; + native TextureID mSelector; + native int mDisplayTop; native int mXpos, mYpos; - native Name mAction; - native bool mEnabled; - - // making this virtual now would require exporting all classes at once. - native /*virtual*/ bool MenuEvent (int mkey, bool fromcontroller); - + native int mWLeft, mWRight; + native int mLinespacing; // needs to be stored for dynamically created menus + native int mAutoselect; // this can only be set by internal menu creation functions + native Font mFont; + native int mFontColor; + native int mFontColor2; + native bool mCenter; + + void Reset() + { + // Reset the default settings (ignore all other values in the struct) + mSelectOfsX = 0; + mSelectOfsY = 0; + mSelector.SetInvalid(); + mDisplayTop = 0; + mXpos = 0; + mYpos = 0; + mLinespacing = 0; + mNetgameMessage = ""; + mFont = NULL; + mFontColor = Font.CR_UNTRANSLATED; + mFontColor2 = Font.CR_UNTRANSLATED; + } } struct FOptionMenuSettings @@ -84,7 +189,7 @@ class OptionMenuDescriptor : MenuDescriptor native native int mPosition; native bool mDontDim; - //native void CalcIndent(); + native void CalcIndent(); //native OptionMenuItem GetItem(Name iname); void Reset() { @@ -96,20 +201,3 @@ class OptionMenuDescriptor : MenuDescriptor native } } -class OptionMenuItem : MenuItemBase native -{ - native String mLabel; - native bool mCentered; - - //native void drawLabel(int indent, int y, EColorRange color, bool grayed = false); -} - -class OptionMenu : Menu native -{ - native bool CanScrollUp; - native bool CanScrollDown; - native int VisBottom; - native OptionMenuItem mFocusControl; - native OptionMenuDescriptor mDesc; -} - diff --git a/wadsrc/static/zscript/menu/menuitembase.txt b/wadsrc/static/zscript/menu/menuitembase.txt index e92f17fcf..de5f05a42 100644 --- a/wadsrc/static/zscript/menu/menuitembase.txt +++ b/wadsrc/static/zscript/menu/menuitembase.txt @@ -6,12 +6,11 @@ class MenuItemBase : Object native { - protected int mXpos, mYpos; - protected Name mAction; + protected native int mXpos, mYpos; + protected native Name mAction; + native bool mEnabled; - bool mEnabled; - - protected void Init(int xpos = 0, int ypos = 0, Name actionname = 'None') + void Init(int xpos = 0, int ypos = 0, Name actionname = 'None') { mXpos = xpos; mYpos = ypos; @@ -34,33 +33,17 @@ class MenuItemBase : Object native virtual bool MouseEvent(int type, int x, int y) { return false; } virtual bool CheckHotkey(int c) { return false; } virtual int GetWidth() { return 0; } - virtual void OffsetPositionY(int ydelta) { mYpos += ydelta; } - virtual int GetY() { return mYpos; } - virtual int GetX() { return mXpos; } - virtual void SetX(int x) { mXpos = x; } - - /* - virtual void DrawSelector(int xofs, int yofs, TextureID tex) - { - if (tex.isNull()) - { - if ((Menu.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 (tex, mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE); - } - } - */ - + virtual int GetIndent() { return 0; } + virtual int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) { return indent; } + + void OffsetPositionY(int ydelta) { mYpos += ydelta; } + int GetY() { return mYpos; } + int GetX() { return mXpos; } + void SetX(int x) { mXpos = x; } } +// this is only used to parse font color ranges in MENUDEF +enum MenudefColorRange +{ + NO_COLOR = -1 +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt new file mode 100644 index 000000000..7d2f34c77 --- /dev/null +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -0,0 +1,469 @@ +/* +** optionmenu.cpp +** Handler class for the option menus and associated items +** +**--------------------------------------------------------------------------- +** Copyright 2010-2017 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +class OptionMenu : Menu +{ + OptionMenuDescriptor mDesc; + bool CanScrollUp; + bool CanScrollDown; + int VisBottom; + OptionMenuItem mFocusControl; + + //============================================================================= + // + // + // + //============================================================================= + + virtual void Init(Menu parent, OptionMenuDescriptor desc) + { + mParentMenu = parent; + mDesc = desc; + if (mDesc != NULL && mDesc.mSelectedItem == -1) mDesc.mSelectedItem = FirstSelectable(); + + } + + + //============================================================================= + // + // + // + //============================================================================= + + int FirstSelectable() + { + if (mDesc != NULL) + { + // Go down to the first selectable item + int i = -1; + do + { + i++; + } + while (i < mDesc.mItems.Size() && !mDesc.mItems[i].Selectable()); + if (i>=0 && i < mDesc.mItems.Size()) return i; + } + return -1; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool Responder (InputEvent ev) + { + if (ev.type == InputEvent.GUI_Event) + { + if (ev.subtype == InputEvent.GUI_WheelUp) + { + int scrollamt = MIN(2, mDesc.mScrollPos); + mDesc.mScrollPos -= scrollamt; + return true; + } + else if (ev.subtype == InputEvent.GUI_WheelDown) + { + if (CanScrollDown) + { + if (VisBottom >= 0 && VisBottom < (mDesc.mItems.Size()-2)) + { + mDesc.mScrollPos += 2; + VisBottom += 2; + } + else + { + mDesc.mScrollPos++; + VisBottom++; + } + } + return true; + } + } + return Super.Responder(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + int startedAt = mDesc.mSelectedItem; + + switch (mkey) + { + case MKEY_Up: + if (mDesc.mSelectedItem == -1) + { + mDesc.mSelectedItem = FirstSelectable(); + break; + } + do + { + --mDesc.mSelectedItem; + + if (mDesc.mScrollPos > 0 && + mDesc.mSelectedItem <= mDesc.mScrollTop + mDesc.mScrollPos) + { + mDesc.mScrollPos = MAX(mDesc.mSelectedItem - mDesc.mScrollTop - 1, 0); + } + + if (mDesc.mSelectedItem < 0) + { + // Figure out how many lines of text fit on the menu + int y = mDesc.mPosition; + + if (y <= 0) + { + if (BigFont && mDesc.mTitle.Length() > 0) + { + y = -y + BigFont.GetHeight(); + } + else + { + y = -y; + } + } + y *= CleanYfac_1; + int rowheight = OptionMenuSettings.mLinespacing * CleanYfac_1; + int maxitems = (screen.GetHeight() - rowheight - y) / rowheight + 1; + + mDesc.mScrollPos = MAX (0, mDesc.mItems.Size() - maxitems + mDesc.mScrollTop); + mDesc.mSelectedItem = mDesc.mItems.Size()-1; + } + } + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt); + break; + + case MKEY_Down: + if (mDesc.mSelectedItem == -1) + { + mDesc.mSelectedItem = FirstSelectable(); + break; + } + do + { + ++mDesc.mSelectedItem; + + if (CanScrollDown && mDesc.mSelectedItem == VisBottom) + { + mDesc.mScrollPos++; + VisBottom++; + } + if (mDesc.mSelectedItem >= mDesc.mItems.Size()) + { + if (startedAt == -1) + { + mDesc.mSelectedItem = -1; + mDesc.mScrollPos = -1; + break; + } + else + { + mDesc.mSelectedItem = 0; + mDesc.mScrollPos = 0; + } + } + } + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable() && mDesc.mSelectedItem != startedAt); + break; + + case MKEY_PageUp: + if (mDesc.mScrollPos > 0) + { + mDesc.mScrollPos -= VisBottom - mDesc.mScrollPos - mDesc.mScrollTop; + if (mDesc.mScrollPos < 0) + { + mDesc.mScrollPos = 0; + } + if (mDesc.mSelectedItem != -1) + { + mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos + 1; + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable()) + { + if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) + { + mDesc.mSelectedItem = 0; + } + } + if (mDesc.mScrollPos > mDesc.mSelectedItem) + { + mDesc.mScrollPos = mDesc.mSelectedItem; + } + } + } + break; + + case MKEY_PageDown: + if (CanScrollDown) + { + int pagesize = VisBottom - mDesc.mScrollPos - mDesc.mScrollTop; + mDesc.mScrollPos += pagesize; + if (mDesc.mScrollPos + mDesc.mScrollTop + pagesize > mDesc.mItems.Size()) + { + mDesc.mScrollPos = mDesc.mItems.Size() - mDesc.mScrollTop - pagesize; + } + if (mDesc.mSelectedItem != -1) + { + mDesc.mSelectedItem = mDesc.mScrollTop + mDesc.mScrollPos; + while (!mDesc.mItems[mDesc.mSelectedItem].Selectable()) + { + if (++mDesc.mSelectedItem >= mDesc.mItems.Size()) + { + mDesc.mSelectedItem = 0; + } + } + if (mDesc.mScrollPos > mDesc.mSelectedItem) + { + mDesc.mScrollPos = mDesc.mSelectedItem; + } + } + } + break; + + case MKEY_Enter: + if (mDesc.mSelectedItem >= 0 && mDesc.mItems[mDesc.mSelectedItem].Activate()) + { + return true; + } + // fall through to default + default: + if (mDesc.mSelectedItem >= 0 && + mDesc.mItems[mDesc.mSelectedItem].MenuEvent(mkey, fromcontroller)) return true; + return Super.MenuEvent(mkey, fromcontroller); + } + + if (mDesc.mSelectedItem != startedAt) + { + MenuSound ("menu/cursor"); + } + return true; + } + + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + y = (y / CleanYfac_1) - mDesc.mDrawTop; + + if (mFocusControl) + { + mFocusControl.MouseEvent(type, x, y); + return true; + } + else + { + int yline = (y / OptionMenuSettings.mLinespacing); + if (yline >= mDesc.mScrollTop) + { + yline += mDesc.mScrollPos; + } + if (yline >= 0 && yline < mDesc.mItems.Size() && mDesc.mItems[yline].Selectable()) + { + if (yline != mDesc.mSelectedItem) + { + mDesc.mSelectedItem = yline; + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + mDesc.mItems[yline].MouseEvent(type, x, y); + return true; + } + } + mDesc.mSelectedItem = -1; + return Super.MouseEvent(type, x, y); + } + + + //============================================================================= + // + // + // + //============================================================================= + + override void Ticker () + { + Super.Ticker(); + for(int i = 0; i < mDesc.mItems.Size(); i++) + { + mDesc.mItems[i].Ticker(); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + int y = mDesc.mPosition; + + if (y <= 0) + { + if (BigFont && mDesc.mTitle.Length() > 0) + { + let tt = Stringtable.Localize(mDesc.mTitle); + screen.DrawText (BigFont, OptionMenuSettings.mTitleColor, + (screen.GetWidth() - BigFont.StringWidth(tt) * CleanXfac_1) / 2, 10*CleanYfac_1, + tt, DTA_CleanNoMove_1, true); + y = -y + BigFont.GetHeight(); + } + else + { + y = -y; + } + } + mDesc.mDrawTop = y; + int fontheight = OptionMenuSettings.mLinespacing * CleanYfac_1; + y *= CleanYfac_1; + + int indent = mDesc.mIndent; + if (indent > 280) + { // kludge for the compatibility options with their extremely long labels + if (indent + 40 <= CleanWidth_1) + { + indent = (screen.GetWidth() - ((indent + 40) * CleanXfac_1)) / 2 + indent * CleanXfac_1; + } + else + { + indent = screen.GetWidth() - 40 * CleanXfac_1; + } + } + else + { + indent = (indent - 160) * CleanXfac_1 + screen.GetWidth() / 2; + } + + int ytop = y + mDesc.mScrollTop * 8 * CleanYfac_1; + int lastrow = screen.GetHeight() - SmallFont.GetHeight() * CleanYfac_1; + + int i; + for (i = 0; i < mDesc.mItems.Size() && y <= lastrow; i++) + { + // Don't scroll the uppermost items + if (i == mDesc.mScrollTop) + { + i += mDesc.mScrollPos; + if (i >= mDesc.mItems.Size()) break; // skipped beyond end of menu + } + bool isSelected = mDesc.mSelectedItem == i; + int cur_indent = mDesc.mItems[i].Draw(mDesc, y, indent, isSelected); + if (cur_indent >= 0 && isSelected && mDesc.mItems[i].Selectable()) + { + if (((MenuTime() % 8) < 6) || GetCurrentMenu() != self) + { + DrawConText(OptionMenuSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y+fontheight-9*CleanYfac_1, "\xd"); + } + } + y += fontheight; + } + + CanScrollUp = (mDesc.mScrollPos > 0); + CanScrollDown = (i < mDesc.mItems.Size()); + VisBottom = i - 1; + + if (CanScrollUp) + { + DrawConText(Font.CR_ORANGE, 3 * CleanXfac_1, ytop, "\x1a"); + } + if (CanScrollDown) + { + DrawConText(Font.CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1, "\x1b"); + } + Super.Drawer(); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void SetFocus(MenuItemBase fc) + { + mFocusControl = OptionMenuItem(fc); + } + + override bool CheckFocus(MenuItemBase fc) + { + return mFocusControl == fc; + } + + override void ReleaseFocus() + { + mFocusControl = NULL; + } +} + + +class GameplayMenu : OptionMenu +{ + override void Drawer () + { + Super.Drawer(); + + String s = String.Format("dmflags = %d dmflags2 = %d", dmflags, dmflags2); + screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, + (screen.GetWidth() - SmallFont.StringWidth (s) * CleanXfac_1) / 2, 0, s, + DTA_CleanNoMove_1, true); + } +} + +class CompatibilityMenu : OptionMenu +{ + override void Drawer () + { + Super.Drawer(); + + String s = String.Format("compatflags = %d compatflags2 = %d", compatflags, compatflags2); + screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, + (screen.GetWidth() - SmallFont.StringWidth (s) * CleanXfac_1) / 2, 0, s, + DTA_CleanNoMove_1, true); + } +} + +class JoystickConfigMenu : OptionMenu +{ + // This is not really needed anymore but needs to be kept for old MENUDEFs that keep the entry +} + diff --git a/wadsrc/static/zscript/menu/optionmenuitems.txt b/wadsrc/static/zscript/menu/optionmenuitems.txt new file mode 100644 index 000000000..4796ce4c9 --- /dev/null +++ b/wadsrc/static/zscript/menu/optionmenuitems.txt @@ -0,0 +1,1200 @@ +/* +** optionmenuitems.txt +** Control items for option menus +** +**--------------------------------------------------------------------------- +** 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 OptionMenuItem : MenuItemBase +{ + String mLabel; + bool mCentered; + + protected void Init(String label, String command, bool center = false) + { + Super.Init(0, 0, command); + mLabel = label; + mCentered = center; + } + + protected void drawLabel(int indent, int y, int color, bool grayed = false) + { + String label = Stringtable.Localize(mLabel); + + int overlay = grayed? Color(96,48,0,0) : 0; + + int x; + int w = SmallFont.StringWidth(label) * CleanXfac_1; + if (!mCentered) x = indent - w; + else x = (screen.GetWidth() - w) / 2; + screen.DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay); + } + + int CursorSpace() + { + return (14 * CleanXfac_1); + } + + override bool Selectable() + { + return true; + } + + override int GetIndent() + { + if (mCentered) return 0; + return SmallFont.StringWidth(Stringtable.Localize(mLabel)); + } + + override bool MouseEvent(int type, int x, int y) + { + if (Selectable() && type == Menu.MOUSE_Release) + { + return Menu.GetCurrentMenu().MenuEvent(Menu.MKEY_Enter, true); + } + return false; + } +} + +//============================================================================= +// +// opens a submenu, command is a submenu name +// +//============================================================================= + +class OptionMenuItemSubmenu : OptionMenuItem +{ + int mParam; + OptionMenuItemSubmenu Init(String label, Name command, int param = 0) + { + Super.init(label, command); + mParam = param; + return self; + } + + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColorMore); + return indent; + } + + override bool Activate() + { + Menu.MenuSound("menu/choose"); + Menu.SetMenu(mAction, mParam); + return true; + } +} + +//============================================================================= +// +// Executes a CCMD, command is a CCMD name +// +//============================================================================= + +class OptionMenuItemCommand : OptionMenuItemSubmenu +{ + OptionMenuItemCommand Init(String label, Name command) + { + Super.Init(label, command); + return self; + } + + override bool Activate() + { + Menu.MenuSound("menu/choose"); + Console.DoCommand(mAction); + return true; + } + +} + +//============================================================================= +// +// Executes a CCMD after confirmation, command is a CCMD name +// +//============================================================================= + +class OptionMenuItemSafeCommand : OptionMenuItemCommand +{ + String mPrompt; + + + OptionMenuItemSafeCommand Init(String label, Name command, String prompt = "") + { + Super.Init(label, command); + mPrompt = prompt; + return self; + } + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_MBYes) + { + Console.DoCommand(mAction); + return true; + } + return Super.MenuEvent(mkey, fromcontroller); + } + + override bool Activate() + { + String msg = mPrompt.Length() > 0 ? mPrompt : "$SAFEMESSAGE"; + msg = StringTable.Localize(msg); + String actionLabel = StringTable.localize(mLabel); + + String FullString; + FullString = String.Format("%s%s%s\n\n%s", TEXTCOLOR_WHITE, actionLabel, TEXTCOLOR_NORMAL, msg); + Menu.StartMessage(FullString, 0); + return true; + } +} + +//============================================================================= +// +// Base class for option lists +// +//============================================================================= + +class OptionMenuItemOptionBase : OptionMenuItem +{ + // command is a CVAR + Name mValues; // Entry in OptionValues table + CVar mGrayCheck; + int mCenter; + + const OP_VALUES = 0x11001; + + protected void Init(String label, Name command, Name values, CVar graycheck, int center) + { + Super.Init(label, command); + mValues = values; + mGrayCheck = graycheck; + mCenter = center; + } + + override bool SetString(int i, String newtext) + { + if (i == OP_VALUES) + { + int cnt = OptionValues.GetCount(mValues); + if (cnt >= 0) + { + mValues = newtext; + int s = GetSelection(); + if (s >= cnt) s = 0; + SetSelection(s); // readjust the CVAR if its value is outside the range now + return true; + } + } + return false; + } + + //============================================================================= + virtual int GetSelection() + { + return 0; + } + + virtual void SetSelection(int Selection) + { + } + + //============================================================================= + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + bool grayed = mGrayCheck != null && !(mGrayCheck.GetInt()); + + if (mCenter) + { + indent = (screen.GetWidth() / 2); + } + drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed); + + int overlay = grayed? Color(96,48,0,0) : 0; + int Selection = GetSelection(); + String text = StringTable.Localize(OptionValues.GetText(mValues, Selection)); + if (text.Length() == 0) text = "Unknown"; + screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay); + return indent; + } + + //============================================================================= + override bool MenuEvent (int mkey, bool fromcontroller) + { + int cnt = OptionValues.GetCount(mValues); + if (cnt > 0) + { + int Selection = GetSelection(); + if (mkey == Menu.MKEY_Left) + { + if (Selection == -1) Selection = 0; + else if (--Selection < 0) Selection = cnt - 1; + } + else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter) + { + if (++Selection >= cnt) Selection = 0; + } + else + { + return Super.MenuEvent(mkey, fromcontroller); + } + SetSelection(Selection); + Menu.MenuSound("menu/change"); + } + else + { + return Super.MenuEvent(mkey, fromcontroller); + } + return true; + } + + override bool Selectable() + { + return mGrayCheck == null || mGrayCheck.GetInt(); + } +} + +//============================================================================= +// +// Change a CVAR, command is the CVAR name +// +//============================================================================= + +class OptionMenuItemOption : OptionMenuItemOptionBase +{ + CVar mCVar; + + OptionMenuItemOption Init(String label, Name command, Name values, CVar graycheck = null, int center = 0) + { + Super.Init(label, command, values, graycheck, center); + mCVar = CVar.FindCVar(mAction); + return self; + } + + //============================================================================= + override int GetSelection() + { + int Selection = -1; + int cnt = OptionValues.GetCount(mValues); + if (cnt > 0 && mCVar != null) + { + if (OptionValues.GetTextValue(mValues, 0).Length() == 0) + { + let f = mCVar.GetFloat(); + for(int i = 0; i < cnt; i++) + { + if (f ~== OptionValues.GetValue(mValues, i)) + { + Selection = i; + break; + } + } + } + else + { + String cv = mCVar.GetString(); + for(int i = 0; i < cnt; i++) + { + if (cv ~== OptionValues.GetTextValue(mValues, i)) + { + Selection = i; + break; + } + } + } + } + return Selection; + } + + override void SetSelection(int Selection) + { + int cnt = OptionValues.GetCount(mValues); + if (cnt > 0 && mCVar != null) + { + if (OptionValues.GetTextValue(mValues, 0).Length() == 0) + { + mCVar.SetFloat(OptionValues.GetValue(mValues, Selection)); + } + else + { + mCVar.SetString(OptionValues.GetTextValue(mValues, Selection)); + } + } + } +} + +//============================================================================= +// +// This class is used to capture the key to be used as the new key binding +// for a control item +// +//============================================================================= + +class EnterKey : Menu +{ + OptionMenuItemControlBase mOwner; + + void Init(Menu parent, OptionMenuItemControlBase owner) + { + Super.Init(parent); + mOwner = owner; + SetMenuMessage(1); + menuactive = Menu.WaitKey; // There should be a better way to disable GUI capture... + } + + override bool TranslateKeyboardEvents() + { + return false; + } + + private void SetMenuMessage(int which) + { + let parent = OptionMenu(mParentMenu); + if (parent != null) + { + let it = parent.GetItem('Controlmessage'); + if (it != null) + { + it.SetValue(0, which); + } + } + } + + override bool Responder(InputEvent ev) + { + // This checks raw keys, not GUI keys. + if (ev.type == InputEvent.KeyDown) + { + mOwner.SendKey(ev.data1); + menuactive = Menu.On; + SetMenuMessage(0); + Close(); + mParentMenu.MenuEvent((ev.data1 == InputEvent.KEY_ESCAPE)? Menu.MKEY_Abort : Menu.MKEY_Input, 0); + return true; + } + return false; + } + + override void Drawer() + { + mParentMenu.Drawer(); + } +} + +//============================================================================= +// +// // Edit a key binding, Action is the CCMD to bind +// +//============================================================================= + +class OptionMenuItemControlBase : OptionMenuItem +{ + KeyBindings mBindings; + int mInput; + bool mWaiting; + + protected void Init(String label, Name command, KeyBindings bindings) + { + Super.init(label, command); + mBindings = bindings; + mWaiting = false; + } + + //============================================================================= + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + drawLabel(indent, y, mWaiting? OptionMenuSettings.mFontColorHighlight: + (selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor)); + + String description; + int Key1, Key2; + + [Key1, Key2] = mBindings.GetKeysForCommand(mAction); + description = KeyBindings.NameKeys (Key1, Key2); + if (description.Length() > 0) + { + Menu.DrawConText(Font.CR_WHITE, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, description); + } + else + { + screen.DrawText(SmallFont, Font.CR_BLACK, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, "---", DTA_CleanNoMove_1, true); + } + return indent; + } + + //============================================================================= + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_Input) + { + mWaiting = false; + mBindings.SetBind(mInput, mAction); + return true; + } + else if (mkey == Menu.MKEY_Clear) + { + mBindings.UnbindACommand(mAction); + return true; + } + else if (mkey == Menu.MKEY_Abort) + { + mWaiting = false; + return true; + } + return false; + } + + void SendKey(int key) + { + mInput = key; + } + + override bool Activate() + { + Menu.MenuSound("menu/choose"); + mWaiting = true; + let input = new("EnterKey"); + input.Init(Menu.GetCurrentMenu(), self); + input.ActivateMenu(); + return true; + } +} + +class OptionMenuItemControl : OptionMenuItemControlBase +{ + OptionMenuItemControl Init(String label, Name command) + { + Super.Init(label, command, Bindings); + return self; + } +} + +class OptionMenuItemMapControl : OptionMenuItemControlBase +{ + OptionMenuItemMapControl Init(String label, Name command) + { + Super.Init(label, command, AutomapBindings); + return self; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuItemStaticText : OptionMenuItem +{ + int mColor; + + // this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility. + OptionMenuItemStaticText Init(String label, int cr = -1) + { + Super.Init(label, 'None', true); + mColor = OptionMenuSettings.mFontColor; + if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff; + else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader; + return self; + } + + OptionMenuItemStaticText InitDirect(String label, int cr) + { + Super.Init(label, 'None', true); + mColor = cr; + return self; + } + + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + drawLabel(indent, y, mColor); + return -1; + } + + override bool Selectable() + { + return false; + } + +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuItemStaticTextSwitchable : OptionMenuItem +{ + int mColor; + String mAltText; + int mCurrent; + + // this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility. + OptionMenuItemStaticTextSwitchable Init(String label, String label2, Name command, int cr = -1) + { + Super.Init(label, command, true); + mAltText = label2; + mCurrent = 0; + + mColor = OptionMenuSettings.mFontColor; + if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff; + else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader; + return self; + } + + OptionMenuItemStaticTextSwitchable InitDirect(String label, String label2, Name command, int cr) + { + Super.Init(label, command, true); + mColor = cr; + mAltText = label2; + mCurrent = 0; + return self; + } + + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + String txt = StringTable.Localize(mCurrent? mAltText : mLabel); + int w = SmallFont.StringWidth(txt) * CleanXfac_1; + int x = (screen.GetWidth() - w) / 2; + screen.DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true); + return -1; + } + + override bool SetValue(int i, int val) + { + if (i == 0) + { + mCurrent = val; + return true; + } + return false; + } + + override bool SetString(int i, String newtext) + { + if (i == 0) + { + mAltText = newtext; + return true; + } + return false; + } + + override bool Selectable() + { + return false; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuSliderBase : OptionMenuItem +{ + // command is a CVAR + double mMin, mMax, mStep; + int mShowValue; + int mDrawX; + int mSliderShort; + + protected void Init(String label, double min, double max, double step, int showval) + { + Super.Init(label, 'None'); + mMin = min; + mMax = max; + mStep = step; + mShowValue = showval; + mDrawX = 0; + mSliderShort = 0; + } + + virtual double GetSliderValue() + { + return 0; + } + + virtual void SetSliderValue(double val) + { + } + + //============================================================================= + // + // Draw a slider. Set fracdigits negative to not display the current value numerically. + // + //============================================================================= + + private void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent) + { + String formater = String.format("%%.%df", fracdigits); // The format function cannot do the '%.*f' syntax. + String textbuf; + double range; + int maxlen = 0; + int right = x + (12*8 + 4) * CleanXfac_1; + int cy = y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1; + + range = max - min; + double ccur = clamp(cur, min, max) - min; + + if (fracdigits >= 0) + { + textbuf = String.format(formater, max); + maxlen = SmallFont.StringWidth(textbuf) * CleanXfac_1; + } + + mSliderShort = right + maxlen > screen.GetWidth(); + + if (!mSliderShort) + { + Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12"); + Menu.DrawConText(Font.CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13"); + } + else + { + // On 320x200 we need a shorter slider + Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12"); + Menu.DrawConText(Font.CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13"); + right -= 5*8*CleanXfac_1; + } + + if (fracdigits >= 0 && right + maxlen <= screen.GetWidth()) + { + textbuf = String.format(formater, cur); + screen.DrawText(SmallFont, Font.CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true); + } + } + + + //============================================================================= + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor); + mDrawX = indent + CursorSpace(); + DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent); + return indent; + } + + //============================================================================= + override bool MenuEvent (int mkey, bool fromcontroller) + { + double value = GetSliderValue(); + + if (mkey == Menu.MKEY_Left) + { + value -= mStep; + } + else if (mkey == Menu.MKEY_Right) + { + value += mStep; + } + else + { + return OptionMenuItem.MenuEvent(mkey, fromcontroller); + } + if (value ~== 0) value = 0; // This is to prevent formatting anomalies with very small values + SetSliderValue(clamp(value, mMin, mMax)); + Menu.MenuSound("menu/change"); + return true; + } + + override bool MouseEvent(int type, int x, int y) + { + let lm = OptionMenu(Menu.GetCurrentMenu()); + if (type != Menu.MOUSE_Click) + { + if (!lm.CheckFocus(self)) return false; + } + if (type == Menu.MOUSE_Release) + { + lm.ReleaseFocus(); + } + + int slide_left = mDrawX+8*CleanXfac_1; + int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each. + + if (type == Menu.MOUSE_Click) + { + if (x < slide_left || x >= slide_right) return true; + } + + x = clamp(x, slide_left, slide_right); + double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left); + if (v != GetSliderValue()) + { + SetSliderValue(v); + //Menu.MenuSound("menu/change"); + } + if (type == Menu.MOUSE_Click) + { + lm.SetFocus(self); + } + return true; + } + +} + +//============================================================================= +// +// +// +//============================================================================= + +class OptionMenuItemSlider : OptionMenuSliderBase +{ + CVar mCVar; + + OptionMenuItemSlider Init(String label, Name command, double min, double max, double step, int showval = 1) + { + Super.Init(label, min, max, step, showval); + mCVar =CVar.FindCVar(command); + return self; + } + + override double GetSliderValue() + { + if (mCVar != null) + { + return mCVar.GetFloat(); + } + else + { + return 0; + } + } + + override void SetSliderValue(double val) + { + if (mCVar != null) + { + mCVar.SetFloat(val); + } + } +} + +//============================================================================= +// +// // Edit a key binding, Action is the CCMD to bind +// +//============================================================================= + +class OptionMenuItemColorPicker : OptionMenuItem +{ + CVar mCVar; + + const CPF_RESET = 0x20001; + + OptionMenuItemColorPicker Init(String label, Name command) + { + Super.Init(label, command); + CVar cv = CVar.FindCVar(command); + if (cv != null && cv.GetRealType() != CVar.CVAR_Color) cv = null; + mCVar = cv; + return self; + } + + //============================================================================= + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor); + + if (mCVar != null) + { + int box_x = indent + CursorSpace(); + int box_y = y + CleanYfac_1; + screen.Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + OptionMenuSettings.mLinespacing*CleanYfac_1, mCVar.GetInt() | 0xff000000); + } + return indent; + } + + override bool SetValue(int i, int v) + { + if (i == CPF_RESET && mCVar != null) + { + mCVar.ResetToDefault(); + return true; + } + return false; + } + + override bool Activate() + { + if (mCVar != null) + { + Menu.MenuSound("menu/choose"); + + // This code is a bit complicated because it should allow subclassing the + // colorpicker menu. + // New color pickers must inherit from the internal one to work here. + + let desc = MenuDescriptor.GetDescriptor('Colorpickermenu'); + if (desc != NULL && (desc.mClass == null || desc.mClass is "ColorPickerMenu")) + { + let odesc = OptionMenuDescriptor(desc); + if (odesc != null) + { + let cls = desc.mClass; + if (cls == null) cls = "ColorpickerMenu"; + let picker = ColorpickerMenu(new(cls)); + picker.Init(Menu.GetCurrentMenu(), mLabel, odesc, mCVar); + picker.ActivateMenu(); + return true; + } + } + } + return false; + } +} + +class OptionMenuItemScreenResolution : OptionMenuItem +{ + String mResTexts[3]; + int mSelection; + int mHighlight; + int mMaxValid; + + enum EValues + { + SRL_INDEX = 0x30000, + SRL_SELECTION = 0x30003, + SRL_HIGHLIGHT = 0x30004, + }; + + OptionMenuItemScreenResolution Init(String command) + { + Super.Init("", command); + mSelection = 0; + mHighlight = -1; + return self; + } + + override bool SetValue(int i, int v) + { + if (i == SRL_SELECTION) + { + mSelection = v; + return true; + } + else if (i == SRL_HIGHLIGHT) + { + mHighlight = v; + return true; + } + return false; + } + + override bool, int GetValue(int i) + { + if (i == SRL_SELECTION) + { + return true, mSelection; + } + return false, 0; + } + + override bool SetString(int i, String newtext) + { + if (i >= SRL_INDEX && i <= SRL_INDEX+2) + { + mResTexts[i-SRL_INDEX] = newtext; + if (mResTexts[0].Length() == 0) mMaxValid = -1; + else if (mResTexts[1].Length() == 0) mMaxValid = 0; + else if (mResTexts[2].Length() == 0) mMaxValid = 1; + else mMaxValid = 2; + return true; + } + return false; + } + + override bool, String GetString(int i) + { + if (i >= SRL_INDEX && i <= SRL_INDEX+2) + { + return true, mResTexts[i-SRL_INDEX]; + } + return false, ""; + } + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_Left) + { + if (--mSelection < 0) mSelection = mMaxValid; + Menu.MenuSound ("menu/cursor"); + return true; + } + else if (mkey == Menu.MKEY_Right) + { + if (++mSelection > mMaxValid) mSelection = 0; + Menu.MenuSound ("menu/cursor"); + return true; + } + else + { + return Super.MenuEvent(mkey, fromcontroller); + } + return false; + } + + override bool MouseEvent(int type, int x, int y) + { + int colwidth = screen.GetWidth() / 3; + mSelection = x / colwidth; + return Super.MouseEvent(type, x, y); + } + + override bool Activate() + { + Menu.MenuSound("menu/choose"); + Menu.SetVideoMode(); + return true; + } + + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + int colwidth = screen.GetWidth() / 3; + int col; + + for (int x = 0; x < 3; x++) + { + if (selected && mSelection == x) + col = OptionMenuSettings.mFontColorSelection; + else if (x == mHighlight) + col = OptionMenuSettings.mFontColorHighlight; + else + col = OptionMenuSettings.mFontColorValue; + + screen.DrawText (SmallFont, col, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true); + } + return colwidth * mSelection + 20 * CleanXfac_1 - CursorSpace(); + } + + override bool Selectable() + { + return mMaxValid >= 0; + } + + override void Ticker() + { + if (Selectable() && mSelection > mMaxValid) + { + mSelection = mMaxValid; + } + } +} + +//============================================================================= +// +// [TP] OptionMenuFieldBase +// +// Base class for input fields +// +//============================================================================= + +class OptionMenuFieldBase : OptionMenuItem +{ + void Init (String label, Name command, CVar graycheck = null) + { + Super.Init(label, command); + mCVar = CVar.FindCVar(mAction); + mGrayCheck = graycheck; + } + + String GetCVarString() + { + if (mCVar == null) + return ""; + + return mCVar.GetString(); + } + + virtual String Represent() + { + return GetCVarString(); + } + + override int Draw (OptionMenuDescriptor d, int y, int indent, bool selected) + { + bool grayed = mGrayCheck != null && !mGrayCheck.GetInt(); + drawLabel(indent, y, selected ? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed); + int overlay = grayed? Color(96, 48, 0, 0) : 0; + + screen.DrawText(SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, Represent(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay); + return indent; + } + + override bool, String GetString (int i) + { + if (i == 0) + { + return true, GetCVarString(); + } + return false, ""; + } + + override bool SetString (int i, String s) + { + if (i == 0) + { + if (mCVar) mCVar.SetString(s); + return true; + } + return false; + } + + + CVar mCVar; + CVar mGrayCheck; +} + +//============================================================================= +// +// [TP] OptionMenuTextField +// +// A text input field widget, for use with string CVars. +// +//============================================================================= + +class OptionMenuItemTextField : OptionMenuFieldBase +{ + TextEnterMenu mEnter; + + OptionMenuItemTextField Init (String label, Name command, CVar graycheck = null) + { + Super.Init(label, command, graycheck); + mEnter = null; + return self; + } + + override String Represent() + { + if (mEnter) return mEnter.GetText() .. SmallFont.GetCursor(); + else return GetCVarString(); + } + + override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected) + { + if (mEnter) + { + // reposition the text so that the cursor is visible when in entering mode. + String text = Represent(); + int tlen = SmallFont.StringWidth(text) * CleanXfac_1; + int newindent = screen.GetWidth() - tlen - CursorSpace(); + if (newindent < indent) indent = newindent; + } + return Super.Draw(desc, y, indent, selected); + } + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_Enter) + { + Menu.MenuSound("menu/choose"); + mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), GetCVarString(), -1, 2, fromcontroller); + return true; + } + else if (mkey == Menu.MKEY_Input) + { + if (mCVar) mCVar.SetString(mEnter.GetText()); + mEnter = null; + return true; + } + else if (mkey == Menu.MKEY_Abort) + { + mEnter = null; + return true; + } + + return Super.MenuEvent(mkey, fromcontroller); + } +} + + +//============================================================================= +// +// [TP] FOptionMenuNumberField +// +// A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e. +// where the user is interested in the exact value specifically) +// +//============================================================================= + +class OptionMenuItemNumberField : OptionMenuFieldBase +{ + OptionMenuItemNumberField Init (String label, Name command, float minimum = 0, float maximum = 100, float step = 1, CVar graycheck = null) + { + Super.Init(label, command, graycheck); + mMinimum = min(minimum, maximum); + mMaximum = max(minimum, maximum); + mStep = max(1, step); + return self; + } + + override String Represent() + { + if (mCVar == null) return ""; + return String.format("%.3f", mCVar.GetFloat()); + } + + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mCVar) + { + float value = mCVar.GetFloat(); + + if (mkey == Menu.MKEY_Left) + { + value -= mStep; + if (value < mMinimum) value = mMaximum; + } + else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter) + { + value += mStep; + if (value > mMaximum) value = mMinimum; + } + else + return Super.MenuEvent(mkey, fromcontroller); + + mCVar.SetFloat(value); + Menu.MenuSound("menu/change"); + } + + return true; + } + + float mMinimum; + float mMaximum; + float mStep; +} + diff --git a/wadsrc/static/zscript/menu/playerdisplay.txt b/wadsrc/static/zscript/menu/playerdisplay.txt new file mode 100644 index 000000000..492ba978c --- /dev/null +++ b/wadsrc/static/zscript/menu/playerdisplay.txt @@ -0,0 +1,290 @@ +/* +** playerdisplay.cpp +** The player display for the player setup and class selection screen +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +//============================================================================= +// +// the player sprite window +// +//============================================================================= + +class ListMenuItemPlayerDisplay : ListMenuItem +{ + ListMenuDescriptor mOwner; + TextureID mBackdrop; + PlayerClass mPlayerClass; + State mPlayerState; + int mPlayerTics; + bool mNoportrait; + int8 mRotation; + int8 mMode; // 0: automatic (used by class selection), 1: manual (used by player setup) + int8 mTranslate; + int mSkin; + int mRandomClass; + int mRandomTimer; + int mClassNum; + int mTranslation; + + enum EPDFlags + { + PDF_ROTATION = 0x10001, + PDF_SKIN = 0x10002, + PDF_CLASS = 0x10003, + PDF_MODE = 0x10004, + PDF_TRANSLATE = 0x10005, + }; + + //============================================================================= + // + // + // + //============================================================================= + void Init(ListMenuDescriptor menu, int x, int y, Color c1, Color c2, bool np = false, Name command = 'None' ) + { + Super.Init(x, y, command); + mOwner = menu; + + Translation trans; + for (int i = 0; i < 256; i++) + { + int r = c1.r + c2.r * i / 255; + int g = c1.g + c2.g * i / 255; + int b = c1.b + c2.b * i / 255; + trans.colors[i] = Color(255, r, g, b); + } + mTranslation = trans.AddTranslation(); + + mBackdrop = TexMan.CheckForTexture("PlayerBackdrop", TexMan.Type_MiscPatch); + mPlayerClass = NULL; + mPlayerState = NULL; + mNoportrait = np; + mMode = 0; + mRotation = 0; + mTranslate = false; + mSkin = 0; + mRandomClass = 0; + mRandomTimer = 0; + mClassNum = -1; + } + + private void UpdatePlayer(int classnum) + { + mPlayerClass = PlayerClasses[classnum]; + mPlayerState = GetDefaultByType (mPlayerClass.Type).SeeState; + if (mPlayerState == NULL) + { // No see state, so try spawn state. + mPlayerState = GetDefaultByType (mPlayerClass.Type).SpawnState; + } + mPlayerTics = mPlayerState != NULL ? mPlayerState.Tics : -1; + } + + //============================================================================= + // + // + // + //============================================================================= + + private void UpdateRandomClass() + { + if (--mRandomTimer < 0) + { + if (++mRandomClass >= PlayerClasses.Size ()) mRandomClass = 0; + UpdatePlayer(mRandomClass); + mPlayerTics = mPlayerState != NULL ? mPlayerState.Tics : -1; + mRandomTimer = 6; + + // Since the newly displayed class may use a different translation + // range than the old one, we need to update the translation, too. + Translation.SetPlayerTranslation(TRANSLATION_Players, MAXPLAYERS, consoleplayer, mPlayerClass); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + void SetPlayerClass(int classnum, bool force = false) + { + if (classnum < 0 || classnum >= PlayerClasses.Size ()) + { + if (mClassNum != -1) + { + mClassNum = -1; + mRandomTimer = 0; + UpdateRandomClass(); + } + } + else if (mPlayerClass != PlayerClasses[classnum] || force) + { + UpdatePlayer(classnum); + mClassNum = classnum; + } + } + + //============================================================================= + // + // + // + //============================================================================= + + bool UpdatePlayerClass() + { + if (mOwner.mSelectedItem >= 0) + { + int classnum; + Name seltype; + + [seltype, classnum] = mOwner.mItems[mOwner.mSelectedItem].GetAction(); + + if (seltype != 'Episodemenu') return false; + if (PlayerClasses.Size() == 0) return false; + + SetPlayerClass(classnum); + return true; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool SetValue(int i, int value) + { + switch (i) + { + case PDF_MODE: + mMode = value; + return true; + + case PDF_ROTATION: + mRotation = value; + return true; + + case PDF_TRANSLATE: + mTranslate = value; + + case PDF_CLASS: + SetPlayerClass(value, true); + break; + + case PDF_SKIN: + mSkin = value; + break; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Ticker() + { + if (mClassNum < 0) UpdateRandomClass(); + + if (mPlayerState != NULL && mPlayerState.Tics != -1 && mPlayerState.NextState != NULL) + { + if (--mPlayerTics <= 0) + { + mPlayerState = mPlayerState.NextState; + mPlayerTics = mPlayerState.Tics; + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer(bool selected) + { + if (mMode == 0 && !UpdatePlayerClass()) + { + return; + } + let playdef = GetDefaultByType((class)(mPlayerClass.Type)); + + Name portrait = playdef.Portrait; + + if (portrait != 'None' && !mNoportrait) + { + TextureID texid = TexMan.CheckForTexture(portrait, TexMan.Type_MiscPatch); + screen.DrawTexture (texid, true, mXpos, mYpos, DTA_Clean, true); + } + else + { + int x = (mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1); + int y = (mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); + + screen.DrawTexture(mBackdrop, false, x, y - 1, + DTA_DestWidth, 72 * CleanXfac, + DTA_DestHeight, 80 * CleanYfac, + DTA_TranslationIndex, mTranslation, + DTA_Masked, true); + + Screen.DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); + + if (mPlayerState != NULL) + { + Vector2 Scale; + TextureID sprite; + bool flip; + + [sprite, flip, Scale] = mPlayerState.GetSpriteTexture(mRotation, mSkin, playdef.Scale); + + if (sprite.IsValid()) + { + int trans = mTranslate? Translation.MakeID(TRANSLATION_Players, MAXPLAYERS) : 0; + let tscale = TexMan.GetScaledSize(sprite); + Scale.X *= CleanXfac * tscale.X; + Scale.Y *= CleanYfac * tscale.Y; + + screen.DrawTexture (sprite, false, + x + 36*CleanXfac, y + 71*CleanYfac, + DTA_DestWidthF, Scale.X, DTA_DestHeightF, Scale.Y, + DTA_TranslationIndex, trans, + DTA_FlipX, flip); + } + } + } + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/playermenu.txt b/wadsrc/static/zscript/menu/playermenu.txt new file mode 100644 index 000000000..389c34043 --- /dev/null +++ b/wadsrc/static/zscript/menu/playermenu.txt @@ -0,0 +1,522 @@ +/* +** playermenu.txt +** The player setup menu +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +//============================================================================= +// +// items for the player menu +// +//============================================================================= + +class ListMenuItemPlayerNameBox : ListMenuItemSelectable +{ + String mText; + Font mFont; + int mFontColor; + int mFrameSize; + String mPlayerName; + TextEnterMenu mEnter; + + //============================================================================= + // + // Player's name + // + //============================================================================= + + void Init(ListMenuDescriptor desc, String text, int frameofs, Name command) + { + Super.Init(desc.mXpos, desc.mYpos, desc.mLinespacing, command); + mText = text; + mFont = desc.mFont; + mFontColor = desc.mFontColor; + mFrameSize = frameofs; + mPlayerName = ""; + mEnter = null; + } + + //============================================================================= + // + // Player's name + // + //============================================================================= + + void InitDirect(int x, int y, int height, int frameofs, String text, Font font, int color, Name command) + { + Super.Init(x, y, height, command); + mText = text; + mFont = font; + mFontColor = color; + mFrameSize = frameofs; + mPlayerName = ""; + mEnter = null; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool SetString(int i, String s) + { + if (i == 0) + { + mPlayerName = s.Mid(0, MAXPLAYERNAME); + return true; + } + return false; + } + + override bool, String GetString(int i) + { + if (i == 0) + { + return true, mPlayerName; + } + return false, ""; + } + + //============================================================================= + // + // [RH] Width of the border is variable + // + //============================================================================= + + protected void DrawBorder (int x, int y, int len) + { + let left = TexMan.CheckForTexture("M_LSLEFT", TexMan.Type_MiscPatch); + let mid = TexMan.CheckForTexture("M_LSCNTR", TexMan.Type_MiscPatch); + let right = TexMan.CheckForTexture("M_LSRGHT", TexMan.Type_MiscPatch); + if (left.IsValid() && right.IsValid() && mid.IsValid()) + { + int i; + + screen.DrawTexture (left, false, x-8, y+7, DTA_Clean, true); + + for (i = 0; i < len; i++) + { + screen.DrawTexture (mid, false, x, y+7, DTA_Clean, true); + x += 8; + } + + screen.DrawTexture (right, false, x, y+7, DTA_Clean, true); + } + else + { + let slot = TexMan.CheckForTexture("M_FSLOT", TexMan.Type_MiscPatch); + if (slot.IsValid()) + { + screen.DrawTexture (slot, false, x, y+1, DTA_Clean, true); + } + else + { + screen.Clear(x, y, x + len, y + SmallFont.GetHeight() * 3/2, 0); + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer(bool selected) + { + String text = StringTable.Localize(mText); + if (text.Length() > 0) + { + screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true); + } + + // Draw player name box + int x = mXpos + mFont.StringWidth(text) + 16 + mFrameSize; + DrawBorder (x, mYpos - mFrameSize, MAXPLAYERNAME+1); + if (!mEnter) + { + screen.DrawText (SmallFont, Font.CR_UNTRANSLATED, x + mFrameSize, mYpos, mPlayerName, DTA_Clean, true); + } + else + { + let printit = mEnter.GetText() .. SmallFont.GetCursor(); + screen.DrawText (SmallFont, Font.CR_UNTRANSLATED, x + mFrameSize, mYpos, printit, DTA_Clean, true); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_Enter) + { + Menu.MenuSound ("menu/choose"); + mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), mPlayerName, MAXPLAYERNAME, 2, fromcontroller); + return true; + } + else if (mkey == Menu.MKEY_Input) + { + mPlayerName = mEnter.GetText(); + mEnter = null; + return true; + } + else if (mkey == Menu.MKEY_Abort) + { + mEnter = null; + return true; + } + return false; + } + +} + +//============================================================================= +// +// items for the player menu +// +//============================================================================= + +class ListMenuItemValueText : ListMenuItemSelectable +{ + Array mSelections; + String mText; + int mSelection; + Font mFont; + int mFontColor; + int mFontColor2; + + //============================================================================= + // + // items for the player menu + // + //============================================================================= + + void Init(ListMenuDescriptor desc, String text, Name command, Name values = 'None') + { + Super.Init(desc.mXpos, desc.mYpos, desc.mLinespacing, command); + mText = text; + mFont = desc.mFont; + mFontColor = desc.mFontColor; + mFontColor2 = desc.mFontColor2; + mSelection = 0; + let cnt = OptionValues.GetCount(values); + for(int i = 0; i < cnt; i++) + { + SetString(i, OptionValues.GetText(values, i)); + } + } + + //============================================================================= + // + // items for the player menu + // + //============================================================================= + + void InitDirect(int x, int y, int height, String text, Font font, int color, int valuecolor, Name command, Name values) + { + Super.Init(x, y, height, command); + mText = text; + mFont = font; + mFontColor = color; + mFontColor2 = valuecolor; + mSelection = 0; + let cnt = OptionValues.GetCount(values); + for(int i = 0; i < cnt; i++) + { + SetString(i, OptionValues.GetText(values, i)); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool SetString(int i, String s) + { + // should actually use the index... + if (i==0) mSelections.Clear(); + mSelections.Push(s); + return true; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool SetValue(int i, int value) + { + if (i == 0) + { + mSelection = value; + return true; + } + return false; + } + + override bool, int GetValue(int i) + { + if (i == 0) + { + return true, mSelection; + } + return false, 0; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mSelections.Size() > 1) + { + if (mkey == Menu.MKEY_Left) + { + Menu.MenuSound("menu/change"); + if (--mSelection < 0) mSelection = mSelections.Size() - 1; + return true; + } + else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter) + { + Menu.MenuSound("menu/change"); + if (++mSelection >= mSelections.Size()) mSelection = 0; + return true; + } + } + return (mkey == Menu.MKEY_Enter); // needs to eat enter keys so that Activate won't get called + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer(bool selected) + { + String text = Stringtable.Localize(mText); + screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true); + + int x = mXpos + mFont.StringWidth(text) + 8; + if (mSelections.Size() > 0) + { + screen.DrawText(mFont, mFontColor2, x, mYpos, mSelections[mSelection], DTA_Clean, true); + } + } + +} + +//============================================================================= +// +// items for the player menu +// +//============================================================================= + +class ListMenuItemSlider : ListMenuItemSelectable +{ + String mText; + Font mFont; + int mFontColor; + int mMinrange, mMaxrange; + int mStep; + int mSelection; + + //============================================================================= + // + // items for the player menu + // + //============================================================================= + + void Init(ListMenuDescriptor desc, String text, Name command, int min, int max, int step) + { + Super.Init(desc.mXpos, desc.mYpos, desc.mLinespacing, command); + mText = text; + mFont = desc.mFont; + mFontColor = desc.mFontColor; + mSelection = 0; + mMinrange = min; + mMaxrange = max; + mStep = step; + } + + //============================================================================= + // + // items for the player menu + // + //============================================================================= + + void InitDirect(int x, int y, int height, String text, Font font, int color, Name command, int min, int max, int step) + { + Super.Init(x, y, height, command); + mText = text; + mFont = font; + mFontColor = color; + mSelection = 0; + mMinrange = min; + mMaxrange = max; + mStep = step; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool SetValue(int i, int value) + { + if (i == 0) + { + mSelection = value; + return true; + } + return false; + } + + override bool, int GetValue(int i) + { + if (i == 0) + { + return true, mSelection; + } + return false, 0; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent (int mkey, bool fromcontroller) + { + if (mkey == Menu.MKEY_Left) + { + Menu.MenuSound("menu/change"); + if ((mSelection -= mStep) < mMinrange) mSelection = mMinrange; + return true; + } + else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter) + { + Menu.MenuSound("menu/change"); + if ((mSelection += mStep) > mMaxrange) mSelection = mMaxrange; + return true; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + let lm = Menu.GetCurrentMenu(); + if (type != Menu.MOUSE_Click) + { + if (!lm.CheckFocus(self)) return false; + } + if (type == Menu.MOUSE_Release) + { + lm.ReleaseFocus(); + } + + int slide_left = SmallFont.StringWidth ("Green") + 8 + mXpos; + int slide_right = slide_left + 12*8; // 12 char cells with 8 pixels each. + + if (type == Menu.MOUSE_Click) + { + if (x < slide_left || x >= slide_right) return true; + } + + x = clamp(x, slide_left, slide_right); + int v = mMinrange + (x - slide_left) * (mMaxrange - mMinrange) / (slide_right - slide_left); + if (v != mSelection) + { + mSelection = v; + Menu.MenuSound("menu/change"); + } + if (type == Menu.MOUSE_Click) + { + lm.SetFocus(self); + } + return true; + } + + //============================================================================= + // + // + // + //============================================================================= + + protected void DrawSlider (int x, int y) + { + int range = mMaxrange - mMinrange; + int cur = mSelection - mMinrange; + + x = (x - 160) * CleanXfac + screen.GetWidth() / 2; + y = (y - 100) * CleanYfac + screen.GetHeight() / 2; + + screen.DrawText (ConFont, Font.CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12", DTA_CellX, 8 * CleanXfac, DTA_CellY, 8 * CleanYfac); + screen.DrawText (ConFont, Font.CR_ORANGE, x + (5 + (int)((cur * 78) / range)) * CleanXfac, y, "\x13", DTA_CellX, 8 * CleanXfac, DTA_CellY, 8 * CleanYfac); + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer(bool selected) + { + String text = StringTable.Localize(mText); + + screen.DrawText(mFont, selected? OptionMenuSettings.mFontColorSelection : mFontColor, mXpos, mYpos, text, DTA_Clean, true); + + int x = SmallFont.StringWidth ("Green") + 8 + mXpos; + int x2 = SmallFont.StringWidth (text) + 8 + mXpos; + DrawSlider (MAX(x2, x), mYpos); + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/textentermenu.txt b/wadsrc/static/zscript/menu/textentermenu.txt new file mode 100644 index 000000000..7974a62bd --- /dev/null +++ b/wadsrc/static/zscript/menu/textentermenu.txt @@ -0,0 +1,15 @@ + +// This is only the parts that are needed to make the menu fully work right now. More to come later. +class TextEnterMenu : Menu native +{ + native bool mInputGridOkay; + + native static TextEnterMenu Open(Menu parent, String text, int maxlen, int sizemode, bool fromcontroller); + native String GetText(); + + + override bool TranslateKeyboardEvents() + { + return mInputGridOkay; + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/menu/videomenu.txt b/wadsrc/static/zscript/menu/videomenu.txt new file mode 100644 index 000000000..a162781eb --- /dev/null +++ b/wadsrc/static/zscript/menu/videomenu.txt @@ -0,0 +1,73 @@ +/* +** videomenu.txt +** The video modes menu +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +//============================================================================= +// +// +// +//============================================================================= + +class VideoModeMenu : OptionMenu +{ + native static bool SetSelectedSize(); + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if ((mkey == MKEY_Up || mkey == MKEY_Down) && mDesc.mSelectedItem >= 0 && mDesc.mSelectedItem < mDesc.mItems.Size()) + { + int sel; + bool selected; + [selected, sel] = mDesc.mItems[mDesc.mSelectedItem].GetValue(OptionMenuItemScreenResolution.SRL_SELECTION); + bool res = Super.MenuEvent(mkey, fromcontroller); + if (selected) mDesc.mItems[mDesc.mSelectedItem].SetValue(OptionMenuItemScreenResolution.SRL_SELECTION, sel); + return res; + } + return Super.MenuEvent(mkey, fromcontroller); + } + + override bool Responder(InputEvent ev) + { + if (ev.type == InputEvent.GUI_Event && ev.subtype == InputEvent.GUI_KeyDown && (ev.data1 == 0x54 || ev.data1 == 0x74)) + { + if (SetSelectedSize()) + { + MenuSound ("menu/choose"); + return true; + } + } + return Super.Responder(ev); + } +} + diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index dc1d1ba1d..7b8ba8f30 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -313,3 +313,12 @@ userinfo_t userinfo; native void BringUpWeapon(); } + +struct PlayerClass native +{ + native class Type; + native uint Flags; + native Array Skins; + + native bool CheckSkin(int skin); +} diff --git a/wadsrc/static/zscript/strife/coin.txt b/wadsrc/static/zscript/strife/coin.txt index b94a3f1d1..b6a83ecf2 100644 --- a/wadsrc/static/zscript/strife/coin.txt +++ b/wadsrc/static/zscript/strife/coin.txt @@ -31,7 +31,7 @@ class Coin : Inventory } else { - String msg = StringTable.Localize("TXT_XGOLD"); + String msg = StringTable.Localize("$TXT_XGOLD"); msg.Replace("%d", "" .. Amount); return msg; } diff --git a/wadsrc/static/zscript/strife/thingstoblowup.txt b/wadsrc/static/zscript/strife/thingstoblowup.txt index bb268838c..a2b515efb 100644 --- a/wadsrc/static/zscript/strife/thingstoblowup.txt +++ b/wadsrc/static/zscript/strife/thingstoblowup.txt @@ -27,7 +27,7 @@ extend class Actor } } - String msgid = "TXT_QUEST_" .. questitem; + String msgid = "$TXT_QUEST_" .. questitem; String msg = StringTable.Localize(msgid); if (msg != msgid) // if both are identical there was no message of this name in the stringtable.