diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16a658a10..b811e8a59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -649,13 +649,13 @@ add_executable( zdoom WIN32 hu_scores.cpp i_net.cpp info.cpp + keysections.cpp lumpconfigfile.cpp m_alloc.cpp m_argv.cpp m_bbox.cpp m_cheat.cpp m_joy.cpp - m_menu.cpp m_misc.cpp m_options.cpp m_png.cpp @@ -788,6 +788,19 @@ add_executable( zdoom WIN32 g_shared/sbar_mugshot.cpp g_shared/shared_hud.cpp g_shared/shared_sbar.cpp + menu/colorpickermenu.cpp + menu/joystickmenu.cpp + menu/listmenu.cpp + menu/loadsavemenu.cpp + menu/menu.cpp + menu/menudef.cpp + menu/menuinput.cpp + menu/messagebox.cpp + menu/optionmenu.cpp + menu/playerdisplay.cpp + menu/playermenu.cpp + menu/readthis.cpp + menu/videomenu.cpp oplsynth/fmopl.cpp oplsynth/mlopl.cpp oplsynth/mlopl_io.cpp diff --git a/src/c_bind.cpp b/src/c_bind.cpp index b9489ca65..cceb8bd35 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -583,7 +583,7 @@ void FKeyBindings::ArchiveBindings(FConfigFile *f, const char *matchcmd) // //============================================================================= -int FKeyBindings::GetKeysForCommand (char *cmd, int *first, int *second) +int FKeyBindings::GetKeysForCommand (const char *cmd, int *first, int *second) { int c, i; @@ -609,7 +609,7 @@ int FKeyBindings::GetKeysForCommand (char *cmd, int *first, int *second) // //============================================================================= -void FKeyBindings::UnbindACommand (char *str) +void FKeyBindings::UnbindACommand (const char *str) { int i; diff --git a/src/c_bind.h b/src/c_bind.h index 7d71b462a..4c9edb2bb 100644 --- a/src/c_bind.h +++ b/src/c_bind.h @@ -57,8 +57,8 @@ public: void SetBinds(const FBinding *binds); bool DoKey(event_t *ev); void ArchiveBindings(FConfigFile *F, const char *matchcmd = NULL); - int GetKeysForCommand (char *cmd, int *first, int *second); - void UnbindACommand (char *str); + int GetKeysForCommand (const char *cmd, int *first, int *second); + void UnbindACommand (const char *str); void UnbindAll (); void UnbindKey(const char *key); void DoBind (const char *key, const char *bind); @@ -85,6 +85,7 @@ public: extern FKeyBindings Bindings; extern FKeyBindings DoubleBindings; extern FKeyBindings AutomapBindings; +extern FKeyBindings MenuBindings; bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds); @@ -95,4 +96,18 @@ void C_UnbindAll (); extern const char *KeyNames[]; +struct FKeyAction +{ + FString mTitle; + FString mAction; +}; + +struct FKeySection +{ + FString mTitle; + FString mSection; + TArray mActions; +}; +extern TArray KeySections; + #endif //__C_BINDINGS_H__ diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 6d21a7922..f3cb40958 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -936,3 +936,4 @@ CCMD(currentpos) FIXED2FLOAT(mo->x), FIXED2FLOAT(mo->y), FIXED2FLOAT(mo->z), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel); } + diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index d4e01128d..7b5387635 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -1119,7 +1119,7 @@ void FFlagCVar::DoSet (UCVarValue value, ECVarType type) // exec scripts because all flags will base their changes off of the value of // the "master" cvar at the time the script was run, overriding any changes // another flag might have made to the same cvar earlier in the script. - if ((ValueVar.Flags & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) + if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) { if (netgame && !players[consoleplayer].settings_controller) { @@ -1139,6 +1139,114 @@ void FFlagCVar::DoSet (UCVarValue value, ECVarType type) } } +// +// Mask cvar implementation +// +// Similar to FFlagCVar but can have multiple bits +// + +FMaskCVar::FMaskCVar (const char *name, FIntCVar &realvar, DWORD bitval) +: FBaseCVar (name, 0, NULL), +ValueVar (realvar), +BitVal (bitval) +{ + int bit; + + Flags &= ~CVAR_ISDEFAULT; + + assert (bitval != 0); + + bit = 0; + while ((bitval & 1) == 0) + { + ++bit; + bitval >>= 1; + } + BitNum = bit; +} + +ECVarType FMaskCVar::GetRealType () const +{ + return CVAR_Dummy; +} + +UCVarValue FMaskCVar::GetGenericRep (ECVarType type) const +{ + return FromInt ((ValueVar & BitVal) >> BitNum, type); +} + +UCVarValue FMaskCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Int; + ret.Int = (ValueVar & BitVal) >> BitNum; + return ret; +} + +UCVarValue FMaskCVar::GetGenericRepDefault (ECVarType type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + return FromInt ((def.Int & BitVal) >> BitNum, type); +} + +UCVarValue FMaskCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + def.Int = (def.Int & BitVal) >> BitNum; + *type = CVAR_Int; + return def; +} + +void FMaskCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + int val = ToInt(value, type) << BitNum; + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + def.Int &= ~BitVal; + def.Int |= val; + ValueVar.SetGenericRepDefault (def, CVAR_Int); +} + +void FMaskCVar::DoSet (UCVarValue value, ECVarType type) +{ + int val = ToInt(value, type) << BitNum; + + // Server cvars that get changed by this need to use a special message, because + // changes are not processed until the next net update. This is a problem with + // exec scripts because all flags will base their changes off of the value of + // the "master" cvar at the time the script was run, overriding any changes + // another flag might have made to the same cvar earlier in the script. + if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) + { + if (netgame && !players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can change %s\n", Name); + return; + } + // Ugh... + for(int i = 0; i < 32; i++) + { + if (BitVal & (1<> BitNum; } + inline int operator *() const { return (ValueVar & BitVal) >> BitNum; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + FIntCVar &ValueVar; + uint32 BitVal; + int BitNum; +}; + class FGUIDCVar : public FBaseCVar { public: diff --git a/src/d_gui.h b/src/d_gui.h index 5e9854f7c..b88041771 100644 --- a/src/d_gui.h +++ b/src/d_gui.h @@ -43,27 +43,34 @@ enum EGUIEvent EV_GUI_KeyRepeat, // same EV_GUI_KeyUp, // same EV_GUI_Char, // data1: translated character (for user text input), data2: alt down? - EV_GUI_MouseMove, - EV_GUI_LButtonDown, - EV_GUI_LButtonUp, - EV_GUI_LButtonDblClick, - EV_GUI_MButtonDown, - EV_GUI_MButtonUp, - EV_GUI_MButtonDblClick, - EV_GUI_RButtonDown, - EV_GUI_RButtonUp, - EV_GUI_RButtonDblClick, - EV_GUI_WheelUp, // data3: shift/ctrl/alt - EV_GUI_WheelDown, // " - EV_GUI_WheelRight, // " - EV_GUI_WheelLeft, // " + EV_GUI_FirstMouseEvent, + EV_GUI_MouseMove, + EV_GUI_LButtonDown, + EV_GUI_LButtonUp, + EV_GUI_LButtonDblClick, + EV_GUI_MButtonDown, + EV_GUI_MButtonUp, + EV_GUI_MButtonDblClick, + EV_GUI_RButtonDown, + EV_GUI_RButtonUp, + EV_GUI_RButtonDblClick, + EV_GUI_WheelUp, // data3: shift/ctrl/alt + EV_GUI_WheelDown, // " + EV_GUI_WheelRight, // " + EV_GUI_WheelLeft, // " + EV_GUI_BackButtonDown, + EV_GUI_BackButtonUp, + EV_GUI_FwdButtonDown, + EV_GUI_FwdButtonUp, + EV_GUI_LastMouseEvent, }; enum GUIKeyModifiers { GKM_SHIFT = 1, GKM_CTRL = 2, - GKM_ALT = 4 + GKM_ALT = 4, + GKM_LBUTTON = 8 }; // Special codes for some GUI keys, including a few real ASCII codes. @@ -100,7 +107,7 @@ enum ESpecialGUIKeys GK_ESCAPE = 27, // ASCII GK_FREE1 = 28, GK_FREE2 = 29, - GK_FREE3 = 30, + GK_BACK = 30, // browser back key GK_CESCAPE = 31 // color escape }; diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 9a49c533b..499d7cbd7 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -79,7 +79,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "FreeDM", "FreeDM", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, { "Blasphemer", "Blasphemer",MAKERGB(115,0,0), MAKERGB(0,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, { "Chex(R) Quest", "Chex1", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex.txt" }, - { "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt" }, + { "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt", GI_NOTEXTCOLOR }, { "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, { "Harmony", "Harmony", MAKERGB(110,180,230), MAKERGB(69,79,126), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, //{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) }, diff --git a/src/d_main.cpp b/src/d_main.cpp index 19ed60437..9dc18f473 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -61,7 +61,7 @@ #include "f_wipe.h" #include "m_argv.h" #include "m_misc.h" -#include "m_menu.h" +#include "menu/menu.h" #include "c_console.h" #include "c_dispatch.h" #include "i_system.h" @@ -392,6 +392,18 @@ CVAR (Flag, sv_nofov, dmflags, DF_NO_FOV); CVAR (Flag, sv_noweaponspawn, dmflags, DF_NO_COOP_WEAPON_SPAWN); CVAR (Flag, sv_nocrouch, dmflags, DF_NO_CROUCH); CVAR (Flag, sv_allowcrouch, dmflags, DF_YES_CROUCH); +CVAR (Flag, sv_cooploseinventory, dmflags, DF_COOP_LOSE_INVENTORY); +CVAR (Flag, sv_cooplosekeys, dmflags, DF_COOP_LOSE_KEYS); +CVAR (Flag, sv_cooploseweapons, dmflags, DF_COOP_LOSE_WEAPONS); +CVAR (Flag, sv_cooplosearmor, dmflags, DF_COOP_LOSE_ARMOR); +CVAR (Flag, sv_cooplosepowerups, dmflags, DF_COOP_LOSE_POWERUPS); +CVAR (Flag, sv_cooploseammo, dmflags, DF_COOP_LOSE_AMMO); +CVAR (Flag, sv_coophalveammo, dmflags, DF_COOP_HALVE_AMMO); + +// Some (hopefully cleaner) interface to these settings. +CVAR (Mask, sv_crouch, dmflags, DF_NO_CROUCH|DF_YES_CROUCH); +CVAR (Mask, sv_jump, dmflags, DF_NO_JUMP|DF_YES_JUMP); +CVAR (Mask, sv_fallingdamage, dmflags, DF_FORCE_FALLINGHX|DF_FORCE_FALLINGZD); //========================================================================== // @@ -462,7 +474,6 @@ CVAR (Flag, sv_disallowsuicide, dmflags2, DF2_NOSUICIDE); CVAR (Flag, sv_noautoaim, dmflags2, DF2_NOAUTOAIM); CVAR (Flag, sv_dontcheckammo, dmflags2, DF2_DONTCHECKAMMO); CVAR (Flag, sv_killbossmonst, dmflags2, DF2_KILLBOSSMONST); - //========================================================================== // // CVAR compatflags @@ -888,6 +899,8 @@ void D_DoomLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; + I_SetCursor(TexMan["cursor"]); + for (;;) { try @@ -2077,6 +2090,7 @@ void D_DoomMain (void) // [GRB] Initialize player class list SetupPlayerClasses (); + // [RH] Load custom key and weapon settings from WADs D_LoadWadSettings (); @@ -2146,7 +2160,7 @@ void D_DoomMain (void) bglobal.spawn_tries = 0; bglobal.wanted_botnum = bglobal.getspawned.Size(); - Printf ("M_Init: Init miscellaneous info.\n"); + Printf ("M_Init: Init menus.\n"); M_Init (); Printf ("P_Init: Init Playloop state.\n"); diff --git a/src/d_net.cpp b/src/d_net.cpp index 73d800b51..e08899719 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -25,7 +25,7 @@ #include #include "version.h" -#include "m_menu.h" +#include "menu/menu.h" #include "m_random.h" #include "i_system.h" #include "i_video.h" diff --git a/src/d_player.h b/src/d_player.h index 28b240c10..2d58e00ee 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -54,6 +54,7 @@ enum APMETA_ColorRange, // skin color range APMETA_InvulMode, APMETA_HealingRadius, + APMETA_Portrait, APMETA_Hexenarmor0, APMETA_Hexenarmor1, APMETA_Hexenarmor2, diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 4d7589e9b..4314d0488 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -72,6 +72,7 @@ #include "doomstat.h" #include "m_argv.h" #include "po_man.h" +#include "menu/menu.h" // MACROS ------------------------------------------------------------------ @@ -298,6 +299,7 @@ static void MarkRoot() Mark(Args); Mark(screen); Mark(StatusBar); + Mark(DMenu::CurrentMenu); DThinker::MarkRoots(); FCanvasTextureInfo::Mark(); Mark(DACSThinker::ActiveThinker); diff --git a/src/g_game.cpp b/src/g_game.cpp index 39a424097..caf43cd6f 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -40,7 +40,7 @@ #include "f_finale.h" #include "m_argv.h" #include "m_misc.h" -#include "m_menu.h" +#include "menu/menu.h" #include "m_random.h" #include "m_crc32.h" #include "i_system.h" @@ -128,6 +128,8 @@ CUSTOM_CVAR (Int, displaynametags, 0, CVAR_ARCHIVE) } } +CVAR(Int, nametagcolor, CR_GOLD, CVAR_ARCHIVE) + gameaction_t gameaction; gamestate_t gamestate = GS_STARTUP; @@ -326,7 +328,7 @@ CCMD (weapnext) if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(), - 1.5f, 0.90f, 0, 0, CR_GOLD, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' )); + 1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' )); } } @@ -337,7 +339,7 @@ CCMD (weapprev) if ((displaynametags & 2) && StatusBar && SmallFont && SendItemUse) { StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, SendItemUse->GetTag(), - 1.5f, 0.90f, 0, 0, CR_GOLD, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' )); + 1.5f, 0.90f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID( 'W', 'E', 'P', 'N' )); } } @@ -368,7 +370,7 @@ CCMD (invnext) } if ((displaynametags & 1) && StatusBar && SmallFont && who->InvSel) StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, who->InvSel->GetTag(), - 1.5f, 0.80f, 0, 0, CR_GOLD, 2.f, 0.35f), MAKE_ID('S','I','N','V')); + 1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V')); } who->player->inventorytics = 5*TICRATE; } @@ -398,7 +400,7 @@ CCMD (invprev) } if ((displaynametags & 1) && StatusBar && SmallFont && who->InvSel) StatusBar->AttachMessage (new DHUDMessageFadeOut (SmallFont, who->InvSel->GetTag(), - 1.5f, 0.80f, 0, 0, CR_GOLD, 2.f, 0.35f), MAKE_ID('S','I','N','V')); + 1.5f, 0.80f, 0, 0, (EColorRange)*nametagcolor, 2.f, 0.35f), MAKE_ID('S','I','N','V')); } who->player->inventorytics = 5*TICRATE; } @@ -897,7 +899,8 @@ bool G_Responder (event_t *ev) stricmp (cmd, "bumpgamma") && stricmp (cmd, "screenshot"))) { - M_StartControlPanel (true, true); + M_StartControlPanel(true); + M_SetMenu(NAME_Mainmenu, -1); return true; } else diff --git a/src/g_level.cpp b/src/g_level.cpp index 8f475d3f3..10ed1f83e 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -68,7 +68,6 @@ #include "m_png.h" #include "m_random.h" #include "version.h" -#include "m_menu.h" #include "statnums.h" #include "sbarinfo.h" #include "r_translate.h" @@ -78,6 +77,7 @@ #include "d_net.h" #include "d_netinf.h" #include "v_palette.h" +#include "menu/menu.h" #include "gi.h" @@ -99,6 +99,7 @@ void STAT_WRITE(FILE *f); EXTERN_CVAR (Float, sv_gravity) EXTERN_CVAR (Float, sv_aircontrol) EXTERN_CVAR (Int, disableautosave) +EXTERN_CVAR (String, playerclass) #define SNAP_ID MAKE_ID('s','n','A','p') #define DSNP_ID MAKE_ID('d','s','N','p') @@ -230,6 +231,15 @@ void G_DeferedInitNew (const char *mapname, int newskill) gameaction = ga_newgame2; } +void G_DeferedInitNew (FGameStartup *gs) +{ + playerclass = gs->PlayerClass; + d_mapname = AllEpisodes[gs->Episode].mEpisodeMap; + d_skill = gs->Skill; + CheckWarpTransMap (d_mapname, true); + gameaction = ga_newgame2; +} + //========================================================================== // // diff --git a/src/g_level.h b/src/g_level.h index f316491d5..cf7fd2d48 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -486,6 +486,8 @@ void G_InitNew (const char *mapname, bool bTitleLevel); // A normal game starts at map 1, // but a warp test can start elsewhere void G_DeferedInitNew (const char *mapname, int skill = -1); +struct FGameStartup; +void G_DeferedInitNew (FGameStartup *gs); void G_ExitLevel (int position, bool keepFacing); void G_SecretExitLevel (int position); @@ -603,6 +605,16 @@ struct FSkillInfo extern TArray AllSkills; extern int DefaultSkill; +struct FEpisode +{ + FString mEpisodeName; + FString mEpisodeMap; + FString mPicName; + char mShortcut; + bool mNoSkill; +}; + +extern TArray AllEpisodes; #endif //__G_LEVEL_H__ diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 9378db768..6b3faa78a 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -38,7 +38,6 @@ #include "g_level.h" #include "sc_man.h" #include "w_wad.h" -#include "m_menu.h" #include "cmdlib.h" #include "v_video.h" #include "p_lnspec.h" @@ -63,6 +62,8 @@ TArray wadlevelinfos; level_info_t TheDefaultLevelInfo; static cluster_info_t TheDefaultClusterInfo; +TArray AllEpisodes; + //========================================================================== // // @@ -1655,10 +1656,10 @@ level_info_t *FMapInfoParser::ParseMapHeader(level_info_t &defaultinfo) void FMapInfoParser::ParseEpisodeInfo () { - int i; + unsigned int i; char map[9]; - char *pic = NULL; - bool picisgfx = false; // Shut up, GCC!!!! + FString pic; + FString name; bool remove = false; char key = 0; bool noskill = false; @@ -1697,15 +1698,13 @@ void FMapInfoParser::ParseEpisodeInfo () { ParseAssign(); sc.MustGetString (); - ReplaceString (&pic, sc.String); - picisgfx = false; + name = sc.String; } else if (sc.Compare ("picname")) { ParseAssign(); sc.MustGetString (); - ReplaceString (&pic, sc.String); - picisgfx = true; + pic = sc.String; } else if (sc.Compare ("remove")) { @@ -1751,9 +1750,9 @@ void FMapInfoParser::ParseEpisodeInfo () } - for (i = 0; i < EpiDef.numitems; ++i) + for (i = 0; i < AllEpisodes.Size(); i++) { - if (strncmp (EpisodeMaps[i], map, 8) == 0) + if (AllEpisodes[i].mEpisodeMap.CompareNoCase(map) == 0) { break; } @@ -1762,50 +1761,17 @@ void FMapInfoParser::ParseEpisodeInfo () if (remove) { // If the remove property is given for an episode, remove it. - if (i < EpiDef.numitems) - { - if (i+1 < EpiDef.numitems) - { - memmove (&EpisodeMaps[i], &EpisodeMaps[i+1], - sizeof(EpisodeMaps[0])*(EpiDef.numitems - i - 1)); - memmove (&EpisodeMenu[i], &EpisodeMenu[i+1], - sizeof(EpisodeMenu[0])*(EpiDef.numitems - i - 1)); - memmove (&EpisodeNoSkill[i], &EpisodeNoSkill[i+1], - sizeof(EpisodeNoSkill[0])*(EpiDef.numitems - i - 1)); - } - EpiDef.numitems--; - } + AllEpisodes.Delete(i); } else { - if (pic == NULL) - { - pic = copystring (map); - picisgfx = false; - } + FEpisode *epi = &AllEpisodes[AllEpisodes.Reserve(1)]; - if (i == EpiDef.numitems) - { - if (EpiDef.numitems == MAX_EPISODES) - { - i = EpiDef.numitems - 1; - } - else - { - i = EpiDef.numitems++; - } - } - else - { - delete[] const_cast(EpisodeMenu[i].name); - } - - EpisodeMenu[i].name = pic; - EpisodeMenu[i].alphaKey = tolower(key); - EpisodeMenu[i].fulltext = !picisgfx; - EpisodeNoSkill[i] = noskill; - strncpy (EpisodeMaps[i], map, 8); - EpisodeMaps[i][8] = 0; + epi->mEpisodeMap = map; + epi->mEpisodeName = name; + epi->mPicName = pic; + epi->mShortcut = tolower(key); + epi->mNoSkill = noskill; } } @@ -1818,12 +1784,7 @@ void FMapInfoParser::ParseEpisodeInfo () void ClearEpisodes() { - for (int i = 0; i < EpiDef.numitems; ++i) - { - delete[] const_cast(EpisodeMenu[i].name); - EpisodeMenu[i].name = NULL; - } - EpiDef.numitems = 0; + AllEpisodes.Clear(); } //========================================================================== @@ -2004,7 +1965,7 @@ void G_ParseMapInfo (const char *basemapinfo) } EndSequences.ShrinkToFit (); - if (EpiDef.numitems == 0) + if (AllEpisodes.Size() == 0) { I_FatalError ("You cannot use clearepisodes in a MAPINFO if you do not define any new episodes after it."); } diff --git a/src/gi.cpp b/src/gi.cpp index 03fea70a9..190d2b031 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -103,12 +103,11 @@ const char* GameInfoBoarders[] = do \ { \ sc.MustGetToken(TK_StringConst); \ - if(strlen(sc.String) > length) \ + if(length > 0 && strlen(sc.String) > length) \ { \ sc.ScriptError("Value for '%s' can not be longer than %d characters.", #key, length); \ } \ - FName val = sc.String; \ - gameinfo.key.Push(val); \ + gameinfo.key[gameinfo.key.Reserve(1)] = sc.String; \ } \ while (sc.CheckToken(',')); \ } @@ -283,6 +282,16 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_INT(defaultdropstyle, "defaultdropstyle") GAMEINFOKEY_CSTRING(Endoom, "endoom", 8) GAMEINFOKEY_INT(player5start, "player5start") + GAMEINFOKEY_STRINGARRAY(quitmessages, "quitmessages", 0) + GAMEINFOKEY_STRING(mTitleColor, "menufontcolor_title") + GAMEINFOKEY_STRING(mFontColor, "menufontcolor_label") + GAMEINFOKEY_STRING(mFontColorValue, "menufontcolor_value") + GAMEINFOKEY_STRING(mFontColorMore, "menufontcolor_action") + GAMEINFOKEY_STRING(mFontColorHeader, "menufontcolor_header") + GAMEINFOKEY_STRING(mFontColorHighlight, "menufontcolor_highlight") + GAMEINFOKEY_STRING(mFontColorSelection, "menufontcolor_selection") + GAMEINFOKEY_CSTRING(mBackButton, "menubackbutton", 8) + else { // ignore unkown keys. diff --git a/src/gi.h b/src/gi.h index cbb1a02bb..ef9777553 100644 --- a/src/gi.h +++ b/src/gi.h @@ -46,6 +46,7 @@ #define GI_COMPATSTAIRS 0x00000020 // same for stairbuilding #define GI_COMPATPOLY1 0x00000040 // Hexen's MAP36 needs old polyobject drawing #define GI_COMPATPOLY2 0x00000080 // so does HEXDD's MAP47 +#define GI_NOTEXTCOLOR 0x00000100 #include "gametype.h" @@ -109,6 +110,15 @@ struct gameinfo_t int defaultdropstyle; int player5start; DWORD pickupcolor; + TArray quitmessages; + FName mTitleColor; + FName mFontColor; + FName mFontColorValue; + FName mFontColorMore; + FName mFontColorHeader; + FName mFontColorHighlight; + FName mFontColorSelection; + char mBackButton[9]; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/keysections.cpp b/src/keysections.cpp new file mode 100644 index 000000000..3da17fb38 --- /dev/null +++ b/src/keysections.cpp @@ -0,0 +1,146 @@ +/* +** keysections.cpp +** Custom key bindings +** +**--------------------------------------------------------------------------- +** Copyright 1998-2009 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include "menu/menu.h" +#include "g_level.h" +#include "d_player.h" +#include "gi.h" +#include "c_bind.h" +#include "c_dispatch.h" +#include "gameconfigfile.h" + +TArray KeySections; + +static void LoadKeys (const char *modname, bool dbl) +{ + char section[64]; + + if (GameNames[gameinfo.gametype] == NULL) + return; + + mysnprintf (section, countof(section), "%s.%s%sBindings", GameNames[gameinfo.gametype], modname, + dbl ? ".Double" : "."); + + FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; + if (GameConfig->SetSection (section)) + { + const char *key, *value; + while (GameConfig->NextInSection (key, value)) + { + bindings->DoBind (key, value); + } + } +} + +static void DoSaveKeys (FConfigFile *config, const char *section, FKeySection *keysection, bool dbl) +{ + config->SetSection (section, true); + config->ClearCurrentSection (); + FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; + for (unsigned i = 0; i < keysection->mActions.Size(); ++i) + { + bindings->ArchiveBindings (config, keysection->mActions[i].mAction); + } +} + +void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen) +{ + for (unsigned i=0; i \n"); + return; + } + + // Limit the ini name to 32 chars + if (strlen (argv[2]) > 32) + argv[2][32] = 0; + + for (unsigned i = 0; i < KeySections.Size(); i++) + { + if (KeySections[i].mTitle.CompareNoCase(argv[2] == 0)) + { + CurrentKeySection = i; + return; + } + } + + CurrentKeySection = KeySections.Reserve(1); + KeySections[CurrentKeySection].mTitle = argv[1]; + KeySections[CurrentKeySection].mSection = argv[2]; + // Load bindings for this section from the ini + LoadKeys (argv[2], 0); + LoadKeys (argv[2], 1); + } +} + +CCMD (addmenukey) +{ + if (ParsingKeyConf) + { + if (argv.argc() != 3) + { + Printf ("Usage: addmenukey \n"); + return; + } + if (CurrentKeySection == -1 || CurrentKeySection >= (int)KeySections.Size()) + { + Printf ("You must use addkeysection first.\n"); + return; + } + + FKeySection *sect = &KeySections[CurrentKeySection]; + + FKeyAction *act = §->mActions[sect->mActions.Reserve(1)]; + act->mTitle = argv[1]; + act->mAction = argv[2]; + } +} + diff --git a/src/m_joy.h b/src/m_joy.h index 2c939f679..00537dee8 100644 --- a/src/m_joy.h +++ b/src/m_joy.h @@ -60,5 +60,6 @@ double Joy_RemoveDeadZone(double axisval, double deadzone, BYTE *buttons); void I_GetAxes(float axes[NUM_JOYAXIS]); void I_GetJoysticks(TArray &sticks); IJoystickConfig *I_UpdateDeviceList(); +extern void UpdateJoystickMenu(IJoystickConfig *); #endif diff --git a/src/m_menu.cpp b/src/m_menu.cpp deleted file mode 100644 index b3e28b88e..000000000 --- a/src/m_menu.cpp +++ /dev/null @@ -1,4152 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// DOOM selection menu, options, episode etc. -// Sliders and icons. Kinda widget stuff. -// -//----------------------------------------------------------------------------- - -// HEADER FILES ------------------------------------------------------------ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#else -#include -#include -#include -#endif - -#include "doomdef.h" -#include "gstrings.h" -#include "c_console.h" -#include "c_dispatch.h" -#include "d_main.h" -#include "i_system.h" -#include "i_video.h" -#include "v_video.h" -#include "v_palette.h" -#include "w_wad.h" -#include "r_local.h" -#include "hu_stuff.h" -#include "g_game.h" -#include "m_argv.h" -#include "m_swap.h" -#include "m_random.h" -#include "s_sound.h" -#include "doomstat.h" -#include "m_menu.h" -#include "v_text.h" -#include "st_stuff.h" -#include "d_gui.h" -#include "version.h" -#include "m_png.h" -#include "templates.h" -#include "lists.h" -#include "gi.h" -#include "p_tick.h" -#include "st_start.h" -#include "teaminfo.h" -#include "r_translate.h" -#include "g_level.h" -#include "d_event.h" -#include "colormatcher.h" -#include "d_netinf.h" - -// MACROS ------------------------------------------------------------------ - -#define SKULLXOFF -32 -#define SELECTOR_XOFFSET (-28) -#define SELECTOR_YOFFSET (-1) - -#define KEY_REPEAT_DELAY (TICRATE*5/12) -#define KEY_REPEAT_RATE (3) - -#define INPUTGRID_WIDTH 13 -#define INPUTGRID_HEIGHT 5 - -// TYPES ------------------------------------------------------------------- - -struct FSaveGameNode : public Node -{ - char Title[SAVESTRINGSIZE]; - FString Filename; - bool bOldVersion; - bool bMissingWads; -}; - -struct FBackdropTexture : public FTexture -{ -public: - FBackdropTexture(); - - const BYTE *GetColumn(unsigned int column, const Span **spans_out); - const BYTE *GetPixels(); - void Unload(); - bool CheckModified(); - -protected: - BYTE Pixels[144*160]; - static const Span DummySpan[2]; - int LastRenderTic; - - angle_t time1, time2, time3, time4; - angle_t t1ang, t2ang, z1ang, z2ang; - - void Render(); -}; - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayerSkin *skin, FRemapTable *table); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -int M_StringHeight (const char *string); -void M_ClearMenus (); - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void M_NewGame (int choice); -static void M_Episode (int choice); -static void M_ChooseSkill (int choice); -static void M_LoadGame (int choice); -static void M_SaveGame (int choice); -static void M_Options (int choice); -static void M_EndGame (int choice); -static void M_ReadThis (int choice); -static void M_ReadThisMore (int choice); -static void M_QuitGame (int choice); -static void M_GameFiles (int choice); -static void M_ClearSaveStuff (); - -static void SCClass (int choice); -static void M_ChooseClass (int choice); - -static void M_FinishReadThis (int choice); -static void M_QuickSave (); -static void M_QuickLoad (); -static void M_LoadSelect (const FSaveGameNode *file); -static void M_SaveSelect (const FSaveGameNode *file); -static void M_ReadSaveStrings (); -static void M_UnloadSaveStrings (); -static FSaveGameNode *M_RemoveSaveSlot (FSaveGameNode *file); -static void M_ExtractSaveData (const FSaveGameNode *file); -static void M_UnloadSaveData (); -static void M_InsertSaveNode (FSaveGameNode *node); -static bool M_SaveLoadResponder (event_t *ev); -static void M_SaveLoadButtonHandler(EMenuKey key); -static void M_DeleteSaveResponse (int choice); - -static void M_DrawMainMenu (); -static void M_DrawReadThis (); -static void M_DrawNewGame (); -static void M_DrawEpisode (); -static void M_DrawLoad (); -static void M_DrawSave (); -static void DrawClassMenu (); -static void DrawHexenSkillMenu (); -static void M_DrawClassMenu (); - -static void M_DrawHereticMainMenu (); -static void M_DrawFiles (); - -void M_DrawFrame (int x, int y, int width, int height); -static void M_DrawSaveLoadBorder (int x,int y, int len); -static void M_DrawSaveLoadCommon (); -static void M_DrawInputGrid(); - -static void M_SetupNextMenu (oldmenu_t *menudef); -static void M_StartMessage (const char *string, void(*routine)(int)); -static void M_EndMessage (int key); - -// [RH] For player setup menu. - void M_PlayerSetup (); -static void M_PlayerSetupTicker (); -static void M_PlayerSetupDrawer (); -static void M_EditPlayerName (int choice); -static void M_ChangePlayerTeam (int choice); -static void M_PlayerNameChanged (FSaveGameNode *dummy); -static void M_PlayerNameNotChanged (); -static void M_ChangeColorSet (int choice); -static void M_SlidePlayerRed (int choice); -static void M_SlidePlayerGreen (int choice); -static void M_SlidePlayerBlue (int choice); -static void M_ChangeClass (int choice); -static void M_ChangeGender (int choice); -static void M_ChangeSkin (int choice); -static void M_ChangeAutoAim (int choice); -static void M_ChangeSwitchPickup (int choice); -static void PickPlayerClass (); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -EXTERN_CVAR (String, playerclass) -EXTERN_CVAR (String, name) -EXTERN_CVAR (Int, team) -EXTERN_CVAR(Bool, neverswitchonpickup) -EXTERN_CVAR(Float, snd_menuvolume) - -extern bool sendpause; -extern int flagsvar; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -EMenuState menuactive; -menustack_t MenuStack[16]; -int MenuStackDepth; -int skullAnimCounter; // skull animation counter -bool drawSkull; // [RH] don't always draw skull -bool M_DemoNoPlay; -bool OptionsActive; -FButtonStatus MenuButtons[NUM_MKEYS]; -int MenuButtonTickers[NUM_MKEYS]; - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static char tempstring[80]; -static char underscore[2]; - -static FSaveGameNode *quickSaveSlot; // NULL = no quicksave slot picked! -static FSaveGameNode *lastSaveSlot; // Used for highlighting the most recently used slot in the menu -static int messageToPrint; // 1 = message to be printed -static const char *messageString; // ...and here is the message string! -static EMenuState messageLastMenuActive; -static void (*messageRoutine)(int response); // Non-NULL if only Y/N should close message -static int showSharewareMessage; -static int messageSelection; // 0 {Yes) or 1 (No) [if messageRoutine is non-NULL] - -static int genStringEnter; // we are going to be entering a savegame string -static size_t genStringLen; // [RH] Max # of chars that can be entered -static void (*genStringEnd)(FSaveGameNode *); -static void (*genStringCancel)(); -static int saveSlot; // which slot to save in -static size_t saveCharIndex; // which char we're editing - -static int LINEHEIGHT; -static const int PLAYERSETUP_LINEHEIGHT = 16; - -static char savegamestring[SAVESTRINGSIZE]; -static FString EndString; - -static short itemOn; // menu item skull is on -static int MenuTime; -static int InfoType; -static int InfoTic; - -static oldmenu_t *currentMenu; // current menudef -static oldmenu_t *TopLevelMenu; // The main menu everything hangs off of - -static FBackdropTexture *FireTexture; -static FRemapTable FireRemap(256); - -static const char *genders[3] = { "male", "female", "other" }; -static FPlayerClass *PlayerClass; -static int PlayerSkin; -static FState *PlayerState; -static int PlayerTics; -static int PlayerRotation; -static TArray PlayerColorSets; - -static FTexture *SavePic; -static FBrokenLines *SaveComment; -static List SaveGames; -static FSaveGameNode *TopSaveGame; -static FSaveGameNode *SelSaveGame; -static FSaveGameNode NewSaveNode; - -static int epi; // Selected episode - -static const char *saved_playerclass = NULL; - -// Heretic and Hexen do not, by default, come with glyphs for all of these -// characters. Oh well. Doom and Strife do. -static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] = - "ABCDEFGHIJKLM" - "NOPQRSTUVWXYZ" - "0123456789+-=" - ".,!?@'\":;[]()" - "<>^#$%&*/_ \b"; -static int InputGridX = INPUTGRID_WIDTH - 1; -static int InputGridY = INPUTGRID_HEIGHT - 1; -static bool InputGridOkay; // Last input was with a controller. - -// PRIVATE MENU DEFINITIONS ------------------------------------------------ - -// -// DOOM MENU -// -static oldmenuitem_t MainMenu[]= -{ - {1,0,'n',"M_NGAME",M_NewGame, CR_UNTRANSLATED}, - {1,0,'l',"M_LOADG",M_LoadGame, CR_UNTRANSLATED}, - {1,0,'s',"M_SAVEG",M_SaveGame, CR_UNTRANSLATED}, - {1,0,'o',"M_OPTION",M_Options, CR_UNTRANSLATED}, // [RH] Moved - {1,0,'r',"M_RDTHIS",M_ReadThis, CR_UNTRANSLATED}, // Another hickup with Special edition. - {1,0,'q',"M_QUITG",M_QuitGame, CR_UNTRANSLATED} -}; - -static oldmenu_t MainDef = -{ - countof(MainMenu), - MainMenu, - M_DrawMainMenu, - 97,64, - 0 -}; - -// -// HERETIC MENU -// -static oldmenuitem_t HereticMainMenu[] = -{ - {1,1,'n',"$MNU_NEWGAME",M_NewGame, CR_UNTRANSLATED}, - {1,1,'o',"$MNU_OPTIONS",M_Options, CR_UNTRANSLATED}, - {1,1,'f',"$MNU_GAMEFILES",M_GameFiles, CR_UNTRANSLATED}, - {1,1,'i',"$MNU_INFO",M_ReadThis, CR_UNTRANSLATED}, - {1,1,'q',"$MNU_QUITGAME",M_QuitGame, CR_UNTRANSLATED} -}; - -static oldmenu_t HereticMainDef = -{ - countof(HereticMainMenu), - HereticMainMenu, - M_DrawHereticMainMenu, - 110, 56, - 0 -}; - -// -// HEXEN "NEW GAME" MENU -// -static oldmenuitem_t ClassItems[] = -{ - { 1,1, 'f', "$MNU_FIGHTER", SCClass, CR_UNTRANSLATED }, - { 1,1, 'c', "$MNU_CLERIC", SCClass, CR_UNTRANSLATED }, - { 1,1, 'm', "$MNU_MAGE", SCClass, CR_UNTRANSLATED }, - { 1,1, 'r', "$MNU_RANDOM", SCClass, CR_UNTRANSLATED } // [RH] -}; - -static oldmenu_t ClassMenu = -{ - 4, ClassItems, - DrawClassMenu, - 66, 58, - 0 -}; - -// -// [GRB] CLASS SELECT -// -oldmenuitem_t ClassMenuItems[8] = -{ - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, - {1,1,0, NULL, M_ChooseClass, CR_UNTRANSLATED }, -}; - -oldmenu_t ClassMenuDef = -{ - 0, - ClassMenuItems, - M_DrawClassMenu, - 48,63, - 0 -}; - -// -// EPISODE SELECT -// -oldmenuitem_t EpisodeMenu[MAX_EPISODES] = -{ - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, - {1,0,0, NULL, M_Episode, CR_UNTRANSLATED}, -}; - -char EpisodeMaps[MAX_EPISODES][9]; -bool EpisodeNoSkill[MAX_EPISODES]; - -oldmenu_t EpiDef = -{ - 0, - EpisodeMenu, // oldmenuitem_t -> - M_DrawEpisode, // drawing routine -> - 48,63, // x,y - 0 // lastOn -}; - -// -// GAME FILES -// -static oldmenuitem_t FilesItems[] = -{ - {1,1,'l',"$MNU_LOADGAME",M_LoadGame, CR_UNTRANSLATED}, - {1,1,'s',"$MNU_SAVEGAME",M_SaveGame, CR_UNTRANSLATED} -}; - -static oldmenu_t FilesMenu = -{ - countof(FilesItems), - FilesItems, - M_DrawFiles, - 110,60, - 0 -}; - -// -// DOOM SKILL SELECT -// -static oldmenuitem_t SkillSelectMenu[]={ - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, - { 1, 0, 0, "", M_ChooseSkill, CR_UNTRANSLATED}, -}; - -static oldmenu_t SkillDef = -{ - 0, - SkillSelectMenu, // oldmenuitem_t -> - M_DrawNewGame, // drawing routine -> - 48,63, // x,y - -1 // lastOn -}; - -static oldmenu_t HexenSkillMenu = -{ - 0, - SkillSelectMenu, - DrawHexenSkillMenu, - 120, 44, - -1 -}; - - -void M_StartupSkillMenu(const char *playerclass) -{ - if (gameinfo.gametype & GAME_Raven) - { - SkillDef.x = 38; - SkillDef.y = 30; - - if (gameinfo.gametype == GAME_Hexen) - { - HexenSkillMenu.x = 38; - if (playerclass != NULL) - { - if (!stricmp(playerclass, "fighter")) HexenSkillMenu.x = 120; - else if (!stricmp(playerclass, "cleric")) HexenSkillMenu.x = 116; - else if (!stricmp(playerclass, "mage")) HexenSkillMenu.x = 112; - } - } - } - SkillDef.numitems = HexenSkillMenu.numitems = 0; - for(unsigned int i = 0; i < AllSkills.Size() && i < 8; i++) - { - FSkillInfo &skill = AllSkills[i]; - - if (skill.PicName.Len() != 0) - { - SkillSelectMenu[i].name = skill.PicName; - SkillSelectMenu[i].fulltext = false; - } - else - { - SkillSelectMenu[i].name = skill.MenuName; - SkillSelectMenu[i].fulltext = true; - } - SkillSelectMenu[i].textcolor = skill.GetTextColor(); - SkillSelectMenu[i].alphaKey = skill.Shortcut; - - if (playerclass != NULL) - { - FString * pmnm = skill.MenuNamesForPlayerClass.CheckKey(playerclass); - if (pmnm != NULL) - { - SkillSelectMenu[i].name = GStrings(*pmnm); - SkillSelectMenu[i].fulltext = true; - if (skill.Shortcut == 0) - SkillSelectMenu[i].alphaKey = tolower(SkillSelectMenu[i].name[0]); - } - } - SkillDef.numitems++; - HexenSkillMenu.numitems++; - } - int defskill = DefaultSkill; - if ((unsigned int)defskill >= AllSkills.Size()) - { - defskill = (AllSkills.Size() - 1) / 2; - } - // The default skill is only set the first time the menu is opened. - // After that, it opens on whichever skill you last selected. - if (SkillDef.lastOn < 0) - { - SkillDef.lastOn = defskill; - } - if (HexenSkillMenu.lastOn < 0) - { - HexenSkillMenu.lastOn = defskill; - } - // Hexen needs some manual coordinate adjustments based on player class - if (gameinfo.gametype == GAME_Hexen) - { - M_SetupNextMenu(&HexenSkillMenu); - } - else - { - M_SetupNextMenu(&SkillDef); - } - -} - -// -// [RH] Player Setup Menu -// -static oldmenuitem_t PlayerSetupMenu[] = -{ - { 1,0,'n',NULL,M_EditPlayerName, CR_UNTRANSLATED}, - { 2,0,'t',NULL,M_ChangePlayerTeam, CR_UNTRANSLATED}, - { 2,0,'c',NULL,M_ChangeColorSet, CR_UNTRANSLATED}, - { 2,0,'r',NULL,M_SlidePlayerRed, CR_UNTRANSLATED}, - { 2,0,'g',NULL,M_SlidePlayerGreen, CR_UNTRANSLATED}, - { 2,0,'b',NULL,M_SlidePlayerBlue, CR_UNTRANSLATED}, - { 2,0,'t',NULL,M_ChangeClass, CR_UNTRANSLATED}, - { 2,0,'s',NULL,M_ChangeSkin, CR_UNTRANSLATED}, - { 2,0,'e',NULL,M_ChangeGender, CR_UNTRANSLATED}, - { 2,0,'a',NULL,M_ChangeAutoAim, CR_UNTRANSLATED}, - { 2,0,'p',NULL,M_ChangeSwitchPickup, CR_UNTRANSLATED} -}; - -enum -{ - // These must be changed if the menu definition is altered - PSM_RED = 3, - PSM_GREEN = 4, - PSM_BLUE = 5, -}; - -static oldmenu_t PSetupDef = -{ - countof(PlayerSetupMenu), - PlayerSetupMenu, - M_PlayerSetupDrawer, - 48, 47, - 0 -}; - -// -// Read This! MENU 1 & 2 -// -static oldmenuitem_t ReadMenu[] = -{ - {1,0,0,NULL,M_ReadThisMore} -}; - -static oldmenu_t ReadDef = -{ - 1, - ReadMenu, - M_DrawReadThis, - 280,185, - 0 -}; - -// -// LOAD GAME MENU -// -static oldmenuitem_t LoadMenu[]= -{ - {1,0,'1',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'2',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'3',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'4',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'5',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'6',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'7',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'8',NULL, NULL, CR_UNTRANSLATED}, -}; - -static oldmenu_t LoadDef = -{ - countof(LoadMenu), - LoadMenu, - M_DrawLoad, - 80,54, - 0 -}; - -// -// SAVE GAME MENU -// -static oldmenuitem_t SaveMenu[] = -{ - {1,0,'1',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'2',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'3',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'4',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'5',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'6',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'7',NULL, NULL, CR_UNTRANSLATED}, - {1,0,'8',NULL, NULL, CR_UNTRANSLATED}, -}; - -static oldmenu_t SaveDef = -{ - countof(LoadMenu), - SaveMenu, - M_DrawSave, - 80,54, - 0 -}; - -// CODE -------------------------------------------------------------------- - -// [RH] Most menus can now be accessed directly -// through console commands. -CCMD (menu_main) -{ - M_StartControlPanel (true, true); -} - -CCMD (menu_load) -{ // F3 - M_StartControlPanel (true); - M_LoadGame (0); -} - -CCMD (menu_save) -{ // F2 - M_StartControlPanel (true); - M_SaveGame (0); -} - -CCMD (menu_help) -{ // F1 - M_StartControlPanel (true); - M_ReadThis (0); -} - -CCMD (quicksave) -{ // F6 - //M_StartControlPanel (true); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - M_QuickSave(); -} - -CCMD (quickload) -{ // F9 - //M_StartControlPanel (true); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - M_QuickLoad(); -} - -CCMD (menu_endgame) -{ // F7 - //M_StartControlPanel (true); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - M_EndGame(0); -} - -CCMD (menu_quit) -{ // F10 - //M_StartControlPanel (true); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - M_QuitGame(0); -} - -CCMD (menu_game) -{ - M_StartControlPanel (true); - M_NewGame(0); -} - -CCMD (menu_options) -{ - M_StartControlPanel (true); - M_Options(0); -} - -CCMD (menu_player) -{ - M_StartControlPanel (true); - M_PlayerSetup (); -} - -CCMD (bumpgamma) -{ - // [RH] Gamma correction tables are now generated - // on the fly for *any* gamma level. - // Q: What are reasonable limits to use here? - - float newgamma = Gamma + 0.1f; - - if (newgamma > 3.0) - newgamma = 1.0; - - Gamma = newgamma; - Printf ("Gamma correction level %g\n", *Gamma); -} - -void M_ActivateMenuInput () -{ - ResetButtonStates (); - menuactive = MENU_On; - // Pause sound effects before we play the menu switch sound. - // That way, it won't be paused. - P_CheckTickerPaused (); -} - -void M_DeactivateMenuInput () -{ - menuactive = MENU_Off; -} - -void M_DrawFiles () -{ -} - -void M_GameFiles (int choice) -{ - M_SetupNextMenu (&FilesMenu); -} - -// -// M_ReadSaveStrings -// -// Find savegames and read their titles -// -static void M_ReadSaveStrings () -{ - if (SaveGames.IsEmpty ()) - { - void *filefirst; - findstate_t c_file; - FString filter; - - atterm (M_UnloadSaveStrings); - - filter = G_BuildSaveName ("*.zds", -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); - FILE *file = fopen (filepath, "rb"); - - if (file != NULL) - { - PNGHandle *png; - char sig[16]; - char title[SAVESTRINGSIZE+1]; - bool oldVer = true; - bool addIt = false; - bool missing = false; - - // ZDoom 1.23 betas 21-33 have the savesig first. - // Earlier versions have the savesig second. - // Later versions have the savegame encapsulated inside a PNG. - // - // Old savegame versions are always added to the menu so - // the user can easily delete them if desired. - - title[SAVESTRINGSIZE] = 0; - - if (NULL != (png = M_VerifyPNG (file))) - { - char *ver = M_GetPNGText (png, "ZDoom Save Version"); - char *engine = M_GetPNGText (png, "Engine"); - if (ver != NULL) - { - if (!M_GetPNGText (png, "Title", title, SAVESTRINGSIZE)) - { - strncpy (title, I_FindName(&c_file), SAVESTRINGSIZE); - } - if (strncmp (ver, SAVESIG, 9) == 0 && - atoi (ver+9) >= MINSAVEVER && - engine != NULL) - { - // Was saved with a compatible ZDoom version, - // so check if it's for the current game. - // If it is, add it. Otherwise, ignore it. - char *iwad = M_GetPNGText (png, "Game WAD"); - if (iwad != NULL) - { - if (stricmp (iwad, Wads.GetWadName (FWadCollection::IWAD_FILENUM)) == 0) - { - addIt = true; - oldVer = false; - missing = !G_CheckSaveGameWads (png, false); - } - delete[] iwad; - } - } - else - { // An old version - addIt = true; - } - delete[] ver; - } - if (engine != NULL) - { - delete[] engine; - } - delete png; - } - else - { - fseek (file, 0, SEEK_SET); - if (fread (sig, 1, 16, file) == 16) - { - - if (strncmp (sig, "ZDOOMSAVE", 9) == 0) - { - 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) - { - addIt = true; - } - } - } - } - - if (addIt) - { - FSaveGameNode *node = new FSaveGameNode; - node->Filename = filepath; - node->bOldVersion = oldVer; - node->bMissingWads = missing; - memcpy (node->Title, title, SAVESTRINGSIZE); - M_InsertSaveNode (node); - } - fclose (file); - } - } while (I_FindNext (filefirst, &c_file) == 0); - I_FindClose (filefirst); - } - } - if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) - { - SelSaveGame = static_cast(SaveGames.Head); - } -} - -static void M_UnloadSaveStrings() -{ - M_UnloadSaveData(); - while (!SaveGames.IsEmpty()) - { - M_RemoveSaveSlot (static_cast(SaveGames.Head)); - } -} - -static FSaveGameNode *M_RemoveSaveSlot (FSaveGameNode *file) -{ - FSaveGameNode *next = static_cast(file->Succ); - - if (file == TopSaveGame) - { - TopSaveGame = next; - } - if (quickSaveSlot == file) - { - quickSaveSlot = NULL; - } - if (lastSaveSlot == file) - { - lastSaveSlot = NULL; - } - file->Remove (); - delete file; - return next; -} - -void M_InsertSaveNode (FSaveGameNode *node) -{ - FSaveGameNode *probe; - - if (SaveGames.IsEmpty ()) - { - SaveGames.AddHead (node); - return; - } - - if (node->bOldVersion) - { // Add node at bottom of list - probe = static_cast(SaveGames.TailPred); - while (probe->Pred != NULL && probe->bOldVersion && - stricmp (node->Title, probe->Title) < 0) - { - probe = static_cast(probe->Pred); - } - node->Insert (probe); - } - else - { // Add node at top of list - probe = static_cast(SaveGames.Head); - while (probe->Succ != NULL && !probe->bOldVersion && - stricmp (node->Title, probe->Title) > 0) - { - probe = static_cast(probe->Succ); - } - node->InsertBefore (probe); - } -} - -void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave) -{ - FSaveGameNode *node; - - if (file == NULL) - return; - - M_ReadSaveStrings (); - - // See if the file is already in our list - for (node = static_cast(SaveGames.Head); - node->Succ != NULL; - node = static_cast(node->Succ)) - { -#ifdef unix - if (node->Filename.Compare (file) == 0) -#else - if (node->Filename.CompareNoCase (file) == 0) -#endif - { - strcpy (node->Title, title); - node->bOldVersion = false; - node->bMissingWads = false; - break; - } - } - - if (node->Succ == NULL) - { - node = new FSaveGameNode; - strcpy (node->Title, title); - node->Filename = file; - node->bOldVersion = false; - node->bMissingWads = false; - M_InsertSaveNode (node); - SelSaveGame = node; - } - - if (okForQuicksave) - { - if (quickSaveSlot == NULL) quickSaveSlot = node; - lastSaveSlot = node; - } -} - -// -// M_LoadGame & Cie. -// -void M_DrawLoad (void) -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - FTexture *title = TexMan["M_LOADG"]; - screen->DrawTexture (title, - (SCREENWIDTH - title->GetScaledWidth()*CleanXfac)/2, 20*CleanYfac, - DTA_CleanNoMove, true, TAG_DONE); - } - else - { - const char *loadgame = GStrings("MNU_LOADGAME"); - screen->DrawText (BigFont, CR_UNTRANSLATED, - (SCREENWIDTH - BigFont->StringWidth (loadgame)*CleanXfac)/2, 10*CleanYfac, - loadgame, DTA_CleanNoMove, true, TAG_DONE); - } - M_DrawSaveLoadCommon (); -} - - - -// -// Draw border for the savegame description -// [RH] Width of the border is variable -// -void M_DrawSaveLoadBorder (int x, int y, int len) -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - int i; - - screen->DrawTexture (TexMan["M_LSLEFT"], x-8, y+7, DTA_Clean, true, TAG_DONE); - - for (i = 0; i < len; i++) - { - screen->DrawTexture (TexMan["M_LSCNTR"], x, y+7, DTA_Clean, true, TAG_DONE); - x += 8; - } - - screen->DrawTexture (TexMan["M_LSRGHT"], x, y+7, DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawTexture (TexMan["M_FSLOT"], x, y+1, DTA_Clean, true, TAG_DONE); - } -} - -static void M_ExtractSaveData (const FSaveGameNode *node) -{ - FILE *file; - PNGHandle *png; - - M_UnloadSaveData (); - - if (node != NULL && - node->Succ != NULL && - !node->Filename.IsEmpty() && - !node->bOldVersion && - (file = fopen (node->Filename.GetChars(), "rb")) != NULL) - { - if (NULL != (png = M_VerifyPNG (file))) - { - char *time, *pcomment, *comment; - size_t commentlen, totallen, timelen; - - // Extract comment - time = M_GetPNGText (png, "Creation Time"); - pcomment = M_GetPNGText (png, "Comment"); - if (pcomment != NULL) - { - commentlen = strlen (pcomment); - } - else - { - commentlen = 0; - } - if (time != NULL) - { - timelen = strlen (time); - totallen = timelen + commentlen + 3; - } - else - { - timelen = 0; - totallen = commentlen + 1; - } - if (totallen != 0) - { - comment = new char[totallen]; - - if (timelen) - { - memcpy (comment, time, timelen); - comment[timelen] = '\n'; - comment[timelen+1] = '\n'; - timelen += 2; - } - if (commentlen) - { - memcpy (comment + timelen, pcomment, commentlen); - } - comment[timelen+commentlen] = 0; - SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, comment); - delete[] comment; - delete[] time; - delete[] pcomment; - } - - // Extract pic - SavePic = PNGTexture_CreateFromFile(png, node->Filename); - - delete png; - } - fclose (file); - } -} - -static void M_UnloadSaveData () -{ - if (SavePic != NULL) - { - delete SavePic; - } - if (SaveComment != NULL) - { - V_FreeBrokenLines (SaveComment); - } - - SavePic = NULL; - SaveComment = NULL; -} - -static void M_DrawSaveLoadCommon () -{ - const int savepicLeft = 10; - const int savepicTop = 54*CleanYfac; - const int savepicWidth = 216*screen->GetWidth()/640; - const int savepicHeight = 135*screen->GetHeight()/400; - - const int rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; - const int listboxLeft = savepicLeft + savepicWidth + 14; - const int listboxTop = savepicTop; - const int listboxWidth = screen->GetWidth() - listboxLeft - 10; - const int listboxHeight1 = screen->GetHeight() - listboxTop - 10; - const int listboxRows = (listboxHeight1 - 1) / rowHeight; - const int listboxHeight = listboxRows * rowHeight + 1; - const int listboxRight = listboxLeft + listboxWidth; - const int listboxBottom = listboxTop + listboxHeight; - - const int commentLeft = savepicLeft; - const int commentTop = savepicTop + savepicHeight + 16; - const int commentWidth = savepicWidth; - const int commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac; - const int commentRight = commentLeft + commentWidth; - const int commentBottom = commentTop + commentHeight; - - FSaveGameNode *node; - int i; - bool didSeeSelected = false; - - // Draw picture area - if (gameaction == ga_loadgame || gameaction == ga_savegame) - { - return; - } - - M_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 - { - screen->Clear (savepicLeft, savepicTop, - savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); - - if (!SaveGames.IsEmpty ()) - { - const char *text = - (SelSaveGame == NULL || !SelSaveGame->bOldVersion) - ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, - savepicTop+(savepicHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw comment area - M_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); - screen->Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); - if (SaveComment != NULL) - { - // 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 - // for that. - for (i = 0; SaveComment != NULL && SaveComment[i].Width >= 0 && i < 6; ++i) - { - screen->DrawText (SmallFont, CR_GOLD, commentLeft, commentTop - + SmallFont->GetHeight()*i*CleanYfac, SaveComment[i].Text, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // Draw file area - do - { - M_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); - screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); - - if (SaveGames.IsEmpty ()) - { - const char * text = GStrings("MNU_NOFILES"); - const int textlen = SmallFont->StringWidth (text)*CleanXfac; - - screen->DrawText (SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, - listboxTop+(listboxHeight-rowHeight)/2, text, - DTA_CleanNoMove, true, TAG_DONE); - return; - } - - for (i = 0, node = TopSaveGame; - i < listboxRows && node->Succ != NULL; - ++i, node = static_cast(node->Succ)) - { - int color; - if (node->bOldVersion) - { - color = CR_BLUE; - } - else if (node->bMissingWads) - { - color = CR_ORANGE; - } - else if (node == SelSaveGame) - { - color = CR_WHITE; - } - else - { - color = CR_TAN; - } - if (node == SelSaveGame) - { - screen->Clear (listboxLeft, listboxTop+rowHeight*i, - listboxRight, listboxTop+rowHeight*(i+1), -1, - genStringEnter ? MAKEARGB(255,255,0,0) : MAKEARGB(255,0,0,255)); - didSeeSelected = true; - if (!genStringEnter) - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - else - { - screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, savegamestring, - DTA_CleanNoMove, true, TAG_DONE); - screen->DrawText (SmallFont, CR_WHITE, - listboxLeft+1+SmallFont->StringWidth (savegamestring)*CleanXfac, - listboxTop+rowHeight*i+CleanYfac, underscore, - DTA_CleanNoMove, true, TAG_DONE); - } - } - else - { - screen->DrawText (SmallFont, color, - listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, - DTA_CleanNoMove, true, TAG_DONE); - } - } - - // This is dumb: If the selected node was not visible, - // scroll down and redraw. M_SaveLoadResponder() - // guarantees that if the node is not visible, it will - // always be below the visible list instead of above it. - // This should not really be done here, but I don't care. - - if (!didSeeSelected) - { - for (i = 1; node->Succ != NULL && node != SelSaveGame; ++i) - { - node = static_cast(node->Succ); - } - if (node->Succ == NULL) - { // SelSaveGame is invalid - didSeeSelected = true; - } - else - { - do - { - TopSaveGame = static_cast(TopSaveGame->Succ); - } while (--i); - } - } - } while (!didSeeSelected); -} - -// Draw a frame around the specified area using the view border -// frame graphics. The border is drawn outside the area, not in it. -void M_DrawFrame (int left, int top, int width, int height) -{ - FTexture *p; - const gameborder_t *border = gameinfo.border; - // Sanity check for incomplete gameinfo - if (border == NULL) - return; - int offset = border->offset; - int right = left + width; - int bottom = top + height; - - // Draw top and bottom sides. - p = TexMan[border->t]; - screen->FlatFill(left, top - p->GetHeight(), right, top, p, true); - p = TexMan[border->b]; - screen->FlatFill(left, bottom, right, bottom + p->GetHeight(), p, true); - - // Draw left and right sides. - p = TexMan[border->l]; - screen->FlatFill(left - p->GetWidth(), top, left, bottom, p, true); - p = TexMan[border->r]; - screen->FlatFill(right, top, right + p->GetWidth(), bottom, p, true); - - // Draw beveled corners. - screen->DrawTexture (TexMan[border->tl], left-offset, top-offset, TAG_DONE); - screen->DrawTexture (TexMan[border->tr], left+width, top-offset, TAG_DONE); - screen->DrawTexture (TexMan[border->bl], left-offset, top+height, TAG_DONE); - screen->DrawTexture (TexMan[border->br], left+width, top+height, TAG_DONE); -} - -// -// Selected from DOOM menu -// -void M_LoadGame (int choice) -{ - if (netgame) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CLOADNET"), NULL); - else - M_StartMessage (GStrings("LOADNET"), NULL); - return; - } - - M_SetupNextMenu (&LoadDef); - drawSkull = false; - M_ReadSaveStrings (); - TopSaveGame = static_cast(SaveGames.Head); - M_ExtractSaveData (SelSaveGame); -} - - -// -// M_SaveGame & Cie. -// -void M_DrawSave() -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - FTexture *title = TexMan["M_SAVEG"]; - screen->DrawTexture (title, - (SCREENWIDTH-title->GetScaledWidth()*CleanXfac)/2, 20*CleanYfac, - DTA_CleanNoMove, true, TAG_DONE); - } - else - { - const char *text = GStrings("MNU_SAVEGAME"); - screen->DrawText (BigFont, CR_UNTRANSLATED, - (SCREENWIDTH - BigFont->StringWidth (text)*CleanXfac)/2, 10*CleanYfac, - text, DTA_CleanNoMove, true, TAG_DONE); - } - M_DrawSaveLoadCommon (); -} - -// -// M_Responder calls this when the user is finished -// -void M_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 (); - BorderNeedRefresh = screen->GetPageCount (); -} - -// -// Selected from DOOM menu -// -void M_SaveGame (int choice) -{ - if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) - { - M_StartMessage (GStrings("SAVEDEAD"), NULL); - return; - } - - if (gamestate != GS_LEVEL) - return; - - M_SetupNextMenu(&SaveDef); - drawSkull = false; - - M_ReadSaveStrings(); - SaveGames.AddHead (&NewSaveNode); - TopSaveGame = static_cast(SaveGames.Head); - if (lastSaveSlot == NULL) - { - SelSaveGame = &NewSaveNode; - } - else - { - SelSaveGame = lastSaveSlot; - } - M_ExtractSaveData (SelSaveGame); -} - - - -// -// M_QuickSave -// -void M_QuickSaveResponse (int ch) -{ - if (ch == 'y') - { - M_DoSave (quickSaveSlot); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); - } -} - -void M_QuickSave () -{ - if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE); - return; - } - - if (gamestate != GS_LEVEL) - return; - - if (quickSaveSlot == NULL) - { - M_StartControlPanel(false); - M_SaveGame (0); - return; - } - if(gameinfo.gametype == GAME_Chex) - mysnprintf (tempstring, countof(tempstring), GStrings("CQSPROMPT"), quickSaveSlot->Title); - else - mysnprintf (tempstring, countof(tempstring), GStrings("QSPROMPT"), quickSaveSlot->Title); - strcpy (savegamestring, quickSaveSlot->Title); - M_StartMessage (tempstring, M_QuickSaveResponse); -} - - - -// -// M_QuickLoad -// -void M_QuickLoadResponse (int ch) -{ - if (ch == 'y') - { - M_LoadSelect (quickSaveSlot); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); - } -} - - -void M_QuickLoad () -{ - if (netgame) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CQLOADNET"), NULL); - else - M_StartMessage (GStrings("QLOADNET"), NULL); - return; - } - - if (quickSaveSlot == NULL) - { - M_StartControlPanel(false); - // signal that whatever gets loaded should be the new quicksave - quickSaveSlot = (FSaveGameNode *)1; - M_LoadGame (0); - return; - } - if(gameinfo.gametype == GAME_Chex) - mysnprintf (tempstring, countof(tempstring), GStrings("CQLPROMPT"), quickSaveSlot->Title); - else - mysnprintf (tempstring, countof(tempstring), GStrings("QLPROMPT"), quickSaveSlot->Title); - M_StartMessage (tempstring, M_QuickLoadResponse); -} - -// -// Read This Menus -// -void M_DrawReadThis () -{ - FTexture *tex = NULL, *prevpic = NULL; - fixed_t alpha; - - // Did the mapper choose a custom help page via MAPINFO? - if ((level.info != NULL) && level.info->f1[0] != 0) - { - tex = TexMan.FindTexture(level.info->f1); - InfoType = 1; - } - - if (tex == NULL) - { - tex = TexMan[gameinfo.infoPages[InfoType-1].GetChars()]; - } - - if (InfoType > 1) - { - prevpic = TexMan[gameinfo.infoPages[InfoType-2].GetChars()]; - } - - alpha = MIN (Scale (gametic - InfoTic, OPAQUE, TICRATE/3), OPAQUE); - if (alpha < OPAQUE && prevpic != NULL) - { - screen->DrawTexture (prevpic, 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), - TAG_DONE); - } - screen->DrawTexture (tex, 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), - DTA_Alpha, alpha, - TAG_DONE); -} - -// -// M_DrawMainMenu -// -void M_DrawMainMenu (void) -{ - if (gameinfo.gametype & GAME_DoomChex) - { - screen->DrawTexture (TexMan["M_DOOM"], 94, 2, DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawTexture (TexMan["M_STRIFE"], 84, 2, DTA_Clean, true, TAG_DONE); - } -} - -void M_DrawHereticMainMenu () -{ - char name[9]; - - screen->DrawTexture (TexMan["M_HTIC"], 88, 0, DTA_Clean, true, TAG_DONE); - - if (gameinfo.gametype == GAME_Hexen) - { - int frame = (MenuTime / 5) % 7; - - mysnprintf (name, countof(name), "FBUL%c0", (frame+2)%7 + 'A'); - screen->DrawTexture (TexMan[name], 37, 80, DTA_Clean, true, TAG_DONE); - - mysnprintf (name, countof(name), "FBUL%c0", frame + 'A'); - screen->DrawTexture (TexMan[name], 278, 80, DTA_Clean, true, TAG_DONE); - } - else - { - int frame = (MenuTime / 3) % 18; - - mysnprintf (name, countof(name), "M_SKL%.2d", 17 - frame); - screen->DrawTexture (TexMan[name], 40, 10, DTA_Clean, true, TAG_DONE); - - mysnprintf (name, countof(name), "M_SKL%.2d", frame); - screen->DrawTexture (TexMan[name], 232, 10, DTA_Clean, true, TAG_DONE); - } -} - -// -// M_NewGame -// -void M_DrawNewGame(void) -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - screen->DrawTexture (TexMan[gameinfo.gametype & GAME_DoomChex ? "M_NEWG" : "M_NGAME"], 96, 14, DTA_Clean, true, TAG_DONE); - screen->DrawTexture (TexMan["M_SKILL"], 54, 38, DTA_Clean, true, TAG_DONE); - } -} - -void M_NewGame(int choice) -{ - if (netgame && !demoplayback) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL); - else - M_StartMessage (GStrings("NEWGAME"), NULL); - return; - } - - // Set up episode menu positioning - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - EpiDef.x = 48; - EpiDef.y = 63; - } - else - { - EpiDef.x = 80; - EpiDef.y = 50; - } - if (EpiDef.numitems > 4) - { - EpiDef.y -= LINEHEIGHT; - } - epi = 0; - - if (gameinfo.gametype == GAME_Hexen && ClassMenuDef.numitems == 0) - { // [RH] Make the default entry the last class the player used. - ClassMenu.lastOn = players[consoleplayer].userinfo.PlayerClass; - if (ClassMenu.lastOn < 0) - { - ClassMenu.lastOn = 3; - } - M_SetupNextMenu (&ClassMenu); - } - // [GRB] Class select - else if (ClassMenuDef.numitems > 1) - { - ClassMenuDef.lastOn = ClassMenuDef.numitems - 1; - if (players[consoleplayer].userinfo.PlayerClass >= 0) - { - int n = 0; - for (int i = 0; i < (int)PlayerClasses.Size () && n < 7; i++) - { - if (!(PlayerClasses[i].Flags & PCF_NOMENU)) - { - if (i == players[consoleplayer].userinfo.PlayerClass) - { - ClassMenuDef.lastOn = n; - break; - } - n++; - } - } - } - - PickPlayerClass (); - - PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState; - PlayerTics = PlayerState->GetTics(); - - if (FireTexture == NULL) - { - FireTexture = new FBackdropTexture; - } - M_SetupNextMenu (&ClassMenuDef); - } - else if (EpiDef.numitems <= 1) - { - if (AllSkills.Size() == 1) - { - M_ChooseSkill(0); - } - else if (EpisodeNoSkill[0]) - { - M_ChooseSkill(AllSkills.Size() == 2? 1:2); - } - else - { - M_StartupSkillMenu(NULL); - } - } - else - { - M_SetupNextMenu (&EpiDef); - } -} - -//========================================================================== -// -// DrawClassMenu -// -//========================================================================== - -static void DrawClassMenu(void) -{ - char name[9]; - int classnum; - - static const char boxLumpName[3][7] = - { - "M_FBOX", - "M_CBOX", - "M_MBOX" - }; - static const char walkLumpName[3][10] = - { - "M_FWALK%d", - "M_CWALK%d", - "M_MWALK%d" - }; - - const char *text = GStrings("MNU_CHOOSECLASS"); - screen->DrawText (BigFont, CR_UNTRANSLATED, 34, 24, text, DTA_Clean, true, TAG_DONE); - classnum = itemOn; - if (classnum > 2) - { - classnum = (MenuTime>>2) % 3; - } - screen->DrawTexture (TexMan[boxLumpName[classnum]], 174, 8, DTA_Clean, true, TAG_DONE); - - mysnprintf (name, countof(name), walkLumpName[classnum], ((MenuTime >> 3) & 3) + 1); - screen->DrawTexture (TexMan[name], 174+24, 8+12, DTA_Clean, true, TAG_DONE); -} - -// [GRB] Class select drawer -static void M_DrawClassMenu () -{ - int tit_y = 15; - const char * text = GStrings("MNU_CHOOSECLASS"); - - if (ClassMenuDef.numitems > 4 && gameinfo.gametype & GAME_Raven) - tit_y = 2; - - screen->DrawText (BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - 160 - BigFont->StringWidth (text)/2, - tit_y, - text, DTA_Clean, true, TAG_DONE); - - int x = (200-160)*CleanXfac+(SCREENWIDTH>>1); - int y = (ClassMenuDef.y-100)*CleanYfac+(SCREENHEIGHT>>1); - - if (!FireTexture) - { - screen->Clear (x, y, x + 72 * CleanXfac, y + 80 * CleanYfac-1, 0, 0); - } - else - { - screen->DrawTexture (FireTexture, x, y - 1, - DTA_DestWidth, 72 * CleanXfac, - DTA_DestHeight, 80 * CleanYfac, - DTA_Translation, &FireRemap, - DTA_Masked, true, - TAG_DONE); - } - - M_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); - - spriteframe_t *sprframe = &SpriteFrames[sprites[PlayerState->sprite].spriteframes + PlayerState->GetFrame()]; - fixed_t scaleX = GetDefaultByType (PlayerClass->Type)->scaleX; - fixed_t scaleY = GetDefaultByType (PlayerClass->Type)->scaleY; - - if (sprframe != NULL) - { - FTexture *tex = TexMan(sprframe->Texture[0]); - if (tex != NULL && tex->UseType != FTexture::TEX_Null) - { - screen->DrawTexture (tex, - x + 36*CleanXfac, y + 71*CleanYfac, - DTA_DestWidth, MulScale16 (tex->GetWidth() * CleanXfac, scaleX), - DTA_DestHeight, MulScale16 (tex->GetHeight() * CleanYfac, scaleY), - TAG_DONE); - } - } -} - -//--------------------------------------------------------------------------- -// -// PROC DrawSkillMenu -// -//--------------------------------------------------------------------------- - -static void DrawHexenSkillMenu() -{ - screen->DrawText (BigFont, CR_UNTRANSLATED, 74, 16, GStrings("MNU_CHOOSESKILL"), DTA_Clean, true, TAG_DONE); -} - - -// -// M_Episode -// -void M_DrawEpisode () -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - screen->DrawTexture (TexMan["M_EPISOD"], 54, 38, DTA_Clean, true, TAG_DONE); - } -} - -static int confirmskill; - -void M_VerifyNightmare (int ch) -{ - if (ch != 'y') - return; - - G_DeferedInitNew (EpisodeMaps[epi], confirmskill); - if (gamestate == GS_FULLCONSOLE) - { - gamestate = GS_HIDECONSOLE; - gameaction = ga_newgame; - } - M_ClearMenus (); -} - -void M_ChooseSkill (int choice) -{ - if (AllSkills[choice].MustConfirm) - { - const char *msg = AllSkills[choice].MustConfirmText; - if (*msg==0) msg = GStrings("NIGHTMARE"); - if (*msg=='$') msg = GStrings(msg+1); - confirmskill = choice; - M_StartMessage (msg, M_VerifyNightmare); - return; - } - - G_DeferedInitNew (EpisodeMaps[epi], choice); - if (gamestate == GS_FULLCONSOLE) - { - gamestate = GS_HIDECONSOLE; - gameaction = ga_newgame; - } - M_ClearMenus (); -} - -void M_Episode (int choice) -{ - if ((gameinfo.flags & GI_SHAREWARE) && choice) - { - if (gameinfo.gametype == GAME_Doom) - { - M_StartMessage(GStrings("SWSTRING"), NULL); - //M_SetupNextMenu(&ReadDef); - } - else if (gameinfo.gametype == GAME_Chex) - { - M_StartMessage(GStrings("CSWSTRING"), NULL); - } - else - { - showSharewareMessage = 3*TICRATE; - } - return; - } - - epi = choice; - - if (AllSkills.Size() == 1) - { - saved_playerclass = NULL; - M_ChooseSkill(0); - return; - } - else if (EpisodeNoSkill[choice]) - { - saved_playerclass = NULL; - M_ChooseSkill(AllSkills.Size() == 2? 1:2); - return; - } - M_StartupSkillMenu(saved_playerclass); - saved_playerclass = NULL; -} - -//========================================================================== -// -// SCClass -// -//========================================================================== - -static void SCClass (int option) -{ - if (netgame) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL); - else - M_StartMessage (GStrings("NEWGAME"), NULL); - return; - } - - if (option == 3) - playerclass = "Random"; - else - playerclass = PlayerClasses[option].Type->Meta.GetMetaString (APMETA_DisplayName); - - if (EpiDef.numitems > 1) - { - saved_playerclass = playerclass; - M_SetupNextMenu (&EpiDef); - } - else if (AllSkills.Size() == 1) - { - M_ChooseSkill(0); - } - else if (!EpisodeNoSkill[0]) - { - M_StartupSkillMenu(playerclass); - } - else - { - M_ChooseSkill(AllSkills.Size() == 2? 1:2); - } -} - -// [GRB] -static void M_ChooseClass (int choice) -{ - if (netgame) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL); - else - M_StartMessage (GStrings("NEWGAME"), NULL); - return; - } - - playerclass = (choice < ClassMenuDef.numitems-1) ? ClassMenuItems[choice].name : "Random"; - - if (EpiDef.numitems > 1) - { - saved_playerclass = playerclass; - M_SetupNextMenu (&EpiDef); - } - else if (AllSkills.Size() == 1) - { - M_ChooseSkill(0); - } - else if (EpisodeNoSkill[0]) - { - M_ChooseSkill(AllSkills.Size() == 2? 1:2); - } - else - { - M_StartupSkillMenu(playerclass); - } -} - - -void M_Options (int choice) -{ - OptionsActive = M_StartOptionsMenu (); -} - - - - -// -// M_EndGame -// -void M_EndGameResponse(int ch) -{ - if (ch != 'y') - return; - - currentMenu->lastOn = itemOn; - M_ClearMenus (); - D_StartTitle (); -} - -void M_EndGame(int choice) -{ - choice = 0; - if (!usergame) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE); - return; - } - - if (netgame) - { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage(GStrings("CNETEND"), NULL); - else - M_StartMessage(GStrings("NETEND"), NULL); - return; - } - - if(gameinfo.gametype == GAME_Chex) - M_StartMessage(GStrings("CENDGAME"), M_EndGameResponse); - else - M_StartMessage(GStrings("ENDGAME"), M_EndGameResponse); -} - - - - -// -// M_ReadThis -// -void M_ReadThis (int choice) -{ - drawSkull = false; - InfoType = 1; - InfoTic = gametic; - M_SetupNextMenu (&ReadDef); -} - -void M_ReadThisMore (int choice) -{ - InfoType++; - InfoTic = gametic; - if ((level.info != NULL && level.info->f1[0] != 0) || InfoType > int(gameinfo.infoPages.Size())) - { - M_FinishReadThis (0); - } -} - -void M_FinishReadThis (int choice) -{ - drawSkull = true; - M_PopMenuStack (); -} - -// -// M_QuitGame -// - -void M_QuitResponse(int ch) -{ - if (ch != 'y') - return; - if (!netgame) - { - if (gameinfo.quitSound.IsNotEmpty()) - { - S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE); - I_WaitVBL (105); - } - } - ST_Endoom(); -} - -void M_QuitGame (int choice) -{ - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - int quitmsg = 0; - if (gameinfo.gametype == GAME_Doom) - { - quitmsg = gametic % (NUM_QUITDOOMMESSAGES + 1); - } - else if (gameinfo.gametype == GAME_Strife) - { - quitmsg = gametic % (NUM_QUITSTRIFEMESSAGES + 1); - if (quitmsg != 0) quitmsg += NUM_QUITDOOMMESSAGES; - } - else - { - quitmsg = gametic % (NUM_QUITCHEXMESSAGES + 1); - if (quitmsg != 0) quitmsg += NUM_QUITDOOMMESSAGES + NUM_QUITSTRIFEMESSAGES; - } - - if (quitmsg != 0) - { - EndString.Format("QUITMSG%d", quitmsg); - EndString.Format("%s\n\n%s", GStrings(EndString), GStrings("DOSY")); - } - else - { - EndString.Format("%s\n\n%s", GStrings("QUITMSG"), GStrings("DOSY")); - } - } - else - { - EndString = GStrings("RAVENQUITMSG"); - } - - M_StartMessage (EndString, M_QuitResponse); -} - - -// -// [RH] Player Setup Menu code -// -void M_PlayerSetup (void) -{ - OptionsActive = false; - drawSkull = true; - strcpy (savegamestring, name); - M_DemoNoPlay = true; - if (demoplayback) - G_CheckDemoStatus (); - M_SetupNextMenu (&PSetupDef); - if (players[consoleplayer].mo != NULL) - { - PlayerClass = &PlayerClasses[players[consoleplayer].CurrentPlayerClass]; - } - PlayerSkin = players[consoleplayer].userinfo.skin; - R_GetPlayerTranslation (players[consoleplayer].userinfo.color, - P_GetPlayerColorSet(PlayerClass->Type->TypeName, players[consoleplayer].userinfo.colorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); - PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState; - PlayerTics = PlayerState->GetTics(); - if (FireTexture == NULL) - { - FireTexture = new FBackdropTexture; - } - P_EnumPlayerColorSets(PlayerClass->Type->TypeName, &PlayerColorSets); -} - -static void M_PlayerSetupTicker (void) -{ - // Based on code in f_finale.c - FPlayerClass *oldclass = PlayerClass; - - if (currentMenu == &ClassMenuDef) - { - int item; - - if (itemOn < ClassMenuDef.numitems-1) - item = itemOn; - else - item = (MenuTime>>2) % (ClassMenuDef.numitems-1); - - PlayerClass = &PlayerClasses[D_PlayerClassToInt (ClassMenuItems[item].name)]; - P_EnumPlayerColorSets(PlayerClass->Type->TypeName, &PlayerColorSets); - } - else - { - PickPlayerClass (); - } - - if (PlayerClass != oldclass) - { - PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState; - PlayerTics = PlayerState->GetTics(); - - PlayerSkin = R_FindSkin (skins[PlayerSkin].name, int(PlayerClass - &PlayerClasses[0])); - R_GetPlayerTranslation (players[consoleplayer].userinfo.color, - P_GetPlayerColorSet(PlayerClass->Type->TypeName, players[consoleplayer].userinfo.colorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); - } - - if (PlayerState->GetTics () != -1 && PlayerState->GetNextState () != NULL) - { - if (--PlayerTics > 0) - return; - - PlayerState = PlayerState->GetNextState(); - PlayerTics = PlayerState->GetTics(); - } -} - - -static void M_DrawPlayerSlider (int x, int y, int cur) -{ - const int range = 255; - - 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); -} - -static void M_PlayerSetupDrawer () -{ - const int LINEHEIGHT = PLAYERSETUP_LINEHEIGHT; - int x, xo, yo; - EColorRange label, value; - DWORD color; - - if (!(gameinfo.gametype & (GAME_DoomStrifeChex))) - { - xo = 5; - yo = 5; - label = CR_GREEN; - value = CR_UNTRANSLATED; - } - else - { - xo = yo = 0; - label = CR_UNTRANSLATED; - value = CR_GREY; - } - - // Draw title - const char *text = GStrings("MNU_PLAYERSETUP"); - screen->DrawText (BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - 160 - BigFont->StringWidth (text)/2, - 15, - text, DTA_Clean, true, TAG_DONE); - - - // Draw player name box - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y+yo, "Name", DTA_Clean, true, TAG_DONE); - M_DrawSaveLoadBorder (PSetupDef.x + 56, PSetupDef.y, MAXPLAYERNAME+1); - screen->DrawText (SmallFont, CR_UNTRANSLATED, PSetupDef.x + 56 + xo, PSetupDef.y+yo, savegamestring, - DTA_Clean, true, TAG_DONE); - - // Draw cursor for player name box - if (genStringEnter) - screen->DrawText (SmallFont, CR_UNTRANSLATED, - PSetupDef.x + SmallFont->StringWidth(savegamestring) + 56+xo, - PSetupDef.y + yo, underscore, DTA_Clean, true, TAG_DONE); - - // Draw player team setting - x = SmallFont->StringWidth ("Team") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT+yo, "Team", - DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT+yo, - !TeamLibrary.IsValidTeam (players[consoleplayer].userinfo.team) ? "None" : - Teams[players[consoleplayer].userinfo.team].GetName (), - DTA_Clean, true, TAG_DONE); - - // Draw player character - { - int x = 320 - 88 - 32 + xo, y = PSetupDef.y + LINEHEIGHT*3 - 18 + yo; - - x = (x-160)*CleanXfac+(SCREENWIDTH>>1); - y = (y-100)*CleanYfac+(SCREENHEIGHT>>1); - if (!FireTexture) - { - screen->Clear (x, y, x + 72 * CleanXfac, y + 80 * CleanYfac-1, 0, 0); - } - else - { - screen->DrawTexture (FireTexture, x, y - 1, - DTA_DestWidth, 72 * CleanXfac, - DTA_DestHeight, 80 * CleanYfac, - DTA_Translation, &FireRemap, - DTA_Masked, false, - TAG_DONE); - } - - M_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); - } - { - spriteframe_t *sprframe; - fixed_t ScaleX, ScaleY; - - if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || - players[consoleplayer].userinfo.PlayerClass == -1 || - PlayerState->sprite != GetDefaultByType (PlayerClass->Type)->SpawnState->sprite) - { - sprframe = &SpriteFrames[sprites[PlayerState->sprite].spriteframes + PlayerState->GetFrame()]; - ScaleX = GetDefaultByType(PlayerClass->Type)->scaleX; - ScaleY = GetDefaultByType(PlayerClass->Type)->scaleY; - } - else - { - sprframe = &SpriteFrames[sprites[skins[PlayerSkin].sprite].spriteframes + PlayerState->GetFrame()]; - ScaleX = skins[PlayerSkin].ScaleX; - ScaleY = skins[PlayerSkin].ScaleY; - } - - if (sprframe != NULL) - { - FTexture *tex = TexMan(sprframe->Texture[0]); - if (tex != NULL && tex->UseType != FTexture::TEX_Null) - { - if (tex->Rotations != 0xFFFF) - { - tex = TexMan(SpriteFrames[tex->Rotations].Texture[PlayerRotation]); - } - screen->DrawTexture (tex, - (320 - 52 - 32 + xo - 160)*CleanXfac + (SCREENWIDTH)/2, - (PSetupDef.y + LINEHEIGHT*3 + 57 - 104)*CleanYfac + (SCREENHEIGHT/2), - DTA_DestWidth, MulScale16 (tex->GetWidth() * CleanXfac, ScaleX), - DTA_DestHeight, MulScale16 (tex->GetHeight() * CleanYfac, ScaleY), - DTA_Translation, translationtables[TRANSLATION_Players](MAXPLAYERS), - TAG_DONE); - } - } - - const char *str = "PRESS " TEXTCOLOR_WHITE "SPACE"; - screen->DrawText (SmallFont, CR_GOLD, 320 - 52 - 32 - - SmallFont->StringWidth (str)/2, - PSetupDef.y + LINEHEIGHT*3 + 76, str, - DTA_Clean, true, TAG_DONE); - str = PlayerRotation ? "TO SEE FRONT" : "TO SEE BACK"; - screen->DrawText (SmallFont, CR_GOLD, 320 - 52 - 32 - - SmallFont->StringWidth (str)/2, - PSetupDef.y + LINEHEIGHT*3 + 76 + SmallFont->GetHeight (), str, - DTA_Clean, true, TAG_DONE); - } - - // Draw player color selection and sliders - FPlayerColorSet *colorset = P_GetPlayerColorSet(PlayerClass->Type->TypeName, players[consoleplayer].userinfo.colorset); - x = SmallFont->StringWidth("Color") + 8 + PSetupDef.x; - screen->DrawText(SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*2+yo, "Color", DTA_Clean, true, TAG_DONE); - screen->DrawText(SmallFont, value, x, PSetupDef.y + LINEHEIGHT*2+yo, - colorset != NULL ? colorset->Name.GetChars() : "Custom", DTA_Clean, true, TAG_DONE); - - // Only show the sliders for a custom color set. - if (colorset == NULL) - { - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + int(LINEHEIGHT*2.875)+yo, "Red", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + int(LINEHEIGHT*3.5)+yo, "Green", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + int(LINEHEIGHT*4.125)+yo, "Blue", DTA_Clean, true, TAG_DONE); - - x = SmallFont->StringWidth ("Green") + 8 + PSetupDef.x; - color = players[consoleplayer].userinfo.color; - - M_DrawPlayerSlider (x, PSetupDef.y + int(LINEHEIGHT*2.875)+yo, RPART(color)); - M_DrawPlayerSlider (x, PSetupDef.y + int(LINEHEIGHT*3.5)+yo, GPART(color)); - M_DrawPlayerSlider (x, PSetupDef.y + int(LINEHEIGHT*4.125)+yo, BPART(color)); - } - - // [GRB] Draw class setting - int pclass = players[consoleplayer].userinfo.PlayerClass; - x = SmallFont->StringWidth ("Class") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*5+yo, "Class", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*5+yo, - pclass == -1 ? "Random" : PlayerClasses[pclass].Type->Meta.GetMetaString (APMETA_DisplayName), - DTA_Clean, true, TAG_DONE); - - // Draw skin setting - x = SmallFont->StringWidth ("Skin") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*6+yo, "Skin", DTA_Clean, true, TAG_DONE); - if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || - players[consoleplayer].userinfo.PlayerClass == -1) - { - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*6+yo, "Base", DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*6+yo, - skins[PlayerSkin].name, DTA_Clean, true, TAG_DONE); - } - - // Draw gender setting - x = SmallFont->StringWidth ("Gender") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*7+yo, "Gender", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*7+yo, - genders[players[consoleplayer].userinfo.gender], DTA_Clean, true, TAG_DONE); - - // Draw autoaim setting - x = SmallFont->StringWidth ("Autoaim") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*8+yo, "Autoaim", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*8+yo, - autoaim == 0 ? "Never" : - autoaim <= 0.25 ? "Very Low" : - autoaim <= 0.5 ? "Low" : - autoaim <= 1 ? "Medium" : - autoaim <= 2 ? "High" : - autoaim <= 3 ? "Very High" : "Always", - DTA_Clean, true, TAG_DONE); - - // Draw Switch on Pickup setting - x = SmallFont->StringWidth ("Switch on Pickup") + 8 + PSetupDef.x; - screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*9+yo, "Switch on Pickup", DTA_Clean, true, TAG_DONE); - screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT*9+yo, - neverswitchonpickup == false ? "Yes" : "No", - DTA_Clean, true, TAG_DONE); -} - -// A 32x32 cloud rendered with Photoshop, plus some other filters -static BYTE pattern1[1024] = -{ - 5, 9, 7,10, 9,15, 9, 7, 8,10, 5, 3, 5, 7, 9, 8,14, 8, 4, 7, 8, 9, 5, 7,14, 7, 0, 7,13,13, 9, 6, - 2, 7, 9, 7, 7,10, 8, 8,11,10, 6, 7,10, 7, 5, 6, 6, 4, 7,13,15,16,11,15,11, 8, 0, 4,13,22,17,11, - 5, 9, 9, 7, 9,10, 4, 3, 6, 7, 8, 6, 5, 4, 2, 2, 1, 4, 6,11,15,15,14,13,17, 9, 5, 9,11,12,17,20, - 9,16, 9, 8,12,13, 7, 3, 7, 9, 5, 4, 2, 5, 5, 5, 7,11, 6, 7, 6,13,17,10,10, 9,12,17,14,12,16,15, - 15,13, 5, 3, 9,10, 4,10,12,12, 7, 9, 8, 8, 8,10, 7, 6, 5, 5, 5, 6,11, 9, 3,13,16,18,21,16,23,18, - 23,13, 0, 0, 0, 0, 0,12,18,14,15,16,13, 7, 7, 5, 9, 6, 6, 8, 4, 0, 0, 0, 0,14,19,17,14,20,21,25, - 19,20,14,13, 7, 5,13,19,14,13,17,15,14, 7, 3, 5, 6,11, 7, 7, 8, 8,10, 9, 9,18,17,15,14,15,18,16, - 16,29,24,23,18, 9,17,20,11, 5,12,15,15,12, 6, 3, 4, 6, 7,10,13,18,18,19,16,12,17,19,23,16,14,14, - 9,18,20,26,19, 5,18,18,10, 5,12,15,14,17,11, 6,11, 9,10,13,10,20,24,20,21,20,14,18,15,22,20,19, - 0, 6,16,18, 8, 7,15,18,10,13,17,17,13,11,15,11,19,12,13,10, 4,15,19,21,21,24,14, 9,17,20,24,17, - 18,17, 7, 7,16,21,22,15, 5,14,20,14,13,21,13, 8,12,14, 7, 8,11,15,13,11,16,17, 7, 5,12,17,19,14, - 25,23,17,16,23,18,15, 7, 0, 6,11, 6,11,15,11, 7,12, 7, 4,10,16,13, 7, 7,15,13, 9,15,21,14, 5, 0, - 18,22,21,21,21,22,12, 6,14,20,15, 6,10,19,13, 8, 7, 3, 7,12,14,16, 9,12,22,15,12,18,24,19,17, 9, - 0,15,18,21,17,25,14,13,19,21,21,11, 6,13,16,16,12,10,12,11,13,20,14,13,18,13, 9,15,16,25,31,20, - 5,20,24,16, 7,14,14,11,18,19,19, 6, 0, 5,11,14,17,16,19,14,15,21,19,15,14,14, 8, 0, 7,24,18,16, - 9,17,15, 7, 6,14,12, 7,14,16,11, 4, 7, 6,13,16,15,13,12,20,21,20,21,17,18,26,14, 0,13,23,21,11, - 9,12,18,11,15,21,13, 8,13,13,10, 7,13, 8, 8,19,13, 7, 4,15,19,18,14,12,14,15, 8, 6,16,22,22,15, - 9,17,14,19,15,14,15, 9,11, 9, 6, 8,14,13,13,12, 5, 0, 0, 6,12,13, 7, 7, 9, 7, 0,12,21,16,15,18, - 15,16,18,11, 6, 8,15, 9, 2, 0, 5,10,10,16, 9, 0, 4,12,15, 9,12, 9, 7, 7,12, 7, 0, 6,12, 6, 9,13, - 12,19,15,14,11, 7, 8, 9,12,10, 5, 5, 7,12,12,10,14,16,16,11, 8,12,10,12,10, 8,10,10,14,12,16,16, - 16,17,20,22,12,15,12,14,19,11, 6, 5,10,13,17,17,21,19,15, 9, 6, 9,15,18,10,10,18,14,20,15,16,17, - 11,19,19,18,19,14,17,13,12,12, 7,11,18,17,16,15,19,19,10, 2, 0, 8,15,12, 8,11,12,10,19,20,19,19, - 6,14,18,13,13,16,16,12, 5, 8,10,12,10,13,18,12, 9,10, 7, 6, 5,11, 8, 6, 7,13,16,13,10,15,20,14, - 0, 5,12,12, 4, 0, 9,16, 9,10,12, 8, 0, 9,13, 9, 0, 2, 4, 7,10, 6, 7, 3, 4,11,16,18,10,11,21,21, - 16,13,11,15, 8, 0, 5, 9, 8, 7, 6, 3, 0, 9,17, 9, 0, 0, 0, 3, 5, 4, 3, 5, 7,15,16,16,17,14,22,22, - 24,14,15,12, 9, 0, 5,10, 8, 4, 7,12,10,11,12, 7, 6, 8, 6, 5, 7, 8, 8,11,13,10,15,14,12,18,20,16, - 16,17,17,18,12, 9,12,16,10, 5, 6,20,13,15, 8, 4, 8, 9, 8, 7, 9,11,12,17,16,16,11,10, 9,10, 5, 0, - 0,14,18,18,15,16,14, 9,10, 9, 9,15,14,10, 4, 6,10, 8, 8, 7,10, 9,10,16,18,10, 0, 0, 7,12,10, 8, - 0,14,19,14, 9,11,11, 8, 8,10,15, 9,10, 7, 4,10,13, 9, 7, 5, 5, 7, 7, 7,13,13, 5, 5,14,22,18,16, - 0,10,14,10, 3, 6, 5, 6, 8, 9, 8, 9, 5, 9, 8, 9, 6, 8, 8, 8, 1, 0, 0, 0, 9,17,12,12,17,19,20,13, - 6,11,17,11, 5, 5, 8,10, 6, 5, 6, 6, 3, 7, 9, 7, 6, 8,12,10, 4, 8, 6, 6,11,16,16,15,16,17,17,16, - 11, 9,10,10, 5, 6,12,10, 5, 1, 6,10, 5, 3, 3, 5, 4, 7,15,10, 7,13, 7, 8,15,11,15,15,15, 8,11,15, -}; - -// Just a 32x32 cloud rendered with the standard Photoshop filter -static BYTE pattern2[1024] = -{ - 9, 9, 8, 8, 8, 8, 6, 6,13,13,11,21,19,21,23,18,23,24,19,19,24,17,18,12, 9,14, 8,12,12, 5, 8, 6, - 11,10, 6, 7, 8, 8, 9,13,10,11,17,15,23,22,23,22,20,26,27,26,17,21,20,14,12, 8,11, 8,11, 7, 8, 7, - 6, 9,13,13,10, 9,13, 7,12,13,16,19,16,20,22,25,22,25,27,22,21,23,15,10,14,14,15,13,12, 8,12, 6, - 6, 7,12,12,12,16, 9,12,12,15,16,11,21,24,19,24,23,26,28,27,26,21,14,15, 7, 7,10,15,12,11,10, 9, - 7,14,11,16,12,18,16,14,16,14,11,14,15,21,23,17,20,18,26,24,27,18,20,11,11,14,10,17,17,10, 6,10, - 13, 9,14,10,13,11,14,15,18,15,15,12,19,19,20,18,22,20,19,22,19,19,19,20,17,15,15,11,16,14,10, 8, - 13,16,12,16,17,19,17,18,15,19,14,18,15,14,15,17,21,19,23,18,23,22,18,18,17,15,15,16,12,12,15,10, - 10,12,14,10,16,11,18,15,21,20,20,17,18,19,16,19,14,20,19,14,19,25,22,21,22,24,18,12, 9, 9, 8, 6, - 10,10,13, 9,15,13,20,19,22,18,18,17,17,21,21,13,13,12,19,18,16,17,27,26,22,23,20,17,12,11, 8, 9, - 7,13,14,15,11,13,18,22,19,23,23,20,22,24,21,14,12,16,17,19,18,18,22,18,24,23,19,17,16,14, 8, 7, - 12,12, 8, 8,16,20,26,25,28,28,22,29,23,22,21,18,13,16,15,15,20,17,25,24,19,17,17,17,15,10, 8, 9, - 7,12,15,11,17,20,25,25,25,29,30,31,28,26,18,16,17,18,20,21,22,20,23,19,18,19,10,16,16,11,11, 8, - 5, 6, 8,14,14,17,17,21,27,23,27,31,27,22,23,21,19,19,21,19,20,19,17,22,13,17,12,15,10,10,12, 6, - 8, 9, 8,14,15,16,15,18,27,26,23,25,23,22,18,21,20,17,19,20,20,16,20,14,15,13,12, 8, 8, 7,11,13, - 7, 6,11,11,11,13,15,22,25,24,26,22,24,26,23,18,24,24,20,18,20,16,17,12,12,12,10, 8,11, 9, 6, 8, - 9,10, 9, 6, 5,14,16,19,17,21,26,20,23,19,19,17,20,21,26,25,23,21,17,13,12, 5,13,11, 7,12,10,12, - 6, 5, 4,10,11, 9,10,13,17,20,20,18,23,26,27,20,21,24,20,19,24,20,18,10,11, 3, 6,13, 9, 6, 8, 8, - 1, 2, 2,11,13,13,11,16,16,16,19,21,20,23,22,28,21,20,19,18,23,16,18, 7, 5, 9, 7, 6, 5,10, 8, 8, - 0, 0, 6, 9,11,15,12,12,19,18,19,26,22,24,26,30,23,22,22,16,20,19,12,12, 3, 4, 6, 5, 4, 7, 2, 4, - 2, 0, 0, 7,11, 8,14,13,15,21,26,28,25,24,27,26,23,24,22,22,15,17,12, 8,10, 7, 7, 4, 0, 5, 0, 1, - 1, 2, 0, 1, 9,14,13,10,19,24,22,29,30,28,30,30,31,23,24,19,17,14,13, 8, 8, 8, 1, 4, 0, 0, 0, 3, - 5, 2, 4, 2, 9, 8, 8, 8,18,23,20,27,30,27,31,25,28,30,28,24,24,15,11,14,10, 3, 4, 3, 0, 0, 1, 3, - 9, 3, 4, 3, 5, 6, 8,13,14,23,21,27,28,27,28,27,27,29,30,24,22,23,13,15, 8, 6, 2, 0, 4, 3, 4, 1, - 6, 5, 5, 3, 9, 3, 6,14,13,16,23,26,28,23,30,31,28,29,26,27,21,20,15,15,13, 9, 1, 0, 2, 0, 5, 8, - 8, 4, 3, 7, 2, 0,10, 7,10,14,21,21,29,28,25,27,30,28,25,24,27,22,19,13,10, 5, 0, 0, 0, 0, 0, 7, - 7, 6, 7, 0, 2, 2, 5, 6,15,11,19,24,22,29,27,31,30,30,31,28,23,18,14,14, 7, 5, 0, 0, 1, 0, 1, 0, - 5, 5, 5, 0, 0, 4, 5,11, 7,10,13,20,21,21,28,31,28,30,26,28,25,21, 9,12, 3, 3, 0, 2, 2, 2, 0, 1, - 3, 3, 0, 2, 0, 3, 5, 3,11,11,16,19,19,27,26,26,30,27,28,26,23,22,16, 6, 2, 2, 3, 2, 0, 2, 4, 0, - 0, 0, 0, 3, 3, 1, 0, 4, 5, 9,11,16,24,20,28,26,28,24,28,25,22,21,16, 5, 7, 5, 7, 3, 2, 3, 3, 6, - 0, 0, 2, 0, 2, 0, 4, 3, 8,12, 9,17,16,23,23,27,27,22,26,22,21,21,13,14, 5, 3, 7, 3, 2, 4, 6, 1, - 2, 5, 6, 4, 0, 1, 5, 8, 7, 6,15,17,22,20,24,28,23,25,20,21,18,16,13,15,13,10, 8, 5, 5, 9, 3, 7, - 7, 7, 0, 5, 1, 6, 7, 9,12, 9,12,21,22,25,24,22,23,25,24,18,24,22,17,13,10, 9,10, 9, 6,11, 6, 5, -}; - -const FTexture::Span FBackdropTexture::DummySpan[2] = { { 0, 160 }, { 0, 0 } }; - -FBackdropTexture::FBackdropTexture() -{ - Width = 144; - Height = 160; - WidthBits = 8; - HeightBits = 8; - WidthMask = 255; - LastRenderTic = 0; - - time1 = ANGLE_1*180; - time2 = ANGLE_1*56; - time3 = ANGLE_1*99; - time4 = ANGLE_1*1; - t1ang = ANGLE_90; - t2ang = 0; - z1ang = 0; - z2ang = ANGLE_90/2; -} - -bool FBackdropTexture::CheckModified() -{ - return LastRenderTic != gametic; -} - -void FBackdropTexture::Unload() -{ -} - -const BYTE *FBackdropTexture::GetColumn(unsigned int column, const Span **spans_out) -{ - if (LastRenderTic != gametic) - { - Render(); - } - column = clamp(column, 0u, 143u); - if (spans_out != NULL) - { - *spans_out = DummySpan; - } - return Pixels + column*160; -} - -const BYTE *FBackdropTexture::GetPixels() -{ - if (LastRenderTic != gametic) - { - Render(); - } - return Pixels; -} - -// This is one plasma and two rotozoomers. I think it turned out quite awesome. -void FBackdropTexture::Render() -{ - BYTE *from; - int width, height, pitch; - - width = 160; - height = 144; - pitch = width; - - int x, y; - - const angle_t a1add = ANGLE_1/2; - const angle_t a2add = ANGLE_MAX-ANGLE_1; - const angle_t a3add = ANGLE_1*5/7; - const angle_t a4add = ANGLE_MAX-ANGLE_1*4/3; - - const angle_t t1add = ANGLE_MAX-ANGLE_1*2; - const angle_t t2add = ANGLE_MAX-ANGLE_1*3+ANGLE_1/6; - const angle_t t3add = ANGLE_1*16/7; - const angle_t t4add = ANGLE_MAX-ANGLE_1*2/3; - const angle_t x1add = 5<>ANGLETOFINESHIFT]>>2)+FRACUNIT/2; - fixed_t z2 = (finecosine[z1ang>>ANGLETOFINESHIFT]>>2)+FRACUNIT*3/4; - - tc = MulScale5 (finecosine[t1ang>>ANGLETOFINESHIFT], z1); - ts = MulScale5 (finesine[t1ang>>ANGLETOFINESHIFT], z1); - uc = MulScale5 (finecosine[t2ang>>ANGLETOFINESHIFT], z2); - us = MulScale5 (finesine[t2ang>>ANGLETOFINESHIFT], z2); - - ltx = -width/2*tc; - lty = -width/2*ts; - lux = -width/2*uc; - luy = -width/2*us; - - for (y = 0; y < height; ++y) - { - a1 = time1; - a2 = time2; - c3 = finecosine[a3>>ANGLETOFINESHIFT]; - c4 = finecosine[a4>>ANGLETOFINESHIFT]; - tx = ltx - (y-height/2)*ts; - ty = lty + (y-height/2)*tc; - ux = lux - (y-height/2)*us; - uy = luy + (y-height/2)*uc; - for (x = 0; x < width; ++x) - { - c1 = finecosine[a1>>ANGLETOFINESHIFT]; - c2 = finecosine[a2>>ANGLETOFINESHIFT]; - from[x] = ((c1 + c2 + c3 + c4) >> (FRACBITS+3-7)) + 128 // plasma - + pattern1[(tx>>27)+((ty>>22)&992)] // rotozoomer 1 - + pattern2[(ux>>27)+((uy>>22)&992)]; // rotozoomer 2 - tx += tc; - ty += ts; - ux += uc; - uy += us; - a1 += a1add; - a2 += a2add; - } - a3 += a3add; - a4 += a4add; - from += pitch; - } - - time1 += t1add; - time2 += t2add; - time3 += t3add; - time4 += t4add; - t1ang += x1add; - t2ang += x2add; - z1ang += z1add; - z2ang += z2add; - - LastRenderTic = gametic; -} - -static void M_ChangeClass (int choice) -{ - if (PlayerClasses.Size () == 1) - { - return; - } - - int type = players[consoleplayer].userinfo.PlayerClass; - - if (!choice) - type = (type < 0) ? (int)PlayerClasses.Size () - 1 : type - 1; - else - type = (type < (int)PlayerClasses.Size () - 1) ? type + 1 : -1; - - cvar_set ("playerclass", type < 0 ? "Random" : - PlayerClasses[type].Type->Meta.GetMetaString (APMETA_DisplayName)); -} - -static void M_ChangeSkin (int choice) -{ - if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || - players[consoleplayer].userinfo.PlayerClass == -1) - { - return; - } - - do - { - if (!choice) - PlayerSkin = (PlayerSkin == 0) ? (int)numskins - 1 : PlayerSkin - 1; - else - PlayerSkin = (PlayerSkin < (int)numskins - 1) ? PlayerSkin + 1 : 0; - } while (!PlayerClass->CheckSkin (PlayerSkin)); - - R_GetPlayerTranslation (players[consoleplayer].userinfo.color, - P_GetPlayerColorSet(PlayerClass->Type->TypeName, players[consoleplayer].userinfo.colorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); - - cvar_set ("skin", skins[PlayerSkin].name); -} - -static void M_ChangeGender (int choice) -{ - int gender = players[consoleplayer].userinfo.gender; - - if (!choice) - gender = (gender == 0) ? 2 : gender - 1; - else - gender = (gender == 2) ? 0 : gender + 1; - - cvar_set ("gender", genders[gender]); -} - -static void M_ChangeAutoAim (int choice) -{ - static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 }; - float aim = autoaim; - int i; - - if (!choice) { - // Select a lower autoaim - - for (i = 6; i >= 1; i--) - { - if (aim >= ranges[i]) - { - aim = ranges[i - 1]; - break; - } - } - } - else - { - // Select a higher autoaim - - for (i = 5; i >= 0; i--) - { - if (aim >= ranges[i]) - { - aim = ranges[i + 1]; - break; - } - } - } - - autoaim = aim; -} - -static void M_ChangeSwitchPickup (int choice) -{ - if (!choice) - neverswitchonpickup = (neverswitchonpickup == 1) ? 0 : 1; - else - neverswitchonpickup = (neverswitchonpickup == 0) ? 1 : 0; -} - -static void M_EditPlayerName (int choice) -{ - // we are going to be intercepting all chars - genStringEnter = 2; - genStringEnd = M_PlayerNameChanged; - genStringCancel = M_PlayerNameNotChanged; - genStringLen = MAXPLAYERNAME; - - saveSlot = 0; - saveCharIndex = strlen (savegamestring); -} - -static void M_PlayerNameNotChanged () -{ - strcpy (savegamestring, name); -} - -static void M_PlayerNameChanged (FSaveGameNode *dummy) -{ - const char *p; - FString command("name \""); - - // Escape any backslashes or quotation marks before sending the name to the console. - for (p = savegamestring; *p != '\0'; ++p) - { - if (*p == '"' || *p == '\\') - { - command << '\\'; - } - command << *p; - } - command << '"'; - C_DoCommand (command); -} - -static void M_ChangePlayerTeam (int choice) -{ - if (!choice) - { - if (team == 0) - { - team = TEAM_NONE; - } - else if (team == TEAM_NONE) - { - team = Teams.Size () - 1; - } - else - { - team = team - 1; - } - } - else - { - if (team == int(Teams.Size () - 1)) - { - team = TEAM_NONE; - } - else if (team == TEAM_NONE) - { - team = 0; - } - else - { - team = team + 1; - } - } -} - -static void M_ChangeColorSet (int choice) -{ - int curpos = (int)PlayerColorSets.Size(); - int mycolorset = players[consoleplayer].userinfo.colorset; - while (--curpos >= 0) - { - if (PlayerColorSets[curpos] == mycolorset) - break; - } - if (choice == 0) - { - curpos--; - } - else - { - curpos++; - } - if (curpos < -1) - { - curpos = (int)PlayerColorSets.Size() - 1; - } - else if (curpos >= (int)PlayerColorSets.Size()) - { - curpos = -1; - } - mycolorset = (curpos >= 0) ? PlayerColorSets[curpos] : -1; - - // disable the sliders if a valid colorset is selected - PlayerSetupMenu[PSM_RED].status = - PlayerSetupMenu[PSM_GREEN].status = - PlayerSetupMenu[PSM_BLUE].status = (mycolorset == -1? 2:-1); - - char command[24]; - mysnprintf(command, countof(command), "colorset %d", mycolorset); - C_DoCommand(command); - R_GetPlayerTranslation(players[consoleplayer].userinfo.color, - P_GetPlayerColorSet(PlayerClass->Type->TypeName, mycolorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); -} - -static void SendNewColor (int red, int green, int blue) -{ - char command[24]; - - mysnprintf (command, countof(command), "color \"%02x %02x %02x\"", red, green, blue); - C_DoCommand (command); - R_GetPlayerTranslation(MAKERGB (red, green, blue), - P_GetPlayerColorSet(PlayerClass->Type->TypeName, players[consoleplayer].userinfo.colorset), - &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); -} - -static void M_SlidePlayerRed (int choice) -{ - int color = players[consoleplayer].userinfo.color; - int red = RPART(color); - - if (choice == 0) { - red -= 16; - if (red < 0) - red = 0; - } else { - red += 16; - if (red > 255) - red = 255; - } - - SendNewColor (red, GPART(color), BPART(color)); -} - -static void M_SlidePlayerGreen (int choice) -{ - int color = players[consoleplayer].userinfo.color; - int green = GPART(color); - - if (choice == 0) { - green -= 16; - if (green < 0) - green = 0; - } else { - green += 16; - if (green > 255) - green = 255; - } - - SendNewColor (RPART(color), green, BPART(color)); -} - -static void M_SlidePlayerBlue (int choice) -{ - int color = players[consoleplayer].userinfo.color; - int blue = BPART(color); - - if (choice == 0) { - blue -= 16; - if (blue < 0) - blue = 0; - } else { - blue += 16; - if (blue > 255) - blue = 255; - } - - SendNewColor (RPART(color), GPART(color), blue); -} - - -// -// Menu Functions -// -void M_StartMessage (const char *string, void (*routine)(int)) -{ - C_HideConsole (); - messageLastMenuActive = menuactive; - messageToPrint = 1; - messageString = string; - messageRoutine = routine; - messageSelection = 0; - if (menuactive == MENU_Off) - { - M_ActivateMenuInput (); - } - if (messageRoutine != NULL) - { - S_StopSound (CHAN_VOICE); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE); - } - return; -} - -void M_EndMessage(int key) -{ - menuactive = messageLastMenuActive; - messageToPrint = 0; - if (messageRoutine != NULL) - { - messageRoutine(key); - } - if (menuactive != MENU_Off) - { - M_DeactivateMenuInput(); - } - SB_state = screen->GetPageCount(); // refresh the status bar - BorderNeedRefresh = screen->GetPageCount(); - S_Sound(CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); -} - - -// -// Find string height from hu_font chars -// -int M_StringHeight (FFont *font, const char *string) -{ - int h; - int height = font->GetHeight (); - - h = height; - while (*string) - { - if ((*string++) == '\n') - h += height; - } - - return h; -} - - - -// -// CONTROL PANEL -// - -// -// M_Responder -// -bool M_Responder (event_t *ev) -{ - int ch; - int i; - EMenuKey mkey = NUM_MKEYS; - bool keyup = true; - - ch = -1; - - if (chatmodeon) - { - return false; - } - if (menuactive == MENU_Off && ev->type == EV_KeyDown) - { - // Pop-up menu? - if (ev->data1 == KEY_ESCAPE) - { - M_StartControlPanel(true, true); - return true; - } - // If devparm is set, pressing F1 always takes a screenshot no matter - // what it's bound to. (for those who don't bother to read the docs) - if (devparm && ev->data1 == KEY_F1) - { - G_ScreenShot(NULL); - return true; - } - return false; - } - if (menuactive == MENU_WaitKey && OptionsActive) - { - M_OptResponder(ev); - return true; - } - if (menuactive != MENU_On && menuactive != MENU_OnNoPause && - !genStringEnter && !messageToPrint) - { - return false; - } - - // There are a few input sources we are interested in: - // - // EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers - // EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard - // EV_GUI_Char : printable characters, which we want in string input mode - // - // This code previously listened for EV_GUI_KeyRepeat to handle repeating - // in the menus, but that doesn't work with gamepads, so now we combine - // the multiple inputs into buttons and handle the repetition manually. - if (ev->type == EV_GUI_Event) - { - // Save game and player name string input - if (genStringEnter) - { - if (ev->subtype == EV_GUI_Char) - { - InputGridOkay = false; - if (saveCharIndex < genStringLen && - (genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8)) - { - savegamestring[saveCharIndex] = (char)ev->data1; - savegamestring[++saveCharIndex] = 0; - } - return true; - } - ch = ev->data1; - if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b') - { - if (saveCharIndex > 0) - { - saveCharIndex--; - savegamestring[saveCharIndex] = 0; - } - } - else if (ev->subtype == EV_GUI_KeyDown) - { - if (ch == GK_ESCAPE) - { - genStringEnter = 0; - genStringCancel(); // [RH] Function to call when escape is pressed - } - else if (ch == '\r') - { - if (savegamestring[0]) - { - genStringEnter = 0; - if (messageToPrint) - M_ClearMenus (); - genStringEnd (SelSaveGame); // [RH] Function to call when enter is pressed - } - } - } - if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) - { - return true; - } - } - if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp) - { - return false; - } - if (ev->subtype == EV_GUI_KeyRepeat) - { - // We do our own key repeat handling but still want to eat the - // OS's repeated keys. - return true; - } - ch = ev->data1; - keyup = ev->subtype == EV_GUI_KeyUp; - if (messageToPrint && messageRoutine == NULL) - { - if (!keyup && !OptionsActive) - { - D_RemoveNextCharEvent(); - M_EndMessage(ch); - return true; - } - } - switch (ch) - { - case GK_ESCAPE: mkey = MKEY_Back; break; - case GK_RETURN: mkey = MKEY_Enter; break; - case GK_UP: mkey = MKEY_Up; break; - case GK_DOWN: mkey = MKEY_Down; break; - case GK_LEFT: mkey = MKEY_Left; break; - case GK_RIGHT: mkey = MKEY_Right; break; - case GK_BACKSPACE: mkey = MKEY_Clear; break; - case GK_PGUP: mkey = MKEY_PageUp; break; - case GK_PGDN: mkey = MKEY_PageDown; break; - default: - if (ch == ' ' && currentMenu == &PSetupDef) - { - mkey = MKEY_Clear; - } - else if (!keyup) - { - if (OptionsActive) - { - M_OptResponder(ev); - } - else - { - ch = tolower (ch); - if (messageToPrint) - { - // Take care of any messages that need input - ch = tolower (ch); - assert(messageRoutine != NULL); - if (ch != ' ' && ch != 'n' && ch != 'y') - { - return false; - } - D_RemoveNextCharEvent(); - M_EndMessage(ch); - return true; - } - else - { - // Search for a menu item associated with the pressed key. - for (i = (itemOn + 1) % currentMenu->numitems; - i != itemOn; - i = (i + 1) % currentMenu->numitems) - { - if (currentMenu->menuitems[i].alphaKey == ch) - { - break; - } - } - if (currentMenu->menuitems[i].alphaKey == ch) - { - itemOn = i; - S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - return true; - } - } - } - } - break; - } - if (!keyup) - { - InputGridOkay = false; - } - } - else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp) - { - keyup = ev->type == EV_KeyUp; - // If this is a button down, it's okay to show the input grid if the - // next action causes us to enter genStringEnter mode. If we are - // already in that mode, then we let M_ButtonHandler() turn it on so - // that it will know if a button press happened while the input grid - // was turned off. - if (!keyup && !genStringEnter) - { - InputGridOkay = true; - } - ch = ev->data1; - switch (ch) - { - case KEY_JOY1: - case KEY_PAD_A: - mkey = MKEY_Enter; - break; - - case KEY_JOY2: - case KEY_PAD_B: - mkey = MKEY_Back; - break; - - case KEY_JOY3: - case KEY_PAD_X: - mkey = MKEY_Clear; - break; - - case KEY_JOY5: - case KEY_PAD_LSHOULDER: - mkey = MKEY_PageUp; - break; - - case KEY_JOY6: - case KEY_PAD_RSHOULDER: - mkey = MKEY_PageDown; - break; - - case KEY_PAD_DPAD_UP: - case KEY_PAD_LTHUMB_UP: - case KEY_JOYAXIS1MINUS: - case KEY_JOYPOV1_UP: - mkey = MKEY_Up; - break; - - case KEY_PAD_DPAD_DOWN: - case KEY_PAD_LTHUMB_DOWN: - case KEY_JOYAXIS1PLUS: - case KEY_JOYPOV1_DOWN: - mkey = MKEY_Down; - break; - - case KEY_PAD_DPAD_LEFT: - case KEY_PAD_LTHUMB_LEFT: - case KEY_JOYAXIS2MINUS: - case KEY_JOYPOV1_LEFT: - mkey = MKEY_Left; - break; - - case KEY_PAD_DPAD_RIGHT: - case KEY_PAD_LTHUMB_RIGHT: - case KEY_JOYAXIS2PLUS: - case KEY_JOYPOV1_RIGHT: - mkey = MKEY_Right; - break; - } - // Any button press will work for messages without callbacks - if (!keyup && messageToPrint && messageRoutine == NULL) - { - M_EndMessage(ch); - return true; - } - } - - if (mkey != NUM_MKEYS) - { - if (keyup) - { - MenuButtons[mkey].ReleaseKey(ch); - } - else - { - MenuButtons[mkey].PressKey(ch); - if (mkey <= MKEY_PageDown) - { - MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; - } - M_ButtonHandler(mkey, false); - } - } - - if (ev->type == EV_GUI_Event && (currentMenu == &SaveDef || currentMenu == &LoadDef)) - { - return M_SaveLoadResponder (ev); - } - - // Eat key downs, but let the rest through. - return !keyup; -} - -void M_ButtonHandler(EMenuKey key, bool repeat) -{ - if (OptionsActive) - { - M_OptButtonHandler(key, repeat); - return; - } - if (key == MKEY_Back) - { - if (genStringEnter) - { - // Cancel string entry. - genStringEnter = 0; - genStringCancel(); - } - else if (messageToPrint) - { - M_EndMessage(GK_ESCAPE); - } - else - { - // Save the cursor position on the current menu, and pop it off the stack - // to go back to the previous menu. - currentMenu->lastOn = itemOn; - M_PopMenuStack(); - } - return; - } - if (messageToPrint) - { - if (key == MKEY_Down || key == MKEY_Up) - { - messageSelection ^= 1; - } - else if (key == MKEY_Enter) - { - M_EndMessage(messageSelection == 0 ? 'y' : 'n'); - } - return; - } - if (genStringEnter) - { - int ch; - - switch (key) - { - case MKEY_Down: - InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT; - break; - - case MKEY_Up: - InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT; - break; - - case MKEY_Right: - InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH; - break; - - case MKEY_Left: - InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH; - break; - - case MKEY_Clear: - if (saveCharIndex > 0) - { - savegamestring[--saveCharIndex] = 0; - } - break; - - case MKEY_Enter: - assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT); - if (InputGridOkay) - { - ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH]; - if (ch == 0) // end - { - if (savegamestring[0] != '\0') - { - genStringEnter = 0; - if (messageToPrint) - { - M_ClearMenus(); - } - genStringEnd(SelSaveGame); - } - } - else if (ch == '\b') // bs - { - if (saveCharIndex > 0) - { - savegamestring[--saveCharIndex] = 0; - } - } - else if (saveCharIndex < genStringLen && - (genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8)) - { - savegamestring[saveCharIndex] = ch; - savegamestring[++saveCharIndex] = 0; - } - } - break; - - default: - break; // Keep GCC quiet - } - InputGridOkay = true; - return; - } - if (currentMenu == &SaveDef || currentMenu == &LoadDef) - { - M_SaveLoadButtonHandler(key); - return; - } - switch (key) - { - case MKEY_Down: - do - { - if (itemOn + 1 >= currentMenu->numitems) - itemOn = 0; - else itemOn++; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } while (currentMenu->menuitems[itemOn].status == -1); - break; - - case MKEY_Up: - do - { - if (itemOn == 0) - itemOn = currentMenu->numitems - 1; - else itemOn--; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } while (currentMenu->menuitems[itemOn].status == -1); - break; - - case MKEY_Left: - if (currentMenu->menuitems[itemOn].routine && - currentMenu->menuitems[itemOn].status == 2) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - currentMenu->menuitems[itemOn].routine(0); - } - break; - - case MKEY_Right: - if (currentMenu->menuitems[itemOn].routine && - currentMenu->menuitems[itemOn].status == 2) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - currentMenu->menuitems[itemOn].routine(1); - } - break; - - case MKEY_Enter: - if (currentMenu->menuitems[itemOn].routine && - currentMenu->menuitems[itemOn].status) - { - currentMenu->lastOn = itemOn; - if (currentMenu->menuitems[itemOn].status == 2) - { - currentMenu->menuitems[itemOn].routine(1); // right arrow - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - else - { - currentMenu->menuitems[itemOn].routine(itemOn); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - } - } - break; - - case MKEY_Clear: - if (currentMenu == &PSetupDef) - { - PlayerRotation ^= 8; - } - break; - - default: - break; // Keep GCC quiet - } -} - -static void M_SaveLoadButtonHandler(EMenuKey key) -{ - if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) - { - return; - } - switch (key) - { - case MKEY_Up: - if (SelSaveGame != SaveGames.Head) - { - if (SelSaveGame == TopSaveGame) - { - TopSaveGame = static_cast(TopSaveGame->Pred); - } - SelSaveGame = static_cast(SelSaveGame->Pred); - } - else - { - SelSaveGame = static_cast(SaveGames.TailPred); - } - M_UnloadSaveData (); - M_ExtractSaveData (SelSaveGame); - break; - - case MKEY_Down: - if (SelSaveGame != SaveGames.TailPred) - { - SelSaveGame = static_cast(SelSaveGame->Succ); - } - else - { - SelSaveGame = TopSaveGame = - static_cast(SaveGames.Head); - } - M_UnloadSaveData (); - M_ExtractSaveData (SelSaveGame); - break; - - case MKEY_Enter: - if (currentMenu == &LoadDef) - { - M_LoadSelect (SelSaveGame); - } - else - { - M_SaveSelect (SelSaveGame); - } - break; - - default: - break; // Keep GCC quiet - } -} - -static bool M_SaveLoadResponder (event_t *ev) -{ - if (ev->subtype != EV_GUI_KeyDown) - { - return false; - } - if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) - { - switch (ev->data1) - { - case GK_F1: - if (!SelSaveGame->Filename.IsEmpty()) - { - char workbuf[512]; - - mysnprintf (workbuf, countof(workbuf), "File on disk:\n%s", SelSaveGame->Filename.GetChars()); - if (SaveComment != NULL) - { - V_FreeBrokenLines (SaveComment); - } - SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, workbuf); - } - break; - - case GK_DEL: - case '\b': - if (SelSaveGame != &NewSaveNode) - { - EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), SelSaveGame->Title, GStrings("PRESSYN")); - - M_StartMessage (EndString, M_DeleteSaveResponse); - } - break; - - case 'N': - if (currentMenu == &SaveDef) - { - SelSaveGame = TopSaveGame = &NewSaveNode; - M_UnloadSaveData (); - } - break; - } - } - return true; -} - -static void M_LoadSelect (const FSaveGameNode *file) -{ - G_LoadGame (file->Filename.GetChars()); - if (gamestate == GS_FULLCONSOLE) - { - gamestate = GS_HIDECONSOLE; - } - if (quickSaveSlot == (FSaveGameNode *)1) - { - quickSaveSlot = SelSaveGame; - } - M_ClearMenus (); - BorderNeedRefresh = screen->GetPageCount (); -} - - -// -// User wants to save. Start string input for M_Responder -// -static void M_CancelSaveName () -{ -} - -static void M_SaveSelect (const FSaveGameNode *file) -{ - // we are going to be intercepting all chars - genStringEnter = 1; - genStringEnd = M_DoSave; - genStringCancel = M_CancelSaveName; - genStringLen = SAVESTRINGSIZE-1; - - if (file != &NewSaveNode) - { - strcpy (savegamestring, file->Title); - } - else - { - // If we are naming a new save, don't start the cursor on "end". - if (InputGridX == INPUTGRID_WIDTH - 1 && InputGridY == INPUTGRID_HEIGHT - 1) - { - InputGridX = 0; - InputGridY = 0; - } - savegamestring[0] = 0; - } - saveCharIndex = strlen (savegamestring); -} - -static void M_DeleteSaveResponse (int choice) -{ - M_ClearSaveStuff (); - if (choice == 'y') - { - FSaveGameNode *next = static_cast(SelSaveGame->Succ); - if (next->Succ == NULL) - { - next = static_cast(SelSaveGame->Pred); - if (next->Pred == NULL) - { - next = NULL; - } - } - - remove (SelSaveGame->Filename.GetChars()); - M_UnloadSaveData (); - SelSaveGame = M_RemoveSaveSlot (SelSaveGame); - M_ExtractSaveData (SelSaveGame); - } -} - -// -// M_StartControlPanel -// -void M_StartControlPanel (bool makeSound, bool wantTop) -{ - // intro might call this repeatedly - if (menuactive == MENU_On) - return; - - for (int i = 0; i < NUM_MKEYS; ++i) - { - MenuButtons[i].ReleaseKey(0); - } - drawSkull = true; - MenuStackDepth = 0; - if (wantTop) - { - M_SetupNextMenu(TopLevelMenu); - } - else - { - // Just a default. The caller ought to call M_SetupNextMenu() next. - currentMenu = TopLevelMenu; - itemOn = currentMenu->lastOn; - } - C_HideConsole (); // [RH] Make sure console goes bye bye. - OptionsActive = false; // [RH] Make sure none of the options menus appear. - M_ActivateMenuInput (); - - if (makeSound) - { - S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); - } -} - - -// -// M_Drawer -// Called after the view has been rendered, -// but before it has been blitted. -// -void M_Drawer () -{ - int i, x, y, max; - PalEntry fade = 0; - - player_t *player = &players[consoleplayer]; - AActor *camera = player->camera; - - if (!screen->Accel2D && camera != NULL && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL)) - { - if (camera->player != NULL) - { - player = camera->player; - } - fade = PalEntry (BYTE(player->BlendA*255), BYTE(player->BlendR*255), BYTE(player->BlendG*255), BYTE(player->BlendB*255)); - } - - // Horiz. & Vertically center string and print it. - if (messageToPrint) - { - int fontheight = SmallFont->GetHeight(); - screen->Dim (fade); - BorderNeedRefresh = screen->GetPageCount (); - SB_state = screen->GetPageCount (); - - FBrokenLines *lines = V_BreakLines (SmallFont, 320, messageString); - y = 100; - - for (i = 0; lines[i].Width >= 0; i++) - y -= SmallFont->GetHeight () / 2; - - for (i = 0; lines[i].Width >= 0; i++) - { - screen->DrawText (SmallFont, CR_UNTRANSLATED, 160 - lines[i].Width/2, y, lines[i].Text, - DTA_Clean, true, TAG_DONE); - y += fontheight; - } - V_FreeBrokenLines (lines); - if (messageRoutine != NULL) - { - y += fontheight; - screen->DrawText(SmallFont, CR_UNTRANSLATED, 160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE); - screen->DrawText(SmallFont, CR_UNTRANSLATED, 160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE); - if (skullAnimCounter < 6) - { - screen->DrawText(ConFont, CR_RED, - (150 - 160) * CleanXfac + screen->GetWidth() / 2, - (y + (fontheight + 1) * messageSelection - 100) * CleanYfac + screen->GetHeight() / 2, - "\xd", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, - TAG_DONE); - } - } - } - else if (menuactive != MENU_Off) - { - if (InfoType == 0 && !OptionsActive) - { - screen->Dim (fade); - } - // For Heretic shareware message: - if (showSharewareMessage) - { - const char *text = GStrings("MNU_ONLYREGISTERED"); - screen->DrawText (SmallFont, CR_WHITE, 160 - SmallFont->StringWidth(text)/2, - 8, text, DTA_Clean, true, TAG_DONE); - } - - BorderNeedRefresh = screen->GetPageCount (); - SB_state = screen->GetPageCount (); - - if (OptionsActive) - { - M_OptDrawer (); - } - else - { - if (currentMenu->routine) - currentMenu->routine(); // call Draw routine - - // DRAW MENU - x = currentMenu->x; - y = currentMenu->y; - max = currentMenu->numitems; - - for (i = 0; i < max; i++) - { - if (currentMenu->menuitems[i].name) - { - if (currentMenu->menuitems[i].fulltext) - { - int color = currentMenu->menuitems[i].textcolor; - if (color == CR_UNTRANSLATED) - { - // The default DBIGFONT is white but Doom's default should be red. - if (gameinfo.gametype & GAME_DoomChex) - { - color = CR_RED; - } - } - const char *text = currentMenu->menuitems[i].name; - if (*text == '$') text = GStrings(text+1); - screen->DrawText (BigFont, color, x, y, text, - DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawTexture (TexMan[currentMenu->menuitems[i].name], x, y, - DTA_Clean, true, TAG_DONE); - } - } - y += LINEHEIGHT; - } - - // DRAW CURSOR - if (drawSkull) - { - if (currentMenu == &PSetupDef) - { - // [RH] Use options menu cursor for the player setup menu. - if (skullAnimCounter < 6) - { - double item; - // The green slider is halfway between lines, and the red and - // blue ones are offset slightly to make room for it. - if (itemOn < 3) - { - item = itemOn; - } - else if (itemOn > 5) - { - item = itemOn - 1; - } - else if (itemOn == 3) - { - item = 2.875; - } - else if (itemOn == 4) - { - item = 3.5; - } - else - { - item = 4.125; - } - screen->DrawText (ConFont, CR_RED, x - 16, - currentMenu->y + int(item*PLAYERSETUP_LINEHEIGHT) + - (!(gameinfo.gametype & (GAME_DoomStrifeChex)) ? 6 : -1), "\xd", - DTA_Clean, true, TAG_DONE); - } - } - else if (gameinfo.gametype & GAME_DoomChex) - { - screen->DrawTexture (TexMan("M_SKULL1"), - x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT, - DTA_Clean, true, TAG_DONE); - } - else if (gameinfo.gametype == GAME_Strife) - { - screen->DrawTexture (TexMan("M_CURS1"), - x - 28, currentMenu->y - 5 + itemOn*LINEHEIGHT, - DTA_Clean, true, TAG_DONE); - } - else - { - screen->DrawTexture (TexMan("M_SLCTR1"), - x + SELECTOR_XOFFSET, - currentMenu->y + itemOn*LINEHEIGHT + SELECTOR_YOFFSET, - DTA_Clean, true, TAG_DONE); - } - } - } - if (genStringEnter && InputGridOkay) - { - M_DrawInputGrid(); - } - } -} - - -static void M_ClearSaveStuff () -{ - M_UnloadSaveData (); - if (SaveGames.Head == &NewSaveNode) - { - SaveGames.RemHead (); - if (SelSaveGame == &NewSaveNode) - { - SelSaveGame = static_cast(SaveGames.Head); - } - if (TopSaveGame == &NewSaveNode) - { - TopSaveGame = static_cast(SaveGames.Head); - } - } - if (quickSaveSlot == (FSaveGameNode *)1) - { - quickSaveSlot = NULL; - } -} - -static void M_DrawInputGrid() -{ - const int cell_width = 18 * CleanXfac; - const int cell_height = 12 * CleanYfac; - const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2; - - // Darken the background behind the character grid. - // Unless we frame it with a border, I think it looks better to extend the - // background across the full width of the screen. - screen->Dim(0, 0.8f, - 0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/, - screen->GetHeight() - 5 * cell_height, - screen->GetWidth() /*13 * cell_width*/, - 5 * cell_height); - - // Highlight the background behind the selected character. - screen->Dim(MAKERGB(255,248,220), 0.6f, - InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2, - InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(), - cell_width, cell_height); - - for (int y = 0; y < INPUTGRID_HEIGHT; ++y) - { - const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(); - for (int x = 0; x < INPUTGRID_WIDTH; ++x) - { - int width; - const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2; - const int ch = InputGridChars[y * INPUTGRID_WIDTH + x]; - FTexture *pic = SmallFont->GetChar(ch, &width); - EColorRange color; - FRemapTable *remap; - - // The highlighted character is yellow; the rest are dark gray. - color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY; - remap = SmallFont->GetColorTranslation(color); - - if (pic != NULL) - { - // Draw a normal character. - screen->DrawTexture(pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, - DTA_Translation, remap, - DTA_CleanNoMove, true, - TAG_DONE); - } - else if (ch == ' ') - { - // Draw the space as a box outline. We also draw it 50% wider than it really is. - const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4; - const int x2 = x1 + width * 3 * CleanXfac / 2; - const int y1 = yy + top_padding; - const int y2 = y1 + SmallFont->GetHeight() * CleanYfac; - const int palentry = remap->Remap[remap->NumEntries*2/3]; - const uint32 palcolor = remap->Palette[remap->NumEntries*2/3]; - screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top - screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom - screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left - screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right - } - else if (ch == '\b' || ch == 0) - { - // Draw the backspace and end "characters". - const char *const str = ch == '\b' ? "BS" : "ED"; - screen->DrawText(SmallFont, color, - xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2, - yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE); - } - } - } -} - -// -// M_ClearMenus -// -void M_ClearMenus () -{ - if (FireTexture) - { - delete FireTexture; - FireTexture = NULL; - } - M_ClearSaveStuff (); - M_DeactivateMenuInput (); - MenuStackDepth = 0; - OptionsActive = false; - InfoType = 0; - drawSkull = true; - M_DemoNoPlay = false; - BorderNeedRefresh = screen->GetPageCount (); -} - - - - -// -// M_SetupNextMenu -// -void M_SetupNextMenu (oldmenu_t *menudef) -{ - MenuStack[MenuStackDepth].menu.old = menudef; - MenuStack[MenuStackDepth].isNewStyle = false; - MenuStack[MenuStackDepth].drawSkull = drawSkull; - MenuStackDepth++; - - currentMenu = menudef; - itemOn = currentMenu->lastOn; -} - - -void M_PopMenuStack (void) -{ - M_DemoNoPlay = false; - InfoType = 0; - M_ClearSaveStuff (); - flagsvar = 0; - if (MenuStackDepth > 1) - { - MenuStackDepth -= 2; - if (MenuStack[MenuStackDepth].isNewStyle) - { - OptionsActive = true; - CurrentMenu = MenuStack[MenuStackDepth].menu.newmenu; - CurrentItem = CurrentMenu->lastOn; - } - else - { - OptionsActive = false; - currentMenu = MenuStack[MenuStackDepth].menu.old; - itemOn = currentMenu->lastOn; - } - drawSkull = MenuStack[MenuStackDepth].drawSkull; - ++MenuStackDepth; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/backup", snd_menuvolume, ATTN_NONE); - } - else - { - M_ClearMenus (); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/clear", snd_menuvolume, ATTN_NONE); - } -} - - -// -// M_Ticker -// -void M_Ticker (void) -{ - if (showSharewareMessage) - { - --showSharewareMessage; - } - if (menuactive == MENU_Off) - { - return; - } - MenuTime++; - if (--skullAnimCounter <= 0) - { - skullAnimCounter = 8; - } - if (currentMenu == &PSetupDef || currentMenu == &ClassMenuDef) - { - M_PlayerSetupTicker(); - } - - for (int i = 0; i < NUM_MKEYS; ++i) - { - if (MenuButtons[i].bDown) - { - if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) - { - MenuButtonTickers[i] = KEY_REPEAT_RATE; - M_ButtonHandler(EMenuKey(i), true); - } - } - } -} - - -// -// M_Init -// -EXTERN_CVAR (Int, screenblocks) - -void M_Init (void) -{ - unsigned int i; - - atterm (M_Deinit); - - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - TopLevelMenu = currentMenu = &MainDef; - if (gameinfo.gametype == GAME_Strife) - { - MainDef.y = 45; - //NewDef.lastOn = 1; - } - } - else - { - TopLevelMenu = currentMenu = &HereticMainDef; - PSetupDef.y -= 7; - LoadDef.y -= 20; - SaveDef.y -= 20; - } - PickPlayerClass (); - OptionsActive = false; - menuactive = MENU_Off; - InfoType = 0; - itemOn = currentMenu->lastOn; - skullAnimCounter = 10; - drawSkull = true; - messageToPrint = 0; - messageString = NULL; - messageLastMenuActive = menuactive; - quickSaveSlot = NULL; - lastSaveSlot = NULL; - strcpy (NewSaveNode.Title, ""); - - underscore[0] = (gameinfo.gametype & (GAME_DoomStrifeChex)) ? '_' : '['; - underscore[1] = '\0'; - - if (gameinfo.gametype & GAME_DoomChex) - { - LINEHEIGHT = 16; - } - else if (gameinfo.gametype == GAME_Strife) - { - LINEHEIGHT = 19; - } - else - { - LINEHEIGHT = 20; - } - - if (!gameinfo.drawreadthis) - { - MainMenu[MainDef.numitems-2] = MainMenu[MainDef.numitems-1]; - MainDef.numitems--; - MainDef.y += 8; - ReadDef.routine = M_DrawReadThis; - ReadDef.x = 330; - ReadDef.y = 165; - //ReadMenu[0].routine = M_FinishReadThis; - } - M_OptInit (); - - // [GRB] Set up player class menu - if (!(gameinfo.gametype == GAME_Hexen && PlayerClasses.Size () == 3 && - PlayerClasses[0].Type->IsDescendantOf (PClass::FindClass (NAME_FighterPlayer)) && - PlayerClasses[1].Type->IsDescendantOf (PClass::FindClass (NAME_ClericPlayer)) && - PlayerClasses[2].Type->IsDescendantOf (PClass::FindClass (NAME_MagePlayer)))) - { - int n = 0; - - for (i = 0; i < PlayerClasses.Size () && n < 7; i++) - { - if (!(PlayerClasses[i].Flags & PCF_NOMENU)) - { - ClassMenuItems[n].name = - PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); - n++; - } - } - - if (n > 1) - { - ClassMenuItems[n].name = "Random"; - ClassMenuDef.numitems = n+1; - } - else - { - if (n == 0) - { - ClassMenuItems[0].name = - PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName); - } - ClassMenuDef.numitems = 1; - } - - if (gameinfo.gametype & (GAME_DoomStrifeChex)) - { - ClassMenuDef.x = 48; - ClassMenuDef.y = 63; - } - else - { - ClassMenuDef.x = 80; - ClassMenuDef.y = 50; - } - if (ClassMenuDef.numitems > 4) - { - ClassMenuDef.y -= LINEHEIGHT; - } - } - - // [RH] Build a palette translation table for the player setup effect - if (gameinfo.gametype != GAME_Hexen) - { - for (i = 0; i < 256; i++) - { - FireRemap.Remap[i] = ColorMatcher.Pick (i/2+32, 0, i/4); - FireRemap.Palette[i] = PalEntry(255, i/2+32, 0, i/4); - } - } - else - { // The reddish color ramp above doesn't look too good with the - // Hexen palette, so Hexen gets a greenish one instead. - for (i = 0; i < 256; ++i) - { - FireRemap.Remap[i] = ColorMatcher.Pick (i/4, i*13/40+7, i/4); - FireRemap.Palette[i] = PalEntry(255, i/4, i*13/40+7, i/4); - } - } -} - -static void PickPlayerClass () -{ - int pclass = 0; - - // [GRB] Pick a class from player class list - if (PlayerClasses.Size () > 1) - { - pclass = players[consoleplayer].userinfo.PlayerClass; - - if (pclass < 0) - { - pclass = (MenuTime>>7) % PlayerClasses.Size (); - } - } - - PlayerClass = &PlayerClasses[pclass]; - P_EnumPlayerColorSets(PlayerClass->Type->TypeName, &PlayerColorSets); -} diff --git a/src/m_menu.h b/src/m_menu.h index 5653b694a..e69de29bb 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -1,280 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Menu widget stuff, episode selection and such. -// -//----------------------------------------------------------------------------- - - -#ifndef __M_MENU_H__ -#define __M_MENU_H__ - -#include "c_cvars.h" - -struct event_t; -struct menu_t; -// -// MENUS -// -// Called by main loop, -// saves config file and calls I_Quit when user exits. -// Even when the menu is not displayed, -// this can resize the view and change game parameters. -// Does all the real work of the menu interaction. -bool M_Responder (event_t *ev); - -// Called by main loop, -// only used for menu (skull cursor) animation. -void M_Ticker (void); - -// Called by main loop, -// draws the menus directly into the screen buffer. -void M_Drawer (void); - -// Called by D_DoomMain, loads the config file. -void M_Init (void); - -void M_Deinit (); - -// Called by intro code to force menu up upon a keypress, -// does nothing if menu is already up. -void M_StartControlPanel (bool makeSound, bool wantTop=false); - -// Turns off the menu -void M_ClearMenus (); - -// [RH] Setup options menu -bool M_StartOptionsMenu (void); - -// [RH] Handle keys for options menu -void M_OptResponder (event_t *ev); - -// [RH] Draw options menu -void M_OptDrawer (void); - -// [RH] Initialize options menu -void M_OptInit (void); - -// [RH] Initialize the video modes menu -void M_InitVideoModesMenu (void); - -void M_SwitchMenu (struct menu_t *menu); - -void M_PopMenuStack (void); - -// [RH] Called whenever the display mode changes -void M_RefreshModesList (); - -void M_ActivateMenuInput (); -void M_DeactivateMenuInput (); - -void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave); - -// -// MENU TYPEDEFS -// -typedef enum { - whitetext, - redtext, - more, - rightmore, - safemore, - rsafemore, - joymore, - slider, - absslider, - inverter, - discrete, - discretes, - cdiscrete, - ediscrete, - control, - screenres, - bitflag, - bitmask, - listelement, - nochoice, - numberedmore, - colorpicker, - intslider, - palettegrid, - joy_sens, - joy_slider, - joy_map, - joy_inverter, - mapcontrol, -} itemtype; - -struct IJoystickConfig; -void UpdateJoystickMenu(IJoystickConfig *selected); - -// Yeargh! It's a monster! -struct menuitem_t -{ - itemtype type; - const char *label; - union { - FBaseCVar *cvar; - FIntCVar *intcvar; - FGUIDCVar *guidcvar; - FColorCVar *colorcvar; - int selmode; - float fval; - int joyselection; - } a; - union { - float min; /* aka numvalues aka invflag */ - float numvalues; - float invflag; - int key1; - char *res1; - int position; - } b; - union { - float max; - int key2; - char *res2; - void *extra; - float discretecenter; // 1 to center or 2 to disable repeat (do I even use centered discretes?) - } c; - union { - float step; - char *res3; - FBoolCVar *graycheck; // for drawing discrete items - } d; - union { - struct value_t *values; - struct valuestring_t *valuestrings; - struct valueenum_t *enumvalues; - char *command; - void (*cfunc)(FBaseCVar *cvar, float newval); - void (*mfunc)(void); - void (*lfunc)(int); - int highlight; - int flagmask; - int joyslidernum; - } e; -}; - -struct menu_t { - const char *texttitle; - int lastOn; - int numitems; - int indent; - menuitem_t *items; - int scrolltop; - int scrollpos; - int y; - bool (*PreDraw)(void); - bool DontDim; - void (*EscapeHandler)(void); -}; - -struct value_t { - float value; - const char *name; -}; - -struct valuestring_t { - float value; - FString name; -}; - -struct valueenum_t { - const char *value; // Value of cvar - const char *name; // Name on menu -}; - -struct oldmenuitem_t -{ - // -1 = no cursor here, 1 = ok, 2 = arrows ok - SBYTE status; - BYTE fulltext; // [RH] Menu name is text, not a graphic - - // hotkey in menu - char alphaKey; - - const char *name; - - // choice = menu item #. - // if status = 2, - // choice=0:leftarrow,1:rightarrow - void (*routine)(int choice); - int textcolor; -}; - -struct oldmenu_t -{ - short numitems; // # of menu items - oldmenuitem_t *menuitems; // menu items - void (*routine)(void); // draw routine - short x; - short y; // x,y of menu - short lastOn; // last item user was on in menu -}; - -struct menustack_t -{ - union { - menu_t *newmenu; - oldmenu_t *old; - } menu; - bool isNewStyle; - bool drawSkull; -}; - -enum EMenuKey -{ - MKEY_Up, - MKEY_Down, - MKEY_Left, - MKEY_Right, - MKEY_PageUp, - MKEY_PageDown, - //----------------- Keys past here do not repeat. - MKEY_Enter, - MKEY_Back, // Back to previous menu - MKEY_Clear, // Clear keybinding/flip player sprite preview - - NUM_MKEYS -}; - -void M_ButtonHandler(EMenuKey key, bool repeat); -void M_OptButtonHandler(EMenuKey key, bool repeat); -void M_DrawConText (int color, int x, int y, const char *str); - -extern value_t YesNo[2]; -extern value_t NoYes[2]; -extern value_t OnOff[2]; - -extern menustack_t MenuStack[16]; -extern int MenuStackDepth; - -extern bool OptionsActive; -extern int skullAnimCounter; - -extern menu_t *CurrentMenu; -extern int CurrentItem; - -#define MAX_EPISODES 8 - -extern oldmenuitem_t EpisodeMenu[MAX_EPISODES]; -extern bool EpisodeNoSkill[MAX_EPISODES]; -extern char EpisodeMaps[MAX_EPISODES][9]; -extern oldmenu_t EpiDef; - -#endif diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 324095d5c..d070fdd52 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -85,8 +85,6 @@ CVAR(String, screenshot_type, "png", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); CVAR(String, screenshot_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); EXTERN_CVAR(Bool, longsavemessages); -extern void FreeKeySections(); - static long ParseCommandLine (const char *args, int *argc, char **argv); // @@ -420,7 +418,6 @@ void M_LoadDefaults () { GameConfig = new FGameConfigFile; GameConfig->DoGlobalSetup (); - atterm (FreeKeySections); atterm (M_SaveDefaultsFinal); } diff --git a/src/m_misc.h b/src/m_misc.h index 1a7b17a96..23905d765 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -43,6 +43,7 @@ void M_LoadDefaults (); bool M_SaveDefaults (const char *filename); void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen); + // Prepends ~/.zdoom to path FString GetUserFile (const char *path); diff --git a/src/m_options.cpp b/src/m_options.cpp index fc5f1b7bb..e69de29bb 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -1,3958 +0,0 @@ -/* -** m_options.cpp -** New options menu code -** -**--------------------------------------------------------------------------- -** Copyright 1998-2009 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** Sorry this got so convoluted. It was originally much cleaner until -** I started adding all sorts of gadgets to the menus. I might someday -** make a project of rewriting the entire menu system using Amiga-style -** taglists to describe each menu item. We'll see... (Probably not.) -*/ - -#include "templates.h" - -#include "doomdef.h" -#include "gstrings.h" - -#include "c_console.h" -#include "c_dispatch.h" -#include "c_bind.h" - -#include "d_main.h" -#include "d_gui.h" - -#include "i_system.h" -#include "i_video.h" - -#include "i_music.h" -#include "i_input.h" -#include "m_joy.h" - -#include "v_video.h" -#include "v_text.h" -#include "w_wad.h" -#include "gi.h" - -#include "r_local.h" -#include "v_palette.h" -#include "gameconfigfile.h" - -#include "hu_stuff.h" - -#include "g_game.h" - -#include "m_argv.h" -#include "m_swap.h" - -#include "s_sound.h" - -#include "doomstat.h" - -#include "m_misc.h" -#include "hardware.h" -#include "sc_man.h" -#include "cmdlib.h" -#include "d_event.h" - -#include "sbar.h" - -// Data. -#include "m_menu.h" - -extern FButtonStatus MenuButtons[NUM_MKEYS]; - -EXTERN_CVAR(Bool, nomonsterinterpolation) -EXTERN_CVAR(Int, showendoom) -EXTERN_CVAR(Bool, hud_althud) -EXTERN_CVAR(Int, compatmode) -EXTERN_CVAR (Bool, vid_vsync) -EXTERN_CVAR(Int, displaynametags) -EXTERN_CVAR (Int, snd_channels) - -// -// defaulted values -// -CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE) - -// Show messages has default, 0 = off, 1 = on -CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) -EXTERN_CVAR (Bool, longsavemessages) -EXTERN_CVAR (Bool, screenshot_quiet) - -EXTERN_CVAR (Bool, cl_run) -EXTERN_CVAR (Int, crosshair) -EXTERN_CVAR (Bool, freelook) -EXTERN_CVAR (Int, sv_smartaim) -EXTERN_CVAR (Int, am_colorset) -EXTERN_CVAR (Bool, am_showkeys) -EXTERN_CVAR (Int, vid_aspect) - -static void CalcIndent (menu_t *menu); - -void M_ChangeMessages (); -void M_SizeDisplay (int diff); - -int M_StringHeight (char *string); - -EColorRange LabelColor; -EColorRange ValueColor; -EColorRange MoreColor; - -static bool CanScrollUp; -static bool CanScrollDown; -static int VisBottom; - -value_t YesNo[2] = { - { 0.0, "No" }, - { 1.0, "Yes" } -}; - -value_t NoYes[2] = { - { 0.0, "Yes" }, - { 1.0, "No" } -}; - -value_t OnOff[2] = { - { 0.0, "Off" }, - { 1.0, "On" } -}; - -value_t OffOn[2] = { - { 0.0, "On" }, - { 1.0, "Off" } -}; - -value_t CompatModes[] = { - { 0.0, "Default" }, - { 1.0, "Doom" }, - { 2.0, "Doom (strict)" }, - { 3.0, "Boom" }, - { 6.0, "Boom (strict)" }, - { 5.0, "MBF" }, - { 4.0, "ZDoom 2.0.63" }, -}; - -menu_t *CurrentMenu; -int CurrentItem; -static const char *OldMessage; -static itemtype OldType; - -int flagsvar; -enum -{ - SHOW_DMFlags = 1, - SHOW_DMFlags2 = 2, - SHOW_CompatFlags = 4 -}; - -/*======================================= - * - * Confirm Menu - Used by safemore - * - *=======================================*/ -static void ActivateConfirm (const char *text, void (*func)()); -static void ConfirmIsAGo (); - -static menuitem_t ConfirmItems[] = { - { whitetext,NULL, {NULL}, {0}, {0}, {0}, {NULL} }, - { redtext, "Do you really want to do this?", {NULL}, {0}, {0}, {0}, {NULL} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {NULL} }, - { rightmore,"Yes", {NULL}, {0}, {0}, {0}, {(value_t*)ConfirmIsAGo} }, - { rightmore,"No", {NULL}, {0}, {0}, {0}, {(value_t*)M_PopMenuStack} }, -}; - -static menu_t ConfirmMenu = { - "PLEASE CONFIRM", - 3, - countof(ConfirmItems), - 140, - ConfirmItems, -}; - -/*======================================= - * - * Options Menu - * - *=======================================*/ - -static void StartAutomapMenu (void); -static void CustomizeControls (void); -static void GameplayOptions (void); -static void CompatibilityOptions (void); -static void VideoOptions (void); -static void SoundOptions (void); -static void MouseOptions (void); -static void JoystickOptions (void); -static void GoToConsole (void); -void M_PlayerSetup (void); -void Reset2Defaults (void); -void Reset2Saved (void); - -static void SetVidMode (void); - -static menuitem_t OptionItems[] = -{ - { more, "Customize Controls", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)CustomizeControls} }, - { more, "Mouse options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MouseOptions} }, - { more, "Joystick options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)JoystickOptions} }, - { discrete, "Always Run", {&cl_run}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { more, "Player Setup", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)M_PlayerSetup} }, - { more, "Gameplay Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)GameplayOptions} }, - { more, "Compatibility Options",{NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)CompatibilityOptions} }, - { more, "Automap Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)StartAutomapMenu} }, - { more, "Sound Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)SoundOptions} }, - { more, "Display Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)VideoOptions} }, - { more, "Set video mode", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)SetVidMode} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { safemore, "Reset to defaults", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)Reset2Defaults} }, - { safemore, "Reset to last saved", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)Reset2Saved} }, - { more, "Go to console", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)GoToConsole} }, -}; - -menu_t OptionMenu = -{ - "OPTIONS", - 0, - countof(OptionItems), - 0, - OptionItems, -}; - -/*======================================= - * - * Mouse Menu - * - *=======================================*/ - -EXTERN_CVAR (Bool, use_mouse) -EXTERN_CVAR (Bool, smooth_mouse) -EXTERN_CVAR (Float, m_forward) -EXTERN_CVAR (Float, m_pitch) -EXTERN_CVAR (Float, m_side) -EXTERN_CVAR (Float, m_yaw) -EXTERN_CVAR (Bool, invertmouse) -EXTERN_CVAR (Bool, lookspring) -EXTERN_CVAR (Bool, lookstrafe) -EXTERN_CVAR (Bool, m_noprescale) - -static menuitem_t MouseItems[] = -{ - { discrete, "Enable mouse", {&use_mouse}, {2.0}, {0.0}, {0.0}, {YesNo} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { slider, "Overall sensitivity", {&mouse_sensitivity}, {0.5}, {2.5}, {0.1f}, {NULL} }, - { discrete, "Prescale mouse movement",{&m_noprescale}, {2.0}, {0.0}, {0.0}, {NoYes} }, - { discrete, "Smooth mouse movement",{&smooth_mouse}, {2.0}, {0.0}, {0.0}, {YesNo} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { slider, "Turning speed", {&m_yaw}, {0.0}, {2.5}, {0.1f}, {NULL} }, - { slider, "Mouselook speed", {&m_pitch}, {0.0}, {2.5}, {0.1f}, {NULL} }, - { slider, "Forward/Backward speed",{&m_forward}, {0.0}, {2.5}, {0.1f}, {NULL} }, - { slider, "Strafing speed", {&m_side}, {0.0}, {2.5}, {0.1f}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Always Mouselook", {&freelook}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Invert Mouse", {&invertmouse}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Lookspring", {&lookspring}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Lookstrafe", {&lookstrafe}, {2.0}, {0.0}, {0.0}, {OnOff} }, -}; - -menu_t MouseMenu = -{ - "MOUSE OPTIONS", - 0, - countof(MouseItems), - 0, - MouseItems, -}; - -/*======================================= - * - * Joystick Menu - * - *=======================================*/ - -EXTERN_CVAR(Bool, use_joystick) -EXTERN_CVAR(Bool, joy_ps2raw) -EXTERN_CVAR(Bool, joy_dinput) -EXTERN_CVAR(Bool, joy_xinput) - -static TArray Joysticks; -static TArray JoystickItems; - -menu_t JoystickMenu = -{ - "CONTROLLER OPTIONS", -}; - -/*======================================= - * - * Joystick Config Menu - * - *=======================================*/ - -IJoystickConfig *SELECTED_JOYSTICK; - -static value_t JoyAxisMapNames[6] = -{ - { (float)JOYAXIS_None, "None" }, - { (float)JOYAXIS_Yaw, "Turning" }, - { (float)JOYAXIS_Pitch, "Looking Up/Down" }, - { (float)JOYAXIS_Forward, "Moving Forward" }, - { (float)JOYAXIS_Side, "Strafing" }, - { (float)JOYAXIS_Up, "Moving Up/Down" } -}; - -static value_t Inversion[2] = -{ - { 0.0, "Not Inverted" }, - { 1.0, "Inverted" } -}; - -static TArray JoystickConfigItems; - -menu_t JoystickConfigMenu = -{ - "CONFIGURE CONTROLLER", -}; - - -/*======================================= - * - * Controls Menu - * - *=======================================*/ - -menuitem_t ControlsItems[] = -{ - { redtext,"ENTER to change, BACKSPACE to clear", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Controls", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Fire", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+attack"} }, - { control, "Secondary Fire", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+altattack"} }, - { control, "Use / Open", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+use"} }, - { control, "Move forward", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+forward"} }, - { control, "Move backward", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+back"} }, - { control, "Strafe left", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+moveleft"} }, - { control, "Strafe right", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+moveright"} }, - { control, "Turn left", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+left"} }, - { control, "Turn right", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+right"} }, - { control, "Jump", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+jump"} }, - { control, "Crouch", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+crouch"} }, - { control, "Crouch Toggle", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"crouch"} }, - { control, "Fly / Swim up", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+moveup"} }, - { control, "Fly / Swim down", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+movedown"} }, - { control, "Stop flying", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"land"} }, - { control, "Mouse look", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+mlook"} }, - { control, "Keyboard look", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+klook"} }, - { control, "Look up", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+lookup"} }, - { control, "Look down", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+lookdown"} }, - { control, "Center view", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"centerview"} }, - { control, "Run", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+speed"} }, - { control, "Strafe", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+strafe"} }, - { control, "Show Scoreboard", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+showscores"} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Chat", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Say", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"messagemode"} }, - { control, "Team say", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"messagemode2"} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Weapons", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Next weapon", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"weapnext"} }, - { control, "Previous weapon", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"weapprev"} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Inventory", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Activate item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invuse"} }, - { control, "Activate all items", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invuseall"} }, - { control, "Next item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invnext"} }, - { control, "Previous item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invprev"} }, - { control, "Drop item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invdrop"} }, - { control, "Query item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invquery"} }, - { control, "Drop weapon", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"weapdrop"} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Other", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Toggle automap", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"togglemap"} }, - { control, "Chasecam", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"chase"} }, - { control, "Coop spy", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"spynext"} }, - { control, "Screenshot", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"screenshot"} }, - { control, "Open console", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"toggleconsole"} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Strife Popup Screens", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { control, "Mission objectives", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"showpop 1"} }, - { control, "Keys list", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"showpop 2"} }, - { control, "Weapons/ammo/stats", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"showpop 3"} }, -}; - -static TArray CustomControlsItems (0); - -menu_t ControlsMenu = -{ - "CUSTOMIZE CONTROLS", - 3, - countof(ControlsItems), - 0, - ControlsItems, - 2, -}; - - -/*======================================= - * - * Display Options Menu - * - *=======================================*/ -static void StartMessagesMenu (void); -static void StartScoreboardMenu (void); -static void InitCrosshairsList(); - -EXTERN_CVAR (Bool, st_scale) -EXTERN_CVAR (Bool, r_stretchsky) -EXTERN_CVAR (Int, r_columnmethod) -EXTERN_CVAR (Bool, r_drawfuzz) -EXTERN_CVAR (Int, cl_rockettrails) -EXTERN_CVAR (Int, cl_pufftype) -EXTERN_CVAR (Int, cl_bloodtype) -EXTERN_CVAR (Int, wipetype) -EXTERN_CVAR (Bool, vid_palettehack) -EXTERN_CVAR (Bool, vid_attachedsurfaces) -EXTERN_CVAR (Int, screenblocks) -EXTERN_CVAR (Int, r_fakecontrast) - -static TArray Crosshairs; - -static value_t ColumnMethods[] = { - { 0.0, "Original" }, - { 1.0, "Optimized" } -}; - -static value_t RocketTrailTypes[] = { - { 0.0, "Off" }, - { 1.0, "Particles" }, - { 2.0, "Sprites" }, - { 3.0, "Sprites & Particles" } -}; - -static value_t BloodTypes[] = { - { 0.0, "Sprites" }, - { 1.0, "Sprites & Particles" }, - { 2.0, "Particles" } -}; - -static value_t PuffTypes[] = { - { 0.0, "Sprites" }, - { 1.0, "Particles" } -}; - -static value_t Wipes[] = { - { 0.0, "None" }, - { 1.0, "Melt" }, - { 2.0, "Burn" }, - { 3.0, "Crossfade" } -}; - -static value_t Endoom[] = { - { 0.0, "Off" }, - { 1.0, "On" }, - { 2.0, "Only modified" } -}; - -static value_t Contrast[] = { - { 0.0, "Off" }, - { 1.0, "On" }, - { 2.0, "Smooth" } -}; - -static value_t DisplayTagsTypes[] = { - { 0.0, "None" }, - { 1.0, "Items" }, - { 2.0, "Weapons" }, - { 3.0, "Both" } -}; - -static menuitem_t VideoItems[] = { - { more, "Message Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)StartMessagesMenu} }, - { more, "Scoreboard Options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)StartScoreboardMenu} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { slider, "Screen size", {&screenblocks}, {3.0}, {12.0}, {1.0}, {NULL} }, - { slider, "Brightness", {&Gamma}, {1.0}, {3.0}, {0.1f}, {NULL} }, - { discrete, "Vertical Sync", {&vid_vsync}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discretes,"Crosshair", {&crosshair}, {8.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Column render mode", {&r_columnmethod}, {2.0}, {0.0}, {0.0}, {ColumnMethods} }, - { discrete, "Stretch short skies", {&r_stretchsky}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Stretch status bar", {&st_scale}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Alternative HUD", {&hud_althud}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Screen wipe style", {&wipetype}, {4.0}, {0.0}, {0.0}, {Wipes} }, -#ifdef _WIN32 - { discrete, "Show ENDOOM screen", {&showendoom}, {3.0}, {0.0}, {0.0}, {Endoom} }, - { discrete, "DirectDraw palette hack", {&vid_palettehack}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Use attached surfaces", {&vid_attachedsurfaces},{2.0}, {0.0}, {0.0}, {OnOff} }, -#endif - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Use fuzz effect", {&r_drawfuzz}, {2.0}, {0.0}, {0.0}, {YesNo} }, - { discrete, "Use fake contrast", {&r_fakecontrast}, {3.0}, {0.0}, {0.0}, {Contrast} }, - { discrete, "Rocket Trails", {&cl_rockettrails}, {4.0}, {0.0}, {0.0}, {RocketTrailTypes} }, - { discrete, "Blood Type", {&cl_bloodtype}, {3.0}, {0.0}, {0.0}, {BloodTypes} }, - { discrete, "Bullet Puff Type", {&cl_pufftype}, {2.0}, {0.0}, {0.0}, {PuffTypes} }, - { discrete, "Display nametags", {&displaynametags}, {4.0}, {0.0}, {0.0}, {DisplayTagsTypes} }, -}; - -#define CROSSHAIR_INDEX 6 - -menu_t VideoMenu = -{ - "DISPLAY OPTIONS", - 0, - countof(VideoItems), - 0, - VideoItems, -}; - -/*======================================= - * - * Automap Menu - * - *=======================================*/ -static void StartMapColorsMenu (void); -static void StartMapControlsMenu (void); - -EXTERN_CVAR (Int, am_rotate) -EXTERN_CVAR (Int, am_overlay) -EXTERN_CVAR (Bool, am_showitems) -EXTERN_CVAR (Bool, am_showmonsters) -EXTERN_CVAR (Bool, am_showsecrets) -EXTERN_CVAR (Bool, am_showtime) -EXTERN_CVAR (Int, am_map_secrets) -EXTERN_CVAR (Bool, am_showtotaltime) -EXTERN_CVAR (Bool, am_drawmapback) -EXTERN_CVAR (Bool, am_textured) - -static value_t MapColorTypes[] = { - { 0, "Custom" }, - { 1, "Traditional Doom" }, - { 2, "Traditional Strife" }, - { 3, "Traditional Raven" } -}; - -static value_t SecretTypes[] = { - { 0, "Never" }, - { 1, "Only when found" }, - { 2, "Always" }, -}; - -static value_t RotateTypes[] = { - { 0, "Off" }, - { 1, "On" }, - { 2, "On for overlay only" } -}; - -static value_t OverlayTypes[] = { - { 0, "Off" }, - { 1, "Overlay+Normal" }, - { 2, "Overlay Only" } -}; - -static menuitem_t AutomapItems[] = { - { discrete, "Map color set", {&am_colorset}, {4.0}, {0.0}, {0.0}, {MapColorTypes} }, - { more, "Set custom colors", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t*)StartMapColorsMenu} }, - { more, "Customize map controls", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t*)StartMapControlsMenu} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Rotate automap", {&am_rotate}, {3.0}, {0.0}, {0.0}, {RotateTypes} }, - { discrete, "Overlay automap", {&am_overlay}, {3.0}, {0.0}, {0.0}, {OverlayTypes} }, - { discrete, "Enable textured display", {&am_textured}, {3.0}, {0.0}, {0.0}, {OnOff} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Show item counts", {&am_showitems}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show monster counts", {&am_showmonsters}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show secret counts", {&am_showsecrets}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show time elapsed", {&am_showtime}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show total time elapsed", {&am_showtotaltime}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show secrets on map", {&am_map_secrets}, {3.0}, {0.0}, {0.0}, {SecretTypes} }, - { discrete, "Draw map background", {&am_drawmapback}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show keys (cheat)", {&am_showkeys}, {2.0}, {0.0}, {0.0}, {OnOff} }, -}; - -menu_t AutomapMenu = -{ - "AUTOMAP OPTIONS", - 0, - countof(AutomapItems), - 0, - AutomapItems, -}; - -menuitem_t MapControlsItems[] = -{ - { redtext,"ENTER to change, BACKSPACE to clear", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"Map Controls", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { mapcontrol, "Pan left", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_panleft"} }, - { mapcontrol, "Pan right", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_panright"} }, - { mapcontrol, "Pan up", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_panup"} }, - { mapcontrol, "Pan down", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_pandown"} }, - { mapcontrol, "Zoom in", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_zoomin"} }, - { mapcontrol, "Zoom out", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"+am_zoomout"} }, - { mapcontrol, "Toggle zoom", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_gobig"} }, - { mapcontrol, "Toggle follow", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_togglefollow"} }, - { mapcontrol, "Toggle grid", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_togglegrid"} }, - { mapcontrol, "Toggle texture", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_toggletexture"} }, - { mapcontrol, "Set mark", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_setmark"} }, - { mapcontrol, "Clear mark", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"am_clearmarks"} }, -}; - -menu_t MapControlsMenu = -{ - "CUSTOMIZE MAP CONTROLS", - 3, - countof(MapControlsItems), - 0, - MapControlsItems, - 2, -}; - - - -/*======================================= - * - * Map Colors Menu - * - *=======================================*/ -static void DefaultCustomColors(); - -EXTERN_CVAR (Color, am_backcolor) -EXTERN_CVAR (Color, am_yourcolor) -EXTERN_CVAR (Color, am_wallcolor) -EXTERN_CVAR (Color, am_secretwallcolor) -EXTERN_CVAR (Color, am_tswallcolor) -EXTERN_CVAR (Color, am_fdwallcolor) -EXTERN_CVAR (Color, am_cdwallcolor) -EXTERN_CVAR (Color, am_thingcolor) -EXTERN_CVAR (Color, am_gridcolor) -EXTERN_CVAR (Color, am_xhaircolor) -EXTERN_CVAR (Color, am_notseencolor) -EXTERN_CVAR (Color, am_lockedcolor) -EXTERN_CVAR (Color, am_ovyourcolor) -EXTERN_CVAR (Color, am_ovwallcolor) -EXTERN_CVAR (Color, am_ovthingcolor) -EXTERN_CVAR (Color, am_ovotherwallscolor) -EXTERN_CVAR (Color, am_ovunseencolor) -EXTERN_CVAR (Color, am_ovtelecolor) -EXTERN_CVAR (Color, am_intralevelcolor) -EXTERN_CVAR (Color, am_interlevelcolor) -EXTERN_CVAR (Color, am_secretsectorcolor) -EXTERN_CVAR (Color, am_ovsecretsectorcolor) -EXTERN_CVAR (Color, am_thingcolor_friend) -EXTERN_CVAR (Color, am_thingcolor_monster) -EXTERN_CVAR (Color, am_thingcolor_item) -EXTERN_CVAR (Color, am_thingcolor_citem) -EXTERN_CVAR (Color, am_ovthingcolor_friend) -EXTERN_CVAR (Color, am_ovthingcolor_monster) -EXTERN_CVAR (Color, am_ovthingcolor_item) -EXTERN_CVAR (Color, am_ovthingcolor_citem) - -static menuitem_t MapColorsItems[] = { - { rsafemore, "Restore default custom colors", {NULL}, {0}, {0}, {0}, {(value_t*)DefaultCustomColors} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { colorpicker, "Background", {&am_backcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "You", {&am_yourcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "1-sided walls", {&am_wallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "2-sided walls with different floors", {&am_fdwallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "2-sided walls with different ceilings", {&am_cdwallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Map grid", {&am_gridcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Center point", {&am_xhaircolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Not-yet-seen walls", {&am_notseencolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Locked doors", {&am_lockedcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Teleporter to the same map", {&am_intralevelcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Teleporter to a different map", {&am_interlevelcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Secret sector", {&am_secretsectorcolor}, {0}, {0}, {0}, {0} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { colorpicker, "Invisible 2-sided walls (for cheat)", {&am_tswallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Secret walls (for cheat)", {&am_secretwallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Actors (for cheat)", {&am_thingcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Monsters (for cheat)", {&am_thingcolor_monster}, {0}, {0}, {0}, {0} }, - { colorpicker, "Friends (for cheat)", {&am_thingcolor_friend}, {0}, {0}, {0}, {0} }, - { colorpicker, "Items (for cheat)", {&am_thingcolor_item}, {0}, {0}, {0}, {0} }, - { colorpicker, "Count Items (for cheat)", {&am_thingcolor_citem}, {0}, {0}, {0}, {0} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { colorpicker, "You (overlay)", {&am_ovyourcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "1-sided walls (overlay)", {&am_ovwallcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "2-sided walls (overlay)", {&am_ovotherwallscolor},{0}, {0}, {0}, {0} }, - { colorpicker, "Not-yet-seen walls (overlay)", {&am_ovunseencolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Teleporter (overlay)", {&am_ovtelecolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Secret sector (overlay)", {&am_ovsecretsectorcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Actors (overlay) (for cheat)", {&am_ovthingcolor}, {0}, {0}, {0}, {0} }, - { colorpicker, "Monsters (overlay) (for cheat)", {&am_ovthingcolor_monster}, {0}, {0}, {0}, {0} }, - { colorpicker, "Friends (overlay) (for cheat)", {&am_ovthingcolor_friend}, {0}, {0}, {0}, {0} }, - { colorpicker, "Items (overlay) (for cheat)", {&am_ovthingcolor_item}, {0}, {0}, {0}, {0} }, - { colorpicker, "Count Items (overlay) (for cheat)", {&am_ovthingcolor_citem}, {0}, {0}, {0}, {0} }, -}; - -menu_t MapColorsMenu = -{ - "CUSTOMIZE MAP COLORS", - 0, - countof(MapColorsItems), - 48, - MapColorsItems, -}; - -/*======================================= - * - * Color Picker Sub-menu - * - *=======================================*/ -static void StartColorPickerMenu (const char *colorname, FColorCVar *cvar); -static void ColorPickerReset (); -static int CurrColorIndex; -static int SelColorIndex; -static void UpdateSelColor (int index); - - -static menuitem_t ColorPickerItems[] = { - { redtext, NULL, {NULL}, {0}, {0}, {0}, {0} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { intslider, "Red", {NULL}, {0}, {255}, {15}, {0} }, - { intslider, "Green", {NULL}, {0}, {255}, {15}, {0} }, - { intslider, "Blue", {NULL}, {0}, {255}, {15}, {0} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { more, "Undo changes", {NULL}, {0}, {0}, {0}, {(value_t*)ColorPickerReset} }, - { redtext, " ", {NULL}, {0}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {0}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {1}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {2}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {3}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {4}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {5}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {6}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {7}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {8}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {9}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {10}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {11}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {12}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {13}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {14}, {0}, {0}, {0} }, - { palettegrid, " ", {NULL}, {15}, {0}, {0}, {0} }, -}; - -menu_t ColorPickerMenu = -{ - "SELECT COLOR", - 2, - countof(ColorPickerItems), - 0, - ColorPickerItems, -}; - -/*======================================= - * - * Messages Menu - * - *=======================================*/ -EXTERN_CVAR (Int, con_scaletext) -EXTERN_CVAR (Bool, con_centernotify) -EXTERN_CVAR (Int, msg0color) -EXTERN_CVAR (Int, msg1color) -EXTERN_CVAR (Int, msg2color) -EXTERN_CVAR (Int, msg3color) -EXTERN_CVAR (Int, msg4color) -EXTERN_CVAR (Int, msgmidcolor) -EXTERN_CVAR (Int, msglevel) - -static value_t ScaleValues[] = -{ - { 0.0, "Off" }, - { 1.0, "On" }, - { 2.0, "Double" } -}; - -static value_t TextColors[] = -{ - { 0.0, "brick" }, - { 1.0, "tan" }, - { 2.0, "gray" }, - { 3.0, "green" }, - { 4.0, "brown" }, - { 5.0, "gold" }, - { 6.0, "red" }, - { 7.0, "blue" }, - { 8.0, "orange" }, - { 9.0, "white" }, - { 10.0, "yellow" }, - { 11.0, "default" }, - { 12.0, "black" }, - { 13.0, "light blue" }, - { 14.0, "cream" }, - { 15.0, "olive" }, - { 16.0, "dark green" }, - { 17.0, "dark red" }, - { 18.0, "dark brown" }, - { 19.0, "purple" }, - { 20.0, "dark gray" }, -}; - -static value_t MessageLevels[] = { - { 0.0, "Item Pickup" }, - { 1.0, "Obituaries" }, - { 2.0, "Critical Messages" } -}; - -static menuitem_t MessagesItems[] = { - { discrete, "Show messages", {&show_messages}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Show obituaries", {&show_obituaries}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { discrete, "Scale text in high res", {&con_scaletext}, {3.0}, {0.0}, {0.0}, {ScaleValues} }, - { discrete, "Minimum message level", {&msglevel}, {3.0}, {0.0}, {0.0}, {MessageLevels} }, - { discrete, "Center messages", {&con_centernotify}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext, "Message Colors", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { cdiscrete, "Item Pickup", {&msg0color}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Obituaries", {&msg1color}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Critical Messages", {&msg2color}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Chat Messages", {&msg3color}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Team Messages", {&msg4color}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Centered Messages", {&msgmidcolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Screenshot messages", {&screenshot_quiet}, {2.0}, {0.0}, {0.0}, {OffOn} }, - { discrete, "Detailed save messages",{&longsavemessages}, {2.0}, {0.0}, {0.0}, {OnOff} }, -}; - -menu_t MessagesMenu = -{ - "MESSAGES", - 0, - countof(MessagesItems), - 0, - MessagesItems, -}; - - -/*======================================= - * - * Scoreboard Menu - * - *=======================================*/ - -EXTERN_CVAR (Bool, sb_cooperative_enable) -EXTERN_CVAR (Int, sb_cooperative_headingcolor) -EXTERN_CVAR (Int, sb_cooperative_yourplayercolor) -EXTERN_CVAR (Int, sb_cooperative_otherplayercolor) - -EXTERN_CVAR (Bool, sb_deathmatch_enable) -EXTERN_CVAR (Int, sb_deathmatch_headingcolor) -EXTERN_CVAR (Int, sb_deathmatch_yourplayercolor) -EXTERN_CVAR (Int, sb_deathmatch_otherplayercolor) - -EXTERN_CVAR (Bool, sb_teamdeathmatch_enable) -EXTERN_CVAR (Int, sb_teamdeathmatch_headingcolor) - -static menuitem_t ScoreboardItems[] = { - { whitetext, "Cooperative Options", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Enable Scoreboard", {&sb_cooperative_enable}, {21.0}, {0.0}, {0.0}, {YesNo} }, - { cdiscrete, "Header Color", {&sb_cooperative_headingcolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Your Player Color", {&sb_cooperative_yourplayercolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Other Players' Color", {&sb_cooperative_otherplayercolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext, "Deathmatch Options", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Enable Scoreboard", {&sb_deathmatch_enable}, {21.0}, {0.0}, {0.0}, {YesNo} }, - { cdiscrete, "Header Color", {&sb_deathmatch_headingcolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Your Player Color", {&sb_deathmatch_yourplayercolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { cdiscrete, "Other Players' Color", {&sb_deathmatch_otherplayercolor}, {21.0}, {0.0}, {0.0}, {TextColors} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext, "Team Deathmatch Options", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Enable Scoreboard", {&sb_teamdeathmatch_enable}, {21.0}, {0.0}, {0.0}, {YesNo} }, - { cdiscrete, "Header Color", {&sb_teamdeathmatch_headingcolor}, {21.0}, {0.0}, {0.0}, {TextColors} } -}; - -menu_t ScoreboardMenu = -{ - "SCOREBOARD OPTIONS", - 2, - countof(ScoreboardItems), - 0, - ScoreboardItems, -}; - - -/*======================================= - * - * Video Modes Menu - * - *=======================================*/ - -extern bool setmodeneeded; -extern int NewWidth, NewHeight, NewBits; -extern int DisplayBits; - -int testingmode; // Holds time to revert to old mode -int OldWidth, OldHeight, OldBits; - -void M_FreeModesList (); -static void BuildModesList (int hiwidth, int hiheight, int hi_id); -static bool GetSelectedSize (int line, int *width, int *height); -static void SetModesMenu (int w, int h, int bits); - -EXTERN_CVAR (Int, vid_defwidth) -EXTERN_CVAR (Int, vid_defheight) -EXTERN_CVAR (Int, vid_defbits) - -static FIntCVar DummyDepthCvar (NULL, 0, 0); - -EXTERN_CVAR (Bool, fullscreen) - -static value_t Depths[22]; - -EXTERN_CVAR (Bool, vid_tft) // Defined below -CUSTOM_CVAR (Int, menu_screenratios, 0, CVAR_ARCHIVE) -{ - if (self < 0 || self > 4) - { - self = 3; - } - else if (self == 4 && !vid_tft) - { - self = 3; - } - else - { - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); - } -} - -static value_t ForceRatios[] = -{ - { 0.0, "Off" }, - { 3.0, "4:3" }, - { 1.0, "16:9" }, - { 2.0, "16:10" }, - { 4.0, "5:4" } -}; -static value_t Ratios[] = -{ - { 0.0, "4:3" }, - { 1.0, "16:9" }, - { 2.0, "16:10" }, - { 3.0, "All" } -}; -static value_t RatiosTFT[] = -{ - { 0.0, "4:3" }, - { 4.0, "5:4" }, - { 1.0, "16:9" }, - { 2.0, "16:10" }, - { 3.0, "All" } -}; - -static char VMEnterText[] = "Press ENTER to set mode"; -static char VMTestText[] = "T to test mode for 5 seconds"; - -static menuitem_t ModesItems[] = { -// { discrete, "Screen mode", {&DummyDepthCvar}, {0.0}, {0.0}, {0.0}, {Depths} }, - { discrete, "Force aspect ratio", {&vid_aspect}, {5.0}, {0.0}, {0.0}, {ForceRatios} }, - { discrete, "Aspect ratio", {&menu_screenratios}, {4.0}, {0.0}, {0.0}, {Ratios} }, - { discrete, "Fullscreen", {&fullscreen}, {2.0}, {0.0}, {0.0}, {YesNo} }, - { discrete, "Enable 5:4 aspect ratio",{&vid_tft}, {2.0}, {0.0}, {0.0}, {YesNo} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { screenres,NULL, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, -// { whitetext,"Note: Only 8 bpp modes are supported",{NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, VMEnterText, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, VMTestText, {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, -}; - -#define VM_ASPECTITEM 1 -#define VM_RESSTART 5 -#define VM_ENTERLINE 15 -#define VM_TESTLINE 17 - -menu_t ModesMenu = -{ - "VIDEO MODE", - 2, - countof(ModesItems), - 0, - ModesItems, -}; - -CUSTOM_CVAR (Bool, vid_tft, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self) - { - ModesItems[VM_ASPECTITEM].b.numvalues = 5.f; - ModesItems[VM_ASPECTITEM].e.values = RatiosTFT; - } - else - { - ModesItems[VM_ASPECTITEM].b.numvalues = 4.f; - ModesItems[VM_ASPECTITEM].e.values = Ratios; - if (menu_screenratios == 4) - { - menu_screenratios = 0; - } - } - setsizeneeded = true; - if (StatusBar != NULL) - { - StatusBar->ScreenSizeChanged(); - } -} - -/*======================================= - * - * Gameplay Options (dmflags) Menu - * - *=======================================*/ -value_t SmartAim[4] = { - { 0.0, "Off" }, - { 1.0, "On" }, - { 2.0, "Never friends" }, - { 3.0, "Only monsters" } -}; - -value_t FallingDM[4] = { - { 0, "Off" }, - { DF_FORCE_FALLINGZD, "Old" }, - { DF_FORCE_FALLINGHX, "Hexen" }, - { DF_FORCE_FALLINGZD|DF_FORCE_FALLINGHX, "Strife" } -}; - -value_t DF_Jump[3] = { - { 0, "Default" }, - { DF_NO_JUMP, "Off" }, - { DF_YES_JUMP, "On" } -}; - -value_t DF_Crouch[3] = { - { 0, "Default" }, - { DF_NO_CROUCH, "Off" }, - { DF_YES_CROUCH, "On" } -}; - - -static menuitem_t DMFlagsItems[] = { - { discrete, "Teamplay", {&teamplay}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { slider, "Team damage scalar", {&teamdamage}, {0.0}, {1.0}, {0.05f},{NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Smart Autoaim", {&sv_smartaim}, {4.0}, {0.0}, {0.0}, {SmartAim} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { bitmask, "Falling damage", {&dmflags}, {4.0}, {DF_FORCE_FALLINGZD|DF_FORCE_FALLINGHX}, {0}, {FallingDM} }, - { bitflag, "Drop weapon", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_WEAPONDROP} }, - { bitflag, "Double ammo", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_DOUBLEAMMO} }, - { bitflag, "Infinite ammo", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_INFINITE_AMMO} }, - { bitflag, "Infinite inventory", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_INFINITE_INVENTORY} }, - { bitflag, "No monsters", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_NO_MONSTERS} }, - { bitflag, "No monsters to exit", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_KILL_MONSTERS} }, - { bitflag, "Monsters respawn", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_MONSTERS_RESPAWN} }, - { bitflag, "No respawn", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_NO_RESPAWN} }, - { bitflag, "Items respawn", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_ITEMS_RESPAWN} }, - { bitflag, "Big powerups respawn", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_RESPAWN_SUPER} }, - { bitflag, "Fast monsters", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_FAST_MONSTERS} }, - { bitflag, "Degeneration", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_DEGENERATION} }, - { bitflag, "Allow Autoaim", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_NOAUTOAIM} }, - { bitflag, "Allow Suicide", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_NOSUICIDE} }, - { bitmask, "Allow jump", {&dmflags}, {3.0}, {DF_NO_JUMP|DF_YES_JUMP}, {0}, {DF_Jump} }, - { bitmask, "Allow crouch", {&dmflags}, {3.0}, {DF_NO_CROUCH|DF_YES_CROUCH}, {0}, {DF_Crouch} }, - { bitflag, "Allow freelook", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_FREELOOK} }, - { bitflag, "Allow FOV", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_FOV} }, - { bitflag, "Allow BFG aiming", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_NO_FREEAIMBFG} }, - { bitflag, "Allow automap", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_NO_AUTOMAP} }, - { bitflag, "Automap allies", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_NO_AUTOMAP_ALLIES} }, - { bitflag, "Allow spying", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_DISALLOW_SPYING} }, - { bitflag, "Chasecam cheat", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_CHASECAM} }, - { bitflag, "Check ammo for weapon switch", {&dmflags2}, {1}, {0}, {0}, {(value_t *)DF2_DONTCHECKAMMO} }, - { bitflag, "Killing Romero kills all his spawns", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_KILLBOSSMONST} }, - - { redtext, " ", {NULL}, {0}, {0}, {0}, {NULL} }, - { whitetext,"Deathmatch Settings", {NULL}, {0}, {0}, {0}, {NULL} }, - { bitflag, "Weapons stay", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_WEAPONS_STAY} }, - { bitflag, "Allow powerups", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_ITEMS} }, - { bitflag, "Allow health", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_HEALTH} }, - { bitflag, "Allow armor", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_ARMOR} }, - { bitflag, "Spawn farthest", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_SPAWN_FARTHEST} }, - { bitflag, "Same map", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_SAME_LEVEL} }, - { bitflag, "Force respawn", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_FORCE_RESPAWN} }, - { bitflag, "Allow exit", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_EXIT} }, - { bitflag, "Barrels respawn", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_BARRELS_RESPAWN} }, - { bitflag, "Respawn protection", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_RESPAWN_INVUL} }, - { bitflag, "Lose frag if fragged", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_LOSEFRAG} }, - { bitflag, "Keep frags gained", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_YES_KEEPFRAGS} }, - { bitflag, "No team switching", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_NO_TEAM_SWITCH} }, - - { redtext, " ", {NULL}, {0}, {0}, {0}, {NULL} }, - { whitetext,"Cooperative Settings", {NULL}, {0}, {0}, {0}, {NULL} }, - { bitflag, "Spawn multi. weapons", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_NO_COOP_WEAPON_SPAWN} }, - { bitflag, "Lose entire inventory",{&dmflags}, {0}, {0}, {0}, {(value_t *)DF_COOP_LOSE_INVENTORY} }, - { bitflag, "Keep keys", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_COOP_LOSE_KEYS} }, - { bitflag, "Keep weapons", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_COOP_LOSE_WEAPONS} }, - { bitflag, "Keep armor", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_COOP_LOSE_ARMOR} }, - { bitflag, "Keep powerups", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_COOP_LOSE_POWERUPS} }, - { bitflag, "Keep ammo", {&dmflags}, {1}, {0}, {0}, {(value_t *)DF_COOP_LOSE_AMMO} }, - { bitflag, "Lose half ammo", {&dmflags}, {0}, {0}, {0}, {(value_t *)DF_COOP_HALVE_AMMO} }, - { bitflag, "Spawn where died", {&dmflags2}, {0}, {0}, {0}, {(value_t *)DF2_SAME_SPAWN_SPOT} }, -}; - -static menu_t DMFlagsMenu = -{ - "GAMEPLAY OPTIONS", - 0, - countof(DMFlagsItems), - 222, - DMFlagsItems, -}; - -/*======================================= - * - * Compatibility Options Menu - * - *=======================================*/ - -static menuitem_t CompatibilityItems[] = { - { discrete, "Compatibility mode", {&compatmode}, {7.0}, {1.0}, {0.0}, {CompatModes} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { bitflag, "Find shortest textures like Doom", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SHORTTEX} }, - { bitflag, "Use buggier stair building", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_STAIRINDEX} }, - { bitflag, "Find neighboring light like Doom", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_LIGHT} }, - { bitflag, "Limit Pain Elementals' Lost Souls", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_LIMITPAIN} }, - { bitflag, "Don't let others hear your pickups", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SILENTPICKUP} }, - { bitflag, "Actors are infinitely tall", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NO_PASSMOBJ} }, - { bitflag, "Enable wall running", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_WALLRUN} }, - { bitflag, "Spawn item drops on the floor", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NOTOSSDROPS} }, - { bitflag, "All special lines can block ", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_USEBLOCKING} }, - { bitflag, "Disable BOOM door light effect", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NODOORLIGHT} }, - { bitflag, "Raven scrollers use original speed", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_RAVENSCROLL} }, - { bitflag, "Use original sound target handling", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SOUNDTARGET} }, - { bitflag, "DEH health settings like Doom2.exe", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_DEHHEALTH} }, - { bitflag, "Self ref. sectors don't block shots", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_TRACE} }, - { bitflag, "Monsters get stuck over dropoffs", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_DROPOFF} }, - { bitflag, "Monsters cannot cross dropoffs", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_CROSSDROPOFF} }, - { bitflag, "Monsters see invisible players", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_INVISIBILITY} }, - { bitflag, "Boom scrollers are additive", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_BOOMSCROLL} }, - { bitflag, "Inst. moving floors are not silent", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SILENT_INSTANT_FLOORS} }, - { bitflag, "Sector sounds use center as source", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SECTORSOUNDS} }, - { bitflag, "Use Doom heights for missile clipping", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MISSILECLIP} }, - { bitflag, "Allow any bossdeath for level special", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_ANYBOSSDEATH} }, - { bitflag, "No Minotaur floor flames in water", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MINOTAUR} }, - { bitflag, "Original A_Mushroom speed in DEH mods", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MUSHROOM} }, - { bitflag, "Monster movement is affected by effects", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MBFMONSTERMOVE} }, - { bitflag, "Crushed monsters can be resurrected", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_CORPSEGIBS} }, - { bitflag, "Friendly monsters aren't blocked", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_NOBLOCKFRIENDS} }, - { bitflag, "Invert sprite sorting", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SPRITESORT} }, - { bitflag, "Use Doom code for hitscan checks", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_HITSCAN} }, - { bitflag, "Cripple sound for silent BFG trick", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_MAGICSILENCE} }, - { bitflag, "Draw polyobjects like Hexen", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_POLYOBJ} }, - - { discrete, "Interpolate monster movement", {&nomonsterinterpolation}, {2.0}, {0.0}, {0.0}, {NoYes} }, -}; - -static menu_t CompatibilityMenu = -{ - "COMPATIBILITY OPTIONS", - 0, - countof(CompatibilityItems), - 0, - CompatibilityItems, -}; - -/*======================================= - * - * Sound Options Menu - * - *=======================================*/ - -#ifdef _WIN32 -EXTERN_CVAR (Float, snd_movievolume) -#endif -EXTERN_CVAR (Bool, snd_flipstereo) -EXTERN_CVAR (Bool, snd_pitched) -EXTERN_CVAR (String, snd_output_format) -EXTERN_CVAR (String, snd_speakermode) -EXTERN_CVAR (String, snd_resampler) -EXTERN_CVAR (String, snd_output) -EXTERN_CVAR (Int, snd_buffersize) -EXTERN_CVAR (Int, snd_buffercount) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_hrtf) -EXTERN_CVAR (Bool, snd_waterreverb) -EXTERN_CVAR (Float, snd_waterlp) -EXTERN_CVAR (Int, snd_mididevice) - -static void MakeSoundChanges (); -static void AdvSoundOptions (); -static void ModReplayerOptions (); - -static value_t SampleRates[] = -{ - { 0.f, "Default" }, - { 4000.f, "4000 Hz" }, - { 8000.f, "8000 Hz" }, - { 11025.f, "11025 Hz" }, - { 22050.f, "22050 Hz" }, - { 32000.f, "32000 Hz" }, - { 44100.f, "44100 Hz" }, - { 48000.f, "48000 Hz" } -}; - -static value_t BufferSizes[] = -{ - { 0.f, "Default" }, - { 64.f, "64 samples" }, - { 128.f, "128 samples" }, - { 256.f, "256 samples" }, - { 512.f, "512 samples" }, - { 1024.f, "1024 samples" }, - { 2048.f, "2048 samples" }, - { 4096.f, "4096 samples" } -}; - -static value_t BufferCounts[] = -{ - { 0.f, "Default" }, - { 2.f, "2" }, - { 3.f, "3" }, - { 4.f, "4" }, - { 5.f, "5" }, - { 6.f, "6" }, - { 7.f, "7" }, - { 8.f, "8" }, - { 9.f, "9" }, - { 10.f, "10" }, - { 11.f, "11" }, - { 12.f, "12" } -}; - -static valueenum_t Outputs[] = -{ - { "Default", "Default" }, -#if defined(_WIN32) - { "DirectSound", "DirectSound" }, - { "WASAPI", "Vista WASAPI" }, - { "ASIO", "ASIO" }, - { "WaveOut", "WaveOut" }, - { "OpenAL", "OpenAL (very beta)" }, -#elif defined(unix) - { "OSS", "OSS" }, - { "ALSA", "ALSA" }, - { "SDL", "SDL" }, - { "ESD", "ESD" }, -#elif defined(__APPLE__) - { "Sound Manager", "Sound Manager" }, - { "Core Audio", "Core Audio" }, -#endif - { "No sound", "No sound" } -}; - -static valueenum_t OutputFormats[] = -{ - { "PCM-8", "8-bit" }, - { "PCM-16", "16-bit" }, - { "PCM-24", "24-bit" }, - { "PCM-32", "32-bit" }, - { "PCM-Float", "32-bit float" } -}; - -static valueenum_t SpeakerModes[] = -{ - { "Auto", "Auto" }, - { "Mono", "Mono" }, - { "Stereo", "Stereo" }, - { "Prologic", "Dolby Prologic Decoder" }, - { "Quad", "Quad" }, - { "Surround", "5 speakers" }, - { "5.1", "5.1 speakers" }, - { "7.1", "7.1 speakers" } -}; - -static valueenum_t Resamplers[] = -{ - { "NoInterp", "No interpolation" }, - { "Linear", "Linear" }, - { "Cubic", "Cubic" }, - { "Spline", "Spline" } -}; - -static menuitem_t SoundItems[] = -{ - { slider, "Sounds volume", {&snd_sfxvolume}, {0.0}, {1.0}, {0.05f}, {NULL} }, - { slider, "Menu volume", {&snd_menuvolume}, {0.0}, {1.0}, {0.05f}, {NULL} }, - { slider, "Music volume", {&snd_musicvolume}, {0.0}, {1.0}, {0.05f}, {NULL} }, - { discrete, "MIDI device", {&snd_mididevice}, {0.0}, {0.0}, {0.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Underwater reverb", {&snd_waterreverb}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { slider, "Underwater cutoff", {&snd_waterlp}, {0.0}, {2000.0},{50.0}, {NULL} }, - { discrete, "Randomize pitches", {&snd_pitched}, {2.0}, {0.0}, {0.0}, {OnOff} }, - { slider, "Sound channels", {&snd_channels}, {8.0}, {256.0}, {8.0}, {NULL} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { more, "Restart sound", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)MakeSoundChanges} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { ediscrete,"Output system", {&snd_output}, {countof(Outputs)}, {0.0}, {0.0}, {(value_t *)Outputs} }, - { ediscrete,"Output format", {&snd_output_format}, {5.0}, {0.0}, {0.0}, {(value_t *)OutputFormats} }, - { ediscrete,"Speaker mode", {&snd_speakermode}, {8.0}, {0.0}, {0.0}, {(value_t *)SpeakerModes} }, - { ediscrete,"Resampler", {&snd_resampler}, {4.0}, {0.0}, {0.0}, {(value_t *)Resamplers} }, - { discrete, "HRTF filter", {&snd_hrtf}, {2.0}, {0.0}, {0.0}, {(value_t *)OnOff} }, - - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { more, "Advanced options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)AdvSoundOptions} }, - { more, "Module replayer options", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)ModReplayerOptions} }, -}; - -static menu_t SoundMenu = -{ - "SOUND OPTIONS", - 0, - countof(SoundItems), - 0, - SoundItems, -}; - -#define MIDI_DEVICE_ITEM 3 - -/*======================================= - * - * Advanced Sound Options Menu - * - *=======================================*/ - -EXTERN_CVAR (Bool, opl_onechip) - -static menuitem_t AdvSoundItems[] = -{ - { discrete, "Sample rate", {&snd_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, - { discrete, "Buffer size", {&snd_buffersize}, {8.0}, {0.0}, {0.0}, {BufferSizes} }, - { discrete, "Buffer count", {&snd_buffercount}, {12.0}, {0.0}, {0.0}, {BufferCounts} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { whitetext,"OPL Synthesis", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Only emulate one OPL chip", {&opl_onechip}, {2.0}, {0.0}, {0.0}, {OnOff} }, -}; - -static menu_t AdvSoundMenu = -{ - "ADVANCED SOUND OPTIONS", - 0, - countof(AdvSoundItems), - 0, - AdvSoundItems, -}; - -/*======================================= - * - * Module Replayer Options Menu - * - *=======================================*/ - -EXTERN_CVAR(Bool, mod_dumb) -EXTERN_CVAR(Int, mod_samplerate) -EXTERN_CVAR(Int, mod_volramp) -EXTERN_CVAR(Int, mod_interp) -EXTERN_CVAR(Bool, mod_autochip) -EXTERN_CVAR(Int, mod_autochip_size_force) -EXTERN_CVAR(Int, mod_autochip_size_scan) -EXTERN_CVAR(Int, mod_autochip_scan_threshold) - -static value_t ModReplayers[] = -{ - { 0.0, "FMOD" }, - { 1.0, "foo_dumb" } -}; - -static value_t ModInterpolations[] = -{ - { 0.0, "None" }, - { 1.0, "Linear" }, - { 2.0, "Cubic" } -}; - -static value_t ModVolumeRamps[] = -{ - { 0.0, "None" }, - { 1.0, "Logarithmic" }, - { 2.0, "Linear" }, - { 3.0, "XM=lin, else none" }, - { 4.0, "XM=lin, else log" } -}; - -static menuitem_t ModReplayerItems[] = -{ - { discrete, "Replayer engine", {&mod_dumb}, {2.0}, {0.0}, {0.0}, {ModReplayers} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Sample rate", {&mod_samplerate}, {8.0}, {0.0}, {0.0}, {SampleRates} }, - { discrete, "Interpolation", {&mod_interp}, {3.0}, {0.0}, {0.0}, {ModInterpolations} }, - { discrete, "Volume ramping", {&mod_volramp}, {5.0}, {0.0}, {0.0}, {ModVolumeRamps} }, - { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, - { discrete, "Chip-o-matic", {&mod_autochip}, {2.0}, {0.0}, {0.0}, {OnOff} }, - // TODO if the menu system is ever rewritten: Provide a decent - // mechanism to edit the chip-o-matic settings like you can with - // the foo_dumb preferences in foobar2000. -}; - -static menu_t ModReplayerMenu = -{ - "MODULE REPLAYER OPTIONS", - 0, - countof(ModReplayerItems), - 0, - ModReplayerItems, -}; - - -//=========================================================================== -static void ActivateConfirm (const char *text, void (*func)()) -{ - ConfirmItems[0].label = text; - ConfirmItems[0].e.mfunc = func; - ConfirmMenu.lastOn = 3; - M_SwitchMenu (&ConfirmMenu); -} - -static void ConfirmIsAGo () -{ - M_PopMenuStack (); - ConfirmItems[0].e.mfunc (); -} - -// -// Set some stuff up for the video modes menu -// -static BYTE BitTranslate[32]; - -void M_OptInit (void) -{ - if (gameinfo.gametype & GAME_DoomChex) - { - LabelColor = CR_UNTRANSLATED; - ValueColor = CR_GRAY; - MoreColor = CR_GRAY; - } - else if (gameinfo.gametype == GAME_Heretic) - { - LabelColor = CR_GREEN; - ValueColor = CR_UNTRANSLATED; - MoreColor = CR_UNTRANSLATED; - } - else // Hexen - { - LabelColor = CR_RED; - ValueColor = CR_UNTRANSLATED; - MoreColor = CR_UNTRANSLATED; - } -} - -void M_InitVideoModesMenu () -{ - int dummy1, dummy2; - size_t currval = 0; - - M_RefreshModesList(); - - for (unsigned int i = 1; i <= 32 && currval < countof(Depths); i++) - { - Video->StartModeIterator (i, screen->IsFullscreen()); - if (Video->NextMode (&dummy1, &dummy2, NULL)) - { - /* - Depths[currval].value = currval; - mysnprintf (name, countof(name), "%d bit", i); - Depths[currval].name = copystring (name); - */ - BitTranslate[currval++] = i; - } - } - - //ModesItems[VM_DEPTHITEM].b.min = (float)currval; - - switch (Video->GetDisplayType ()) - { - case DISPLAY_FullscreenOnly: - ModesItems[2].type = nochoice; - ModesItems[2].b.min = 1.f; - break; - case DISPLAY_WindowOnly: - ModesItems[2].type = nochoice; - ModesItems[2].b.min = 0.f; - break; - default: - break; - } -} - - -// -// Toggle messages on/off -// -void M_ChangeMessages () -{ - if (show_messages) - { - Printf (128, "%s\n", GStrings("MSGOFF")); - show_messages = false; - } - else - { - Printf (128, "%s\n", GStrings("MSGON")); - show_messages = true; - } -} - -CCMD (togglemessages) -{ - M_ChangeMessages (); -} - -void M_SizeDisplay (int diff) -{ - // changing screenblocks automatically resizes the display - screenblocks = screenblocks + diff; -} - -CCMD (sizedown) -{ - M_SizeDisplay (-1); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); -} - -CCMD (sizeup) -{ - M_SizeDisplay (1); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); -} - -// 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) -{ - int len = (int)strlen(str); - - screen->DrawText (ConFont, color, x, y, str, - DTA_CellX, 8 * CleanXfac_1, - DTA_CellY, 8 * CleanYfac_1, - TAG_DONE); -} - -void M_BuildKeyList (menuitem_t *item, int numitems) -{ - int i; - - for (i = 0; i < numitems; i++, item++) - { - if (item->type == control) - Bindings.GetKeysForCommand (item->e.command, &item->b.key1, &item->c.key2); - else if (item->type == mapcontrol) - AutomapBindings.GetKeysForCommand (item->e.command, &item->b.key1, &item->c.key2); - } -} - -static void CalcIndent (menu_t *menu) -{ - int i, widest = 0, thiswidth; - menuitem_t *item; - - for (i = 0; i < menu->numitems; i++) - { - item = menu->items + i; - if (item->type != whitetext && item->type != redtext && item->type != screenres && - item->type != joymore && (item->type != discrete || item->c.discretecenter != 1)) - { - thiswidth = SmallFont->StringWidth (item->label); - if (thiswidth > widest) - widest = thiswidth; - } - } - menu->indent = widest + 4; -} - -void M_SwitchMenu (menu_t *menu) -{ - MenuStack[MenuStackDepth].menu.newmenu = menu; - MenuStack[MenuStackDepth].isNewStyle = true; - MenuStack[MenuStackDepth].drawSkull = false; - MenuStackDepth++; - - CanScrollUp = false; - CanScrollDown = false; - CurrentMenu = menu; - CurrentItem = menu->lastOn; - - if (!menu->indent) - { - CalcIndent (menu); - } - - flagsvar = 0; -} - -bool M_StartOptionsMenu (void) -{ - M_SwitchMenu (&OptionMenu); - return true; -} - -// Draw a slider. Set fracdigits negative to not display the current value numerically. -static void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits) -{ - double range; - - range = max - min; - double ccur = clamp(cur, min, max) - min; - - M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12"); - M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), y, "\x13"); - - if (fracdigits >= 0) - { - char textbuf[16]; - mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); - screen->DrawText(SmallFont, CR_DARKGRAY, x + (12*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); - } -} - -int M_FindCurVal (float cur, value_t *values, int numvals) -{ - int v; - - for (v = 0; v < numvals; v++) - if (values[v].value == cur) - break; - - return v; -} - -int M_FindCurVal (float cur, valuestring_t *values, int numvals) -{ - int v; - - for (v = 0; v < numvals; v++) - if (values[v].value == cur) - break; - - return v; -} - -const char *M_FindCurVal(const char *cur, valueenum_t *values, int numvals) -{ - for (int v = 0; v < numvals; ++v) - { - if (stricmp(values[v].value, cur) == 0) - { - return values[v].name; - } - } - return cur; -} - -const char *M_FindPrevVal(const char *cur, valueenum_t *values, int numvals) -{ - for (int v = 0; v < numvals; ++v) - { - if (stricmp(values[v].value, cur) == 0) - { - return values[v == 0 ? numvals - 1 : v - 1].value; - } - } - return values[0].value; -} - -const char *M_FindNextVal(const char *cur, valueenum_t *values, int numvals) -{ - for (int v = 0; v < numvals; ++v) - { - if (stricmp(values[v].value, cur) == 0) - { - return values[v == numvals - 1 ? 0 : v + 1].value; - } - } - return values[0].value; -} - -void M_OptDrawer () -{ - EColorRange color; - int y, width, i, x, ytop, fontheight; - menuitem_t *item; - UCVarValue value; - DWORD overlay; - int labelofs; - int indent; - int cursorspace; - - if (!CurrentMenu->DontDim) - { - screen->Dim (); - } - - if (CurrentMenu->PreDraw != NULL) - { - if (CurrentMenu->PreDraw ()) return; - } - - if (CurrentMenu->y != 0) - { - y = CurrentMenu->y; - } - else - { - if (BigFont && CurrentMenu->texttitle) - { - screen->DrawText (BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - (screen->GetWidth() - BigFont->StringWidth(CurrentMenu->texttitle) * CleanXfac_1) / 2, 10*CleanYfac_1, - CurrentMenu->texttitle, DTA_CleanNoMove_1, true, TAG_DONE); - y = 15 + BigFont->GetHeight(); - } - else - { - y = 15; - } - } - if (gameinfo.gametype & GAME_Raven) - { - labelofs = 2 * CleanXfac_1; - y -= 2; - fontheight = 9; - } - else - { - labelofs = 0; - fontheight = 8; - } - cursorspace = 14 * CleanXfac_1; - y *= CleanYfac_1; - fontheight *= CleanYfac_1; - ytop = y + CurrentMenu->scrolltop * 8 * CleanYfac_1; - int lastrow = screen->GetHeight() - SmallFont->GetHeight() * CleanYfac_1; - - for (i = 0; i < CurrentMenu->numitems && y <= lastrow; i++, y += fontheight) - { - if (i == CurrentMenu->scrolltop) - { - i += CurrentMenu->scrollpos; - } - - item = CurrentMenu->items + i; - overlay = 0; - if (item->type == discrete && item->c.discretecenter == 1) - { - indent = screen->GetWidth() / 2; - } - else if (item->type == joymore) - { - indent = 4 * CleanXfac_1; - } - else - { - indent = CurrentMenu->indent; - 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; - } - } - - if (item->type != screenres) - { - FString somestring; - const char *label; - if (item->type != joymore) - { - label = item->label; - } - else - { - if (Joysticks.Size() == 0) - { - label = "No devices connected"; - } - else - { - somestring = Joysticks[item->a.joyselection]->GetName(); - label = somestring; - } - } - width = SmallFont->StringWidth(label) * CleanXfac_1; - switch (item->type) - { - case more: - case safemore: - x = indent - width; - color = MoreColor; - break; - - case joymore: - x = 20 * CleanXfac_1; - color = MoreColor; - break; - - case numberedmore: - case rsafemore: - case rightmore: - x = indent + cursorspace; - color = item->type != rightmore ? CR_GREEN : MoreColor; - break; - - case redtext: - x = screen->GetWidth() / 2 - width / 2; - color = LabelColor; - break; - - case whitetext: - x = screen->GetWidth() / 2 - width / 2; - color = CR_GOLD;//ValueColor; - break; - - case listelement: - x = indent + cursorspace; - color = LabelColor; - break; - - case colorpicker: - x = indent + cursorspace; - color = MoreColor; - break; - - case discrete: - if (item->d.graycheck != NULL && !(**item->d.graycheck)) - { - overlay = MAKEARGB(128,0,0,0); - } - // Intentional fall-through - - default: - x = indent - width; - color = ((item->type == control || item->type == mapcontrol) && menuactive == MENU_WaitKey && i == CurrentItem) - ? CR_YELLOW : LabelColor; - break; - } - screen->DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); - - switch (item->type) - { - case numberedmore: - if (item->b.position != 0) - { - char tbuf[16]; - - mysnprintf (tbuf, countof(tbuf), "%d.", item->b.position); - x = indent - SmallFont->StringWidth (tbuf) * CleanXfac_1; - screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_CleanNoMove_1, true, TAG_DONE); - } - break; - - case bitmask: - { - int v, vals; - - value = item->a.cvar->GetGenericRep (CVAR_Int); - value.Float = float(value.Int & int(item->c.max)); - vals = (int)item->b.numvalues; - - v = M_FindCurVal (value.Float, item->e.values, vals); - - if (v == vals) - { - screen->DrawText (SmallFont, ValueColor, indent + cursorspace, y, "Unknown", - DTA_CleanNoMove_1, true, TAG_DONE); - } - else - { - screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - indent + cursorspace, y, item->e.values[v].name, - DTA_CleanNoMove_1, true, TAG_DONE); - } - - } - break; - - case discretes: - case discrete: - case cdiscrete: - case inverter: - case joy_map: - { - int v, vals; - - overlay = 0; - if (item->type == joy_map) - { - value.Float = (float)SELECTED_JOYSTICK->GetAxisMap(item->a.joyselection); - } - else - { - value = item->a.cvar->GetGenericRep (CVAR_Float); - } - if (item->type == inverter) - { - value.Float = (value.Float < 0.f); - vals = 2; - } - else - { - vals = (int)item->b.numvalues; - } - if (item->type != discretes) - { - v = M_FindCurVal (value.Float, item->e.values, vals); - } - else - { - v = M_FindCurVal (value.Float, item->e.valuestrings, vals); - } - if (item->type == discrete) - { - if (item->d.graycheck != NULL && !(**item->d.graycheck)) - { - overlay = MAKEARGB(96,48,0,0); - } - } - - if (v == vals) - { - screen->DrawText (SmallFont, ValueColor, indent + cursorspace, y, "Unknown", - DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); - } - else - { - screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - indent + cursorspace, y, - item->type != discretes ? item->e.values[v].name : item->e.valuestrings[v].name.GetChars(), - DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); - } - - } - break; - - case ediscrete: - { - const char *v; - - value = item->a.cvar->GetGenericRep (CVAR_String); - v = M_FindCurVal(value.String, item->e.enumvalues, (int)item->b.numvalues); - screen->DrawText(SmallFont, ValueColor, indent + cursorspace, y, v, DTA_CleanNoMove_1, true, TAG_DONE); - } - break; - - case nochoice: - screen->DrawText (SmallFont, CR_GOLD, indent + cursorspace, y, - (item->e.values[(int)item->b.min]).name, DTA_CleanNoMove_1, true, TAG_DONE); - break; - - case joy_sens: - value.Float = SELECTED_JOYSTICK->GetSensitivity(); - M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, value.Float, 1); - break; - - case joy_slider: - if (item->e.joyslidernum == 0) - { - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - } - else - { - assert(item->e.joyslidernum == 1); - value.Float = SELECTED_JOYSTICK->GetAxisDeadZone(item->a.joyselection); - } - M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 3); - break; - - case joy_inverter: - assert(item->e.joyslidernum == 0); - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - screen->DrawText(SmallFont, ValueColor, indent + cursorspace, y, - (value.Float < 0) ? "Yes" : "No", - DTA_CleanNoMove_1, true, TAG_DONE); - break; - - case slider: - value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, value.Float, 1); - break; - - case absslider: - value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, fabs(value.Float), 1); - break; - - case intslider: - M_DrawSlider (indent + cursorspace, y + labelofs, item->b.min, item->c.max, item->a.fval, 0); - break; - - case control: - case mapcontrol: - { - char description[64]; - - C_NameKeys (description, item->b.key1, item->c.key2); - if (description[0]) - { - M_DrawConText(CR_WHITE, indent + cursorspace, y-1+labelofs, description); - } - else - { - screen->DrawText(SmallFont, CR_BLACK, indent + cursorspace, y + labelofs, "---", - DTA_CleanNoMove_1, true, TAG_DONE); - } - } - break; - - case colorpicker: - { - int box_x, box_y; - box_x = indent - 35 * CleanXfac_1; - box_y = (gameinfo.gametype & GAME_Raven) ? y - CleanYfac_1 : y; - screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + fontheight-CleanYfac_1, - item->a.colorcvar->GetIndex(), 0); - } - break; - - case palettegrid: - { - int box_x, box_y; - int x1, p; - const int w = fontheight; - const int h = fontheight; - - box_y = y - 2 * CleanYfac_1; - p = 0; - box_x = indent - 32 * CleanXfac_1; - for (x1 = 0, p = int(item->b.min * 16); x1 < 16; ++p, ++x1) - { - screen->Clear (box_x, box_y, box_x + w, box_y + h, p, 0); - if (p == CurrColorIndex || (i == CurrentItem && x1 == SelColorIndex)) - { - int r, g, b; - DWORD col; - double blinky; - if (i == CurrentItem && x1 == SelColorIndex) - { - r = 255, g = 128, b = 0; - } - else - { - r = 200, g = 200, b = 255; - } - // Make sure the cursors stand out against similar colors - // by pulsing them. - blinky = fabs(sin(I_MSTime()/1000.0)) * 0.5 + 0.5; - col = MAKEARGB(255,int(r*blinky),int(g*blinky),int(b*blinky)); - - screen->Clear (box_x, box_y, box_x + w, box_y + 1, -1, col); - screen->Clear (box_x, box_y + h-1, box_x + w, box_y + h, -1, col); - screen->Clear (box_x, box_y, box_x + 1, box_y + h, -1, col); - screen->Clear (box_x + w - 1, box_y, box_x + w, box_y + h, -1, col); - } - box_x += w; - } - } - break; - - case bitflag: - { - value_t *value; - const char *str; - - if (item->b.min) - value = NoYes; - else - value = YesNo; - - if (item->a.cvar) - { - if ((*(item->a.intcvar)) & item->e.flagmask) - str = value[1].name; - else - str = value[0].name; - } - else - { - str = "???"; - } - - screen->DrawText (SmallFont, ValueColor, - indent + cursorspace, y, str, DTA_CleanNoMove_1, true, TAG_DONE); - } - break; - - default: - break; - } - - if (item->type != palettegrid && // Palette grids draw their own cursor - i == CurrentItem && - (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) - { - M_DrawConText(CR_RED, indent + 3 * CleanXfac_1, y-CleanYfac_1+labelofs, "\xd"); - } - } - else - { - char *str = NULL; - int colwidth = screen->GetWidth() / 3; - - for (x = 0; x < 3; x++) - { - switch (x) - { - case 0: str = item->b.res1; break; - case 1: str = item->c.res2; break; - case 2: str = item->d.res3; break; - } - if (str) - { - if (x == item->e.highlight) - color = CR_GOLD; //ValueColor; - else - color = CR_BRICK; //LabelColor; - - screen->DrawText (SmallFont, color, colwidth * x + 20 * CleanXfac_1, y, str, DTA_CleanNoMove_1, true, TAG_DONE); - } - } - - if (i == CurrentItem && ((item->a.selmode != -1 && (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) || testingmode)) - { - M_DrawConText(CR_RED, item->a.selmode * colwidth + 8 * CleanXfac_1, y - CleanYfac_1 + labelofs, "\xd"); - } - } - } - - CanScrollUp = (CurrentMenu->scrollpos > 0); - CanScrollDown = (i < CurrentMenu->numitems); - VisBottom = i - 1; - - if (CanScrollUp) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop + labelofs, "\x1a"); - } - if (CanScrollDown) - { - M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1 + labelofs, "\x1b"); - } - - if (flagsvar) - { - static const FIntCVar *const vars[3] = { &dmflags, &dmflags2, &compatflags }; - char flagsblah[256]; - char *fillptr = flagsblah; - bool printed = false; - - for (int i = 0; i < 3; ++i) - { - if (flagsvar & (1 << i)) - { - if (printed) - { - fillptr += mysnprintf (fillptr, countof(flagsblah) - (fillptr - flagsblah), " "); - } - printed = true; - fillptr += mysnprintf (fillptr, countof(flagsblah) - (fillptr - flagsblah), "%s = %d", vars[i]->GetName (), **vars[i]); - } - } - screen->DrawText (SmallFont, ValueColor, - (screen->GetWidth() - SmallFont->StringWidth (flagsblah) * CleanXfac_1) / 2, 0, flagsblah, - DTA_CleanNoMove_1, true, TAG_DONE); - } -} - -void M_OptResponder(event_t *ev) -{ - menuitem_t *item = CurrentMenu->items + CurrentItem; - - if (menuactive == MENU_WaitKey && ev->type == EV_KeyDown) - { - if (ev->data1 != KEY_ESCAPE) - { - if (item->type == control) - { - Bindings.SetBind(ev->data1, item->e.command); - } - else if (item->type == mapcontrol) - { - AutomapBindings.SetBind(ev->data1, item->e.command); - } - M_BuildKeyList(CurrentMenu->items, CurrentMenu->numitems); - } - menuactive = MENU_On; - CurrentMenu->items[0].label = OldMessage; - CurrentMenu->items[0].type = OldType; - } - else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown) - { - if (CurrentMenu == &ModesMenu && (ev->data1 == 't' || ev->data1 == 'T')) - { // Test selected resolution - if (!(item->type == screenres && - GetSelectedSize (CurrentItem, &NewWidth, &NewHeight))) - { - NewWidth = SCREENWIDTH; - NewHeight = SCREENHEIGHT; - } - OldWidth = SCREENWIDTH; - OldHeight = SCREENHEIGHT; - 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); - } - else if (ev->data1 >= '0' && ev->data1 <= '9') - { // Activate an item of type numberedmore - int i; - int num = ev->data1 == '0' ? 10 : ev->data1 - '0'; - - for (i = 0; i < CurrentMenu->numitems; ++i) - { - menuitem_t *item = CurrentMenu->items + i; - - if (item->type == numberedmore && item->b.position == num) - { - CurrentItem = i; - M_OptButtonHandler(MKEY_Enter, false); - break; - } - } - } - } -} - -void M_OptButtonHandler(EMenuKey key, bool repeat) -{ - menuitem_t *item; - UCVarValue value; - - item = CurrentMenu->items + CurrentItem; - - if (item->type == bitflag && - (key == MKEY_Left || key == MKEY_Right || key == MKEY_Enter) - && !demoplayback) - { - *(item->a.intcvar) = (*(item->a.intcvar)) ^ item->e.flagmask; - return; - } - - // The controls that manipulate joystick interfaces can only be changed from the - // keyboard, because I can't think of a good way to avoid problems otherwise. - if (item->type == discrete && item->c.discretecenter == 2 && (key == MKEY_Left || key == MKEY_Right)) - { - if (repeat) - { - return; - } - for (int i = 0; i < FButtonStatus::MAX_KEYS; ++i) - { - if (MenuButtons[key].Keys[i] >= KEY_FIRSTJOYBUTTON) - { - return; - } - } - } - - switch (key) - { - default: - break; // Keep GCC quiet - - case MKEY_Down: - if (CurrentMenu->numitems > 1) - { - int modecol; - - if (item->type == screenres) - { - modecol = item->a.selmode; - item->a.selmode = -1; - } - else - { - modecol = 0; - } - - do - { - CurrentItem++; - if (CanScrollDown && CurrentItem == VisBottom) - { - CurrentMenu->scrollpos++; - VisBottom++; - } - if (CurrentItem == CurrentMenu->numitems) - { - CurrentMenu->scrollpos = 0; - CurrentItem = 0; - } - } while (CurrentMenu->items[CurrentItem].type == redtext || - CurrentMenu->items[CurrentItem].type == whitetext || - (CurrentMenu->items[CurrentItem].type == screenres && - !CurrentMenu->items[CurrentItem].b.res1) || - (CurrentMenu->items[CurrentItem].type == numberedmore && - !CurrentMenu->items[CurrentItem].b.position)); - - if (CurrentMenu->items[CurrentItem].type == screenres) - { - item = &CurrentMenu->items[CurrentItem]; - while ((modecol == 2 && !item->d.res3) || (modecol == 1 && !item->c.res2)) - { - modecol--; - } - CurrentMenu->items[CurrentItem].a.selmode = modecol; - } - - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - break; - - case MKEY_Up: - if (CurrentMenu->numitems > 1) - { - int modecol; - - if (item->type == screenres) - { - modecol = item->a.selmode; - item->a.selmode = -1; - } - else - { - modecol = 0; - } - - do - { - CurrentItem--; - if (CurrentMenu->scrollpos > 0 && - CurrentItem == CurrentMenu->scrolltop + CurrentMenu->scrollpos) - { - CurrentMenu->scrollpos--; - } - if (CurrentItem < 0) - { - int ytop, maxitems, rowheight; - - // Figure out how many lines of text fit on the menu - if (CurrentMenu->y != 0) - { - ytop = CurrentMenu->y; - } - else if (BigFont && CurrentMenu->texttitle) - { - ytop = 15 + BigFont->GetHeight (); - } - else - { - ytop = 15; - } - if (!(gameinfo.gametype & GAME_DoomChex)) - { - ytop -= 2; - rowheight = 9; - } - else - { - rowheight = 8; - } - ytop *= CleanYfac_1; - rowheight *= CleanYfac_1; - maxitems = (screen->GetHeight() - rowheight - ytop) / rowheight + 1; - - CurrentMenu->scrollpos = MAX (0,CurrentMenu->numitems - maxitems + CurrentMenu->scrolltop); - CurrentItem = CurrentMenu->numitems - 1; - } - } while (CurrentMenu->items[CurrentItem].type == redtext || - CurrentMenu->items[CurrentItem].type == whitetext || - (CurrentMenu->items[CurrentItem].type == screenres && - !CurrentMenu->items[CurrentItem].b.res1) || - (CurrentMenu->items[CurrentItem].type == numberedmore && - !CurrentMenu->items[CurrentItem].b.position)); - - if (CurrentMenu->items[CurrentItem].type == screenres) - CurrentMenu->items[CurrentItem].a.selmode = modecol; - - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - break; - - case MKEY_PageUp: - if (CurrentMenu->scrollpos > 0) - { - CurrentMenu->scrollpos -= VisBottom - CurrentMenu->scrollpos - CurrentMenu->scrolltop; - if (CurrentMenu->scrollpos < 0) - { - CurrentMenu->scrollpos = 0; - } - CurrentItem = CurrentMenu->scrolltop + CurrentMenu->scrollpos + 1; - while (CurrentMenu->items[CurrentItem].type == redtext || - CurrentMenu->items[CurrentItem].type == whitetext || - (CurrentMenu->items[CurrentItem].type == screenres && - !CurrentMenu->items[CurrentItem].b.res1) || - (CurrentMenu->items[CurrentItem].type == numberedmore && - !CurrentMenu->items[CurrentItem].b.position)) - { - ++CurrentItem; - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - break; - - case MKEY_PageDown: - if (CanScrollDown) - { - int pagesize = VisBottom - CurrentMenu->scrollpos - CurrentMenu->scrolltop; - CurrentMenu->scrollpos += pagesize; - if (CurrentMenu->scrollpos + CurrentMenu->scrolltop + pagesize > CurrentMenu->numitems) - { - CurrentMenu->scrollpos = CurrentMenu->numitems - CurrentMenu->scrolltop - pagesize; - } - CurrentItem = CurrentMenu->scrolltop + CurrentMenu->scrollpos + 1; - while (CurrentMenu->items[CurrentItem].type == redtext || - CurrentMenu->items[CurrentItem].type == whitetext || - (CurrentMenu->items[CurrentItem].type == screenres && - !CurrentMenu->items[CurrentItem].b.res1) || - (CurrentMenu->items[CurrentItem].type == numberedmore && - !CurrentMenu->items[CurrentItem].b.position)) - { - ++CurrentItem; - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - } - break; - - case MKEY_Left: - switch (item->type) - { - case slider: - case absslider: - case intslider: - { - UCVarValue newval; - bool reversed; - - if (item->type == intslider) - value.Float = item->a.fval; - else - value = item->a.cvar->GetGenericRep (CVAR_Float); - reversed = item->type == absslider && value.Float < 0.f; - newval.Float = (reversed ? -value.Float : value.Float) - item->d.step; - - if (newval.Float < item->b.min) - newval.Float = item->b.min; - else if (newval.Float > item->c.max) - newval.Float = item->c.max; - - if (reversed) - { - newval.Float = -newval.Float; - } - - if (item->type == intslider) - item->a.fval = newval.Float; - else if (item->e.cfunc) - item->e.cfunc (item->a.cvar, newval.Float); - else - item->a.cvar->SetGenericRep (newval, CVAR_Float); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_sens: - value.Float = SELECTED_JOYSTICK->GetSensitivity() - item->d.step; - if (value.Float < item->b.min) - value.Float = item->b.min; - SELECTED_JOYSTICK->SetSensitivity(value.Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_slider: - if (item->e.joyslidernum == 0) - { - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - } - else - { - assert(item->e.joyslidernum == 1); - value.Float = SELECTED_JOYSTICK->GetAxisDeadZone(item->a.joyselection); - } - if (value.Float >= 0) - { - value.Float -= item->d.step; - if (value.Float < item->b.min) - value.Float = item->b.min; - } - else - { - value.Float += item->d.step; - if (value.Float < -item->c.max) - value.Float = -item->c.max; - } - if (item->e.joyslidernum == 0) - { - SELECTED_JOYSTICK->SetAxisScale(item->a.joyselection, value.Float); - } - else - { - SELECTED_JOYSTICK->SetAxisDeadZone(item->a.joyselection, value.Float); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case palettegrid: - SelColorIndex = (SelColorIndex - 1) & 15; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - break; - - case discretes: - case discrete: - case cdiscrete: - case joy_map: - { - int cur; - int numvals; - - numvals = (int)item->b.min; - if (item->type == joy_map) - { - value.Float = (float)SELECTED_JOYSTICK->GetAxisMap(item->a.joyselection); - } - else - { - value = item->a.cvar->GetGenericRep (CVAR_Float); - } - if (item->type != discretes) - { - cur = M_FindCurVal (value.Float, item->e.values, numvals); - } - else - { - cur = M_FindCurVal (value.Float, item->e.valuestrings, numvals); - } - if (--cur < 0) - cur = numvals - 1; - - value.Float = item->type != discretes ? item->e.values[cur].value : item->e.valuestrings[cur].value; - if (item->type == joy_map) - { - SELECTED_JOYSTICK->SetAxisMap(item->a.joyselection, (EJoyAxis)(int)value.Float); - } - else - { - item->a.cvar->SetGenericRep (value, CVAR_Float); - } - - // Hack hack. Rebuild list of resolutions - if (item->e.values == Depths) - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case ediscrete: - value = item->a.cvar->GetGenericRep(CVAR_String); - value.String = const_cast(M_FindPrevVal(value.String, item->e.enumvalues, (int)item->b.numvalues)); - item->a.cvar->SetGenericRep(value, CVAR_String); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case bitmask: - { - int cur; - int numvals; - int bmask = int(item->c.max); - - numvals = (int)item->b.min; - value = item->a.cvar->GetGenericRep (CVAR_Int); - - cur = M_FindCurVal (float(value.Int & bmask), item->e.values, numvals); - if (--cur < 0) - cur = numvals - 1; - - value.Int = (value.Int & ~bmask) | int(item->e.values[cur].value); - item->a.cvar->SetGenericRep (value, CVAR_Int); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case inverter: - value = item->a.cvar->GetGenericRep (CVAR_Float); - value.Float = -value.Float; - item->a.cvar->SetGenericRep (value, CVAR_Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_inverter: - assert(item->e.joyslidernum == 0); - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - SELECTED_JOYSTICK->SetAxisScale(item->a.joyselection, -value.Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case screenres: - { - int col; - - col = item->a.selmode - 1; - if (col < 0) - { - if (CurrentItem > 0) - { - if (CurrentMenu->items[CurrentItem - 1].type == screenres) - { - item->a.selmode = -1; - CurrentMenu->items[--CurrentItem].a.selmode = 2; - } - } - } - else - { - item->a.selmode = col; - } - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - break; - - default: - break; - } - break; - - case MKEY_Right: - switch (item->type) - { - case slider: - case absslider: - case intslider: - { - UCVarValue newval; - bool reversed; - - if (item->type == intslider) - value.Float = item->a.fval; - else - value = item->a.cvar->GetGenericRep (CVAR_Float); - reversed = item->type == absslider && value.Float < 0.f; - newval.Float = (reversed ? -value.Float : value.Float) + item->d.step; - - if (newval.Float > item->c.max) - newval.Float = item->c.max; - else if (newval.Float < item->b.min) - newval.Float = item->b.min; - - if (reversed) - { - newval.Float = -newval.Float; - } - - if (item->type == intslider) - item->a.fval = newval.Float; - else if (item->e.cfunc) - item->e.cfunc (item->a.cvar, newval.Float); - else - item->a.cvar->SetGenericRep (newval, CVAR_Float); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_sens: - value.Float = SELECTED_JOYSTICK->GetSensitivity() + item->d.step; - if (value.Float > item->c.max) - value.Float = item->c.max; - SELECTED_JOYSTICK->SetSensitivity(value.Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_slider: - if (item->e.joyslidernum == 0) - { - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - } - else - { - assert(item->e.joyslidernum == 1); - value.Float = SELECTED_JOYSTICK->GetAxisDeadZone(item->a.joyselection); - } - if (value.Float >= 0) - { - value.Float += item->d.step; - if (value.Float > item->c.max) - value.Float = item->c.max; - } - else - { - value.Float -= item->d.step; - if (value.Float > item->b.min) - value.Float = -item->b.min; - } - if (item->e.joyslidernum == 0) - { - SELECTED_JOYSTICK->SetAxisScale(item->a.joyselection, value.Float); - } - else - { - SELECTED_JOYSTICK->SetAxisDeadZone(item->a.joyselection, value.Float); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case palettegrid: - SelColorIndex = (SelColorIndex + 1) & 15; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - break; - - case discretes: - case discrete: - case cdiscrete: - case joy_map: - { - int cur; - int numvals; - - numvals = (int)item->b.min; - if (item->type == joy_map) - { - value.Float = (float)SELECTED_JOYSTICK->GetAxisMap(item->a.joyselection); - } - else - { - value = item->a.cvar->GetGenericRep (CVAR_Float); - } - if (item->type != discretes) - { - cur = M_FindCurVal (value.Float, item->e.values, numvals); - } - else - { - cur = M_FindCurVal (value.Float, item->e.valuestrings, numvals); - } - if (++cur >= numvals) - cur = 0; - - value.Float = item->type != discretes ? item->e.values[cur].value : item->e.valuestrings[cur].value; - if (item->type == joy_map) - { - SELECTED_JOYSTICK->SetAxisMap(item->a.joyselection, (EJoyAxis)(int)value.Float); - } - else - { - item->a.cvar->SetGenericRep (value, CVAR_Float); - } - - // Hack hack. Rebuild list of resolutions - if (item->e.values == Depths) - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case ediscrete: - value = item->a.cvar->GetGenericRep(CVAR_String); - value.String = const_cast(M_FindNextVal(value.String, item->e.enumvalues, (int)item->b.numvalues)); - item->a.cvar->SetGenericRep(value, CVAR_String); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case bitmask: - { - int cur; - int numvals; - int bmask = int(item->c.max); - - numvals = (int)item->b.min; - value = item->a.cvar->GetGenericRep (CVAR_Int); - - cur = M_FindCurVal (float(value.Int & bmask), item->e.values, numvals); - if (++cur >= numvals) - cur = 0; - - value.Int = (value.Int & ~bmask) | int(item->e.values[cur].value); - item->a.cvar->SetGenericRep (value, CVAR_Int); - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case inverter: - value = item->a.cvar->GetGenericRep (CVAR_Float); - value.Float = -value.Float; - item->a.cvar->SetGenericRep (value, CVAR_Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case joy_inverter: - assert(item->e.joyslidernum == 0); - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - SELECTED_JOYSTICK->SetAxisScale(item->a.joyselection, -value.Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - break; - - case screenres: - { - int col; - - col = item->a.selmode + 1; - if ((col > 2) || (col == 2 && !item->d.res3) || (col == 1 && !item->c.res2)) - { - if (CurrentMenu->numitems - 1 > CurrentItem) - { - if (CurrentMenu->items[CurrentItem + 1].type == screenres) - { - if (CurrentMenu->items[CurrentItem + 1].b.res1) - { - item->a.selmode = -1; - CurrentMenu->items[++CurrentItem].a.selmode = 0; - } - } - } - } - else - { - item->a.selmode = col; - } - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); - break; - - default: - break; - } - break; - - case MKEY_Clear: - if (item->type == control) - { - Bindings.UnbindACommand (item->e.command); - item->b.key1 = item->c.key2 = 0; - } - else if (item->type == mapcontrol) - { - AutomapBindings.UnbindACommand (item->e.command); - item->b.key1 = item->c.key2 = 0; - } - break; - - case MKEY_Enter: - if (CurrentMenu == &ModesMenu && item->type == screenres) - { - if (!GetSelectedSize (CurrentItem, &NewWidth, &NewHeight)) - { - NewWidth = SCREENWIDTH; - NewHeight = SCREENHEIGHT; - } - else - { - testingmode = 1; - setmodeneeded = true; - NewBits = BitTranslate[DummyDepthCvar]; - } - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - SetModesMenu (NewWidth, NewHeight, NewBits); - } - else if ((item->type == more || - item->type == numberedmore || - item->type == rightmore || - item->type == rsafemore || - item->type == joymore || - item->type == safemore) - && item->e.mfunc) - { - CurrentMenu->lastOn = CurrentItem; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - if (item->type == safemore || item->type == rsafemore) - { - ActivateConfirm (item->label, item->e.mfunc); - } - else - { - item->e.mfunc(); - } - } - else if (item->type == discrete || item->type == cdiscrete || item->type == discretes) - { - int cur; - int numvals; - - numvals = (int)item->b.min; - value = item->a.cvar->GetGenericRep (CVAR_Float); - if (item->type != discretes) - { - cur = M_FindCurVal (value.Float, item->e.values, numvals); - } - else - { - cur = M_FindCurVal (value.Float, item->e.valuestrings, numvals); - } - if (++cur >= numvals) - cur = 0; - - value.Float = item->type != discretes ? item->e.values[cur].value : item->e.valuestrings[cur].value; - item->a.cvar->SetGenericRep (value, CVAR_Float); - - // Hack hack. Rebuild list of resolutions - if (item->e.values == Depths) - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); - - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - else if (item->type == control || item->type == mapcontrol) - { - menuactive = MENU_WaitKey; - OldMessage = CurrentMenu->items[0].label; - OldType = CurrentMenu->items[0].type; - CurrentMenu->items[0].label = "Press new key for control, ESC to cancel"; - CurrentMenu->items[0].type = redtext; - } - else if (item->type == listelement) - { - CurrentMenu->lastOn = CurrentItem; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - item->e.lfunc (CurrentItem); - } - else if (item->type == inverter) - { - value = item->a.cvar->GetGenericRep (CVAR_Float); - value.Float = -value.Float; - item->a.cvar->SetGenericRep (value, CVAR_Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - else if (item->type == joy_inverter) - { - assert(item->e.joyslidernum == 0); - value.Float = SELECTED_JOYSTICK->GetAxisScale(item->a.joyselection); - SELECTED_JOYSTICK->SetAxisScale(item->a.joyselection, -value.Float); - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); - } - else if (item->type == screenres) - { - } - else if (item->type == colorpicker) - { - CurrentMenu->lastOn = CurrentItem; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); - StartColorPickerMenu (item->label, item->a.colorcvar); - } - else if (item->type == palettegrid) - { - UpdateSelColor (SelColorIndex + int(item->b.min * 16)); - } - break; - - case MKEY_Back: - CurrentMenu->lastOn = CurrentItem; - if (CurrentMenu->EscapeHandler != NULL) - { - CurrentMenu->EscapeHandler (); - } - M_PopMenuStack (); - break; - } -} - -static void GoToConsole (void) -{ - M_ClearMenus (); - C_ToggleConsole (); -} - -static void UpdateStuff (void) -{ - M_SizeDisplay (0); -} - -void Reset2Defaults (void) -{ - C_SetDefaultBindings (); - C_SetCVarsToDefaults (); - UpdateStuff(); -} - -void Reset2Saved (void) -{ - GameConfig->DoGlobalSetup (); - GameConfig->DoGameSetup (GameNames[gameinfo.gametype]); - UpdateStuff(); -} - -static void StartMessagesMenu (void) -{ - M_SwitchMenu (&MessagesMenu); -} - -static void StartAutomapMenu (void) -{ - M_SwitchMenu (&AutomapMenu); -} - -static void StartScoreboardMenu (void) -{ - M_SwitchMenu (&ScoreboardMenu); -} - -CCMD (menu_messages) -{ - M_StartControlPanel (true); - OptionsActive = true; - StartMessagesMenu (); -} - -CCMD (menu_automap) -{ - M_StartControlPanel (true); - OptionsActive = true; - StartAutomapMenu (); -} - -CCMD (menu_scoreboard) -{ - M_StartControlPanel (true); - OptionsActive = true; - StartScoreboardMenu (); -} - -static void StartMapColorsMenu (void) -{ - M_SwitchMenu (&MapColorsMenu); -} - -static void StartMapControlsMenu (void) -{ - M_BuildKeyList (MapControlsMenu.items, MapControlsMenu.numitems); - M_SwitchMenu (&MapControlsMenu); -} - -CCMD (menu_mapcolors) -{ - M_StartControlPanel (true); - OptionsActive = true; - StartMapColorsMenu (); -} - -static void DefaultCustomColors () -{ - // Find the color cvars by scanning the MapColors menu. - for (int i = 0; i < MapColorsMenu.numitems; ++i) - { - if (MapColorsItems[i].type == colorpicker) - { - MapColorsItems[i].a.colorcvar->ResetToDefault (); - } - } -} - -static bool ColorPickerDrawer () -{ - DWORD newColor = MAKEARGB(255, - int(ColorPickerItems[2].a.fval), - int(ColorPickerItems[3].a.fval), - int(ColorPickerItems[4].a.fval)); - DWORD oldColor = DWORD(*ColorPickerItems[0].a.colorcvar) | 0xFF000000; - - int x = screen->GetWidth()*2/3; - int y = (15 + BigFont->GetHeight() + SmallFont->GetHeight()*5 - 10) * CleanYfac_1; - - screen->Clear (x, y, x + 48*CleanXfac_1, y + 48*CleanYfac_1, -1, oldColor); - screen->Clear (x + 48*CleanXfac_1, y, x + 48*2*CleanXfac_1, y + 48*CleanYfac_1, -1, newColor); - - y += 49*CleanYfac_1; - screen->DrawText (SmallFont, CR_GRAY, x+(24-SmallFont->StringWidth("Old")/2)*CleanXfac_1, y, - "Old", DTA_CleanNoMove_1, true, TAG_DONE); - screen->DrawText (SmallFont, CR_WHITE, x+(48+24-SmallFont->StringWidth("New")/2)*CleanXfac_1, y, - "New", DTA_CleanNoMove_1, true, TAG_DONE); - return false; -} - -static void SetColorPickerSliders () -{ - FColorCVar *cvar = ColorPickerItems[0].a.colorcvar; - ColorPickerItems[2].a.fval = float(RPART(DWORD(*cvar))); - ColorPickerItems[3].a.fval = float(GPART(DWORD(*cvar))); - ColorPickerItems[4].a.fval = float(BPART(DWORD(*cvar))); - CurrColorIndex = cvar->GetIndex(); -} - -static void UpdateSelColor (int index) -{ - ColorPickerItems[2].a.fval = GPalette.BaseColors[index].r; - ColorPickerItems[3].a.fval = GPalette.BaseColors[index].g; - ColorPickerItems[4].a.fval = GPalette.BaseColors[index].b; -} - -static void ColorPickerReset () -{ - SetColorPickerSliders (); -} - -static void ActivateColorChoice () -{ - UCVarValue val; - val.Int = MAKERGB - (int(ColorPickerItems[2].a.fval), - int(ColorPickerItems[3].a.fval), - int(ColorPickerItems[4].a.fval)); - ColorPickerItems[0].a.colorcvar->SetGenericRep (val, CVAR_Int); -} - -static void StartColorPickerMenu (const char *colorname, FColorCVar *cvar) -{ - ColorPickerMenu.PreDraw = ColorPickerDrawer; - ColorPickerMenu.EscapeHandler = ActivateColorChoice; - ColorPickerItems[0].label = colorname; - ColorPickerItems[0].a.colorcvar = cvar; - SetColorPickerSliders (); - M_SwitchMenu (&ColorPickerMenu); -} - -static void CustomizeControls (void) -{ - M_BuildKeyList (ControlsMenu.items, ControlsMenu.numitems); - M_SwitchMenu (&ControlsMenu); -} - -CCMD (menu_keys) -{ - M_StartControlPanel (true); - OptionsActive = true; - CustomizeControls (); -} - -EXTERN_CVAR (Int, dmflags) - -static void GameplayOptions (void) -{ - M_SwitchMenu (&DMFlagsMenu); - flagsvar = SHOW_DMFlags | SHOW_DMFlags2; -} - -CCMD (menu_gameplay) -{ - M_StartControlPanel (true); - OptionsActive = true; - GameplayOptions (); -} - -static void CompatibilityOptions (void) -{ - M_SwitchMenu (&CompatibilityMenu); - flagsvar = SHOW_CompatFlags; -} - -CCMD (menu_compatibility) -{ - M_StartControlPanel (true); - OptionsActive = true; - CompatibilityOptions (); -} - -static void MouseOptions () -{ - M_SwitchMenu (&MouseMenu); -} - -CCMD (menu_mouse) -{ - M_StartControlPanel (true); - OptionsActive = true; - MouseOptions (); -} - -static bool DrawJoystickConfigMenuHeader() -{ - FString joyname = SELECTED_JOYSTICK->GetName(); - screen->DrawText(BigFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - (screen->GetWidth() - BigFont->StringWidth(CurrentMenu->texttitle) * CleanXfac_1) / 2, - 5 * CleanYfac_1, - CurrentMenu->texttitle, DTA_CleanNoMove_1, true, TAG_DONE); - screen->DrawText(SmallFont, gameinfo.gametype & GAME_DoomChex ? CR_RED : CR_UNTRANSLATED, - (screen->GetWidth() - SmallFont->StringWidth(joyname) * CleanXfac_1) / 2, (8 + BigFont->GetHeight()) * CleanYfac_1, - joyname, DTA_CleanNoMove_1, true, TAG_DONE); - return false; -} - -static void UpdateJoystickConfigMenu(IJoystickConfig *joy) -{ - int i; - menuitem_t item = { whitetext }; - - JoystickConfigItems.Clear(); - if (joy == NULL) - { - item.type = redtext; - item.label = "Invalid controller specified for menu"; - JoystickConfigItems.Push(item); - } - else - { - SELECTED_JOYSTICK = joy; - - item.type = joy_sens; - item.label = "Overall sensitivity"; - item.b.min = 0; - item.c.max = 2; - item.d.step = 0.1f; - JoystickConfigItems.Push(item); - - item.type = redtext; - item.label = " "; - JoystickConfigItems.Push(item); - - item.type = whitetext; - if (joy->GetNumAxes() > 0) - { - item.label = "Axis Configuration"; - JoystickConfigItems.Push(item); - - for (i = 0; i < joy->GetNumAxes(); ++i) - { - item.type = redtext; - item.label = " "; - JoystickConfigItems.Push(item); - - item.type = joy_map; - item.label = joy->GetAxisName(i); - item.a.joyselection = i; - item.b.numvalues = countof(JoyAxisMapNames); - item.e.values = JoyAxisMapNames; - JoystickConfigItems.Push(item); - - item.type = joy_slider; - item.label = "Sensitivity"; - item.b.min = 0; - item.c.max = 4; - item.d.step = 0.1f; - item.e.joyslidernum = 0; - JoystickConfigItems.Push(item); - - item.type = joy_inverter; - item.label = "Invert"; - JoystickConfigItems.Push(item); - - item.type = joy_slider; - item.label = "Dead Zone"; - item.b.position = 1; - item.c.max = 0.9f; - item.d.step = 0.05f; - item.e.joyslidernum = 1; - JoystickConfigItems.Push(item); - } - } - else - { - item.label = "No configurable axes"; - JoystickConfigItems.Push(item); - } - } - JoystickConfigMenu.items = &JoystickConfigItems[0]; - JoystickConfigMenu.numitems = JoystickConfigItems.Size(); - JoystickConfigMenu.lastOn = 0; - JoystickConfigMenu.scrollpos = 0; - JoystickConfigMenu.y = 25 + BigFont->GetHeight(); - JoystickConfigMenu.PreDraw = DrawJoystickConfigMenuHeader; - if (screen != NULL) - { - CalcIndent(&JoystickConfigMenu); - } -} - -static void StartJoystickConfigMenu() -{ - UpdateJoystickConfigMenu(Joysticks[JoystickItems[JoystickMenu.lastOn].a.joyselection]); - M_SwitchMenu(&JoystickConfigMenu); -} - -void UpdateJoystickMenu(IJoystickConfig *selected) -{ - int i; - menuitem_t item = { whitetext }; - int itemnum = -1; - - JoystickItems.Clear(); - I_GetJoysticks(Joysticks); - if ((unsigned)itemnum >= Joysticks.Size()) - { - itemnum = Joysticks.Size() - 1; - } - if (selected != NULL) - { - for (i = 0; (unsigned)i < Joysticks.Size(); ++i) - { - if (Joysticks[i] == selected) - { - itemnum = i; - break; - } - } - } - item.type = discrete; - item.label = "Enable controller support"; - item.a.cvar = &use_joystick; - item.b.numvalues = 2; - item.c.discretecenter = 2; - item.e.values = YesNo; - JoystickItems.Push(item); - -#ifdef _WIN32 - item.label = "Enable DirectInput controllers"; - item.a.cvar = &joy_dinput; - JoystickItems.Push(item); - - item.label = "Enable XInput controllers"; - item.a.cvar = &joy_xinput; - JoystickItems.Push(item); - - item.label = "Enable raw PlayStation 2 adapters"; - item.a.cvar = &joy_ps2raw; - JoystickItems.Push(item); -#endif - - item.type = redtext; - item.label = " "; - item.c.discretecenter = 0; - JoystickItems.Push(item); - - if (Joysticks.Size() == 0) - { - item.type = redtext; - item.label = "No controllers detected"; - JoystickItems.Push(item); - if (!use_joystick) - { - item.type = whitetext; - item.label = "Controller support must be"; - JoystickItems.Push(item); - - item.label = "enabled to detect any"; - JoystickItems.Push(item); - } - } - else - { - item.label = "Configure controllers:"; - JoystickItems.Push(item); - - item.type = joymore; - item.e.mfunc = StartJoystickConfigMenu; - for (int i = 0; i < (int)Joysticks.Size(); ++i) - { - item.a.joyselection = i; - if (i == itemnum) - { - JoystickMenu.lastOn = JoystickItems.Size(); - } - JoystickItems.Push(item); - } - } - JoystickMenu.items = &JoystickItems[0]; - JoystickMenu.numitems = JoystickItems.Size(); - if (JoystickMenu.lastOn >= JoystickMenu.numitems) - { - JoystickMenu.lastOn = JoystickMenu.numitems - 1; - } - if (CurrentMenu == &JoystickMenu && CurrentItem >= JoystickMenu.numitems) - { - CurrentItem = JoystickMenu.lastOn; - } - if (screen != NULL) - { - CalcIndent(&JoystickMenu); - } - - // If the joystick config menu is open, close it if the device it's - // open for is gone. - for (i = 0; (unsigned)i < Joysticks.Size(); ++i) - { - if (Joysticks[i] == SELECTED_JOYSTICK) - { - break; - } - } - if (i == (int)Joysticks.Size()) - { - SELECTED_JOYSTICK = NULL; - if (CurrentMenu == &JoystickConfigMenu) - { - M_PopMenuStack(); - } - } -} - -static void JoystickOptions () -{ - UpdateJoystickMenu (NULL); - M_SwitchMenu (&JoystickMenu); -} - -CCMD (menu_joystick) -{ - M_StartControlPanel (true); - OptionsActive = true; - JoystickOptions (); -} - -static void FreeMIDIMenuList() -{ - if (SoundItems[MIDI_DEVICE_ITEM].e.values != NULL) - { - delete[] SoundItems[MIDI_DEVICE_ITEM].e.values; - } -} - -static void SoundOptions () -{ - I_BuildMIDIMenuList(&SoundItems[MIDI_DEVICE_ITEM].e.values, &SoundItems[MIDI_DEVICE_ITEM].b.min); - atterm(FreeMIDIMenuList); - M_SwitchMenu(&SoundMenu); -} - -CCMD (menu_sound) -{ - M_StartControlPanel (true); - OptionsActive = true; - SoundOptions (); -} - -static void AdvSoundOptions () -{ - M_SwitchMenu (&AdvSoundMenu); -} - -CCMD (menu_advsound) -{ - M_StartControlPanel (true); - OptionsActive = true; - AdvSoundOptions (); -} - -static void MakeSoundChanges (void) -{ - static char snd_reset[] = "snd_reset"; - AddCommandString (snd_reset); -} - -static void ModReplayerOptions() -{ - for (size_t i = 2; i < countof(ModReplayerItems); ++i) - { - if (ModReplayerItems[i].type == discrete) - { - ModReplayerItems[i].d.graycheck = &mod_dumb; - } - } - M_SwitchMenu(&ModReplayerMenu); -} - -CCMD (menu_modreplayer) -{ - M_StartControlPanel(true); - OptionsActive = true; - ModReplayerOptions(); -} - -static void VideoOptions (void) -{ - InitCrosshairsList(); - M_SwitchMenu (&VideoMenu); -} - -CCMD (menu_display) -{ - M_StartControlPanel (true); - OptionsActive = true; - InitCrosshairsList(); - M_SwitchMenu (&VideoMenu); -} - -static void BuildModesList (int hiwidth, int hiheight, int hi_bits) -{ - char strtemp[32], **str; - int i, c; - int width, height, showbits; - bool letterbox=false; - int ratiomatch; - - if (menu_screenratios >= 0 && menu_screenratios <= 4 && menu_screenratios != 3) - { - ratiomatch = menu_screenratios; - } - else - { - ratiomatch = -1; - } - showbits = BitTranslate[DummyDepthCvar]; - - if (Video != NULL) - { - Video->StartModeIterator (showbits, screen->IsFullscreen()); - } - - for (i = VM_RESSTART; ModesItems[i].type == screenres; i++) - { - ModesItems[i].e.highlight = -1; - for (c = 0; c < 3; c++) - { - bool haveMode = false; - - switch (c) - { - default: str = &ModesItems[i].b.res1; break; - case 1: str = &ModesItems[i].c.res2; break; - case 2: str = &ModesItems[i].d.res3; break; - } - if (Video != NULL) - { - while ((haveMode = Video->NextMode (&width, &height, &letterbox)) && - (ratiomatch >= 0 && CheckRatio (width, height) != ratiomatch)) - { - } - } - - if (haveMode) - { - if (/* hi_bits == showbits && */ width == hiwidth && height == hiheight) - ModesItems[i].e.highlight = ModesItems[i].a.selmode = c; - - mysnprintf (strtemp, countof(strtemp), "%dx%d%s", width, height, letterbox?TEXTCOLOR_BROWN" LB":""); - ReplaceString (str, strtemp); - } - else - { - if (*str) - { - delete[] *str; - *str = NULL; - } - } - } - } -} - -void M_RefreshModesList () -{ - BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); -} - -void M_FreeModesList () -{ - for (int i = VM_RESSTART; ModesItems[i].type == screenres; ++i) - { - for (int c = 0; c < 3; ++c) - { - char **str; - - switch (c) - { - default: str = &ModesItems[i].b.res1; break; - case 1: str = &ModesItems[i].c.res2; break; - case 2: str = &ModesItems[i].d.res3; break; - } - if (str != NULL) - { - delete[] *str; - *str = NULL; - } - } - } -} - -static bool GetSelectedSize (int line, int *width, int *height) -{ - if (ModesItems[line].type != screenres) - { - return false; - } - else - { - char *res, *breakpt; - long x, y; - - switch (ModesItems[line].a.selmode) - { - default: res = ModesItems[line].b.res1; break; - case 1: res = ModesItems[line].c.res2; break; - case 2: res = ModesItems[line].d.res3; break; - } - x = strtol (res, &breakpt, 10); - y = strtol (breakpt+1, NULL, 10); - - *width = x; - *height = y; - return true; - } -} - -static int FindBits (int bits) -{ - int i; - - for (i = 0; i < 22; i++) - { - if (BitTranslate[i] == bits) - return i; - } - - return 0; -} - -static void SetModesMenu (int w, int h, int bits) -{ - DummyDepthCvar = FindBits (bits); - - if (testingmode <= 1) - { - if (ModesItems[VM_ENTERLINE].label != VMEnterText) - free (const_cast(ModesItems[VM_ENTERLINE].label)); - ModesItems[VM_ENTERLINE].label = VMEnterText; - ModesItems[VM_TESTLINE].label = VMTestText; - } - else - { - char strtemp[64]; - - mysnprintf (strtemp, countof(strtemp), "TESTING %dx%dx%d", w, h, bits); - ModesItems[VM_ENTERLINE].label = copystring (strtemp); - ModesItems[VM_TESTLINE].label = "Please wait 5 seconds..."; - } - - BuildModesList (w, h, bits); -} - -void M_RestoreMode () -{ - NewWidth = OldWidth; - NewHeight = OldHeight; - NewBits = OldBits; - setmodeneeded = true; - testingmode = 0; - SetModesMenu (OldWidth, OldHeight, OldBits); -} - -void M_SetDefaultMode () -{ - // Make current resolution the default - vid_defwidth = SCREENWIDTH; - vid_defheight = SCREENHEIGHT; - vid_defbits = DisplayBits; - testingmode = 0; - SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits); -} - -static void SetVidMode () -{ - SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits); - if (ModesMenu.items[ModesMenu.lastOn].type == screenres) - { - if (ModesMenu.items[ModesMenu.lastOn].a.selmode == -1) - { - ModesMenu.items[ModesMenu.lastOn].a.selmode++; - } - } - M_SwitchMenu (&ModesMenu); -} - -CCMD (menu_video) -{ - M_StartControlPanel (true); - OptionsActive = true; - SetVidMode (); -} - -void M_LoadKeys (const char *modname, bool dbl) -{ - char section[64]; - - if (GameNames[gameinfo.gametype] == NULL) - return; - - mysnprintf (section, countof(section), "%s.%s%sBindings", GameNames[gameinfo.gametype], modname, - dbl ? ".Double" : "."); - - FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; - if (GameConfig->SetSection (section)) - { - const char *key, *value; - while (GameConfig->NextInSection (key, value)) - { - bindings->DoBind (key, value); - } - } -} - -int M_DoSaveKeys (FConfigFile *config, char *section, int i, bool dbl) -{ - int most = (int)CustomControlsItems.Size(); - - config->SetSection (section, true); - config->ClearCurrentSection (); - FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; - for (++i; i < most; ++i) - { - menuitem_t *item = &CustomControlsItems[i]; - if (item->type == control) - { - bindings->ArchiveBindings (config, item->e.command); - continue; - } - break; - } - return i; -} - -void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, size_t sublen) -{ - if (ControlsMenu.items == ControlsItems) - return; - - // Start after the normal controls - unsigned int i = countof(ControlsItems); - unsigned int most = CustomControlsItems.Size(); - - while (i < most) - { - menuitem_t *item = &CustomControlsItems[i]; - - if (item->type == whitetext) - { - assert (item->e.command != NULL); - mysnprintf (subsection, sublen, "%s.Bindings", item->e.command); - M_DoSaveKeys (config, section, (int)i, false); - mysnprintf (subsection, sublen, "%s.DoubleBindings", item->e.command); - i = M_DoSaveKeys (config, section, (int)i, true); - } - else - { - i++; - } - } -} - -static int AddKeySpot; - -void FreeKeySections() -{ - const unsigned int numStdControls = countof(ControlsItems); - unsigned int i; - - for (i = numStdControls; i < CustomControlsItems.Size(); ++i) - { - menuitem_t *item = &CustomControlsItems[i]; - if (item->type == whitetext || item->type == control || item->type == mapcontrol) - { - if (item->label != NULL) - { - delete[] item->label; - item->label = NULL; - } - if (item->e.command != NULL) - { - delete[] item->e.command; - item->e.command = NULL; - } - } - } -} - -CCMD (addkeysection) -{ - if (argv.argc() != 3) - { - Printf ("Usage: addkeysection \n"); - return; - } - - const int numStdControls = countof(ControlsItems); - int i; - - if (ControlsMenu.items == ControlsItems) - { // No custom controls have been defined yet. - for (i = 0; i < numStdControls; ++i) - { - CustomControlsItems.Push (ControlsItems[i]); - } - } - - // See if this section already exists - int last = (int)CustomControlsItems.Size(); - for (i = numStdControls; i < last; ++i) - { - menuitem_t *item = &CustomControlsItems[i]; - - if (item->type == whitetext && - stricmp (item->label, argv[1]) == 0) - { // found it - break; - } - } - - if (i == last) - { // Add the new section - // Limit the ini name to 32 chars - if (strlen (argv[2]) > 32) - argv[2][32] = 0; - - menuitem_t tempItem = { redtext, " " }; - - // Add a blank line to the menu - CustomControlsItems.Push (tempItem); - - // Add the section name to the menu - tempItem.type = whitetext; - tempItem.label = copystring (argv[1]); - tempItem.e.command = copystring (argv[2]); // Record ini section name in command field - CustomControlsItems.Push (tempItem); - ControlsMenu.items = &CustomControlsItems[0]; - - // Load bindings for this section from the ini - M_LoadKeys (argv[2], 0); - M_LoadKeys (argv[2], 1); - - AddKeySpot = 0; - } - else - { // Add new keys to the end of this section - do - { - i++; - } while (i < last && CustomControlsItems[i].type == control); - if (i < last) - { - AddKeySpot = i; - } - else - { - AddKeySpot = 0; - } - } -} - -CCMD (addmenukey) -{ - if (argv.argc() != 3) - { - Printf ("Usage: addmenukey \n"); - return; - } - if (ControlsMenu.items == ControlsItems) - { - Printf ("You must use addkeysection first.\n"); - return; - } - - menuitem_t newItem = { control, }; - newItem.label = copystring (argv[1]); - newItem.e.command = copystring (argv[2]); - if (AddKeySpot == 0) - { // Just add to the end of the menu - CustomControlsItems.Push (newItem); - } - else - { // Add somewhere in the middle of the menu - size_t movecount = CustomControlsItems.Size() - AddKeySpot; - CustomControlsItems.Reserve (1); - memmove (&CustomControlsItems[AddKeySpot+1], - &CustomControlsItems[AddKeySpot], - sizeof(menuitem_t)*movecount); - CustomControlsItems[AddKeySpot++] = newItem; - } - ControlsMenu.items = &CustomControlsItems[0]; - ControlsMenu.numitems = (int)CustomControlsItems.Size(); -} - -void M_Deinit () -{ - // Free bitdepth names for the modes menu. - for (size_t i = 0; i < countof(Depths); ++i) - { - if (Depths[i].name != NULL) - { - delete[] Depths[i].name; - Depths[i].name = NULL; - } - } - - // Free resolutions from the modes menu. - M_FreeModesList(); -} - -// Reads any XHAIRS lumps for the names of crosshairs and -// adds them to the display options menu. -void InitCrosshairsList() -{ - int lastlump, lump; - valuestring_t value; - - lastlump = 0; - - Crosshairs.Clear(); - value.value = 0; - value.name = "None"; - Crosshairs.Push(value); - - while ((lump = Wads.FindLump("XHAIRS", &lastlump)) != -1) - { - FScanner sc(lump); - while (sc.GetNumber()) - { - value.value = float(sc.Number); - sc.MustGetString(); - value.name = sc.String; - if (value.value != 0) - { // Check if it already exists. If not, add it. - unsigned int i; - - for (i = 1; i < Crosshairs.Size(); ++i) - { - if (Crosshairs[i].value == value.value) - { - break; - } - } - if (i < Crosshairs.Size()) - { - Crosshairs[i].name = value.name; - } - else - { - Crosshairs.Push(value); - } - } - } - } - VideoItems[CROSSHAIR_INDEX].b.numvalues = float(Crosshairs.Size()); - VideoItems[CROSSHAIR_INDEX].e.valuestrings = &Crosshairs[0]; -} diff --git a/src/menu/colorpickermenu.cpp b/src/menu/colorpickermenu.cpp new file mode 100644 index 000000000..d803022bf --- /dev/null +++ b/src/menu/colorpickermenu.cpp @@ -0,0 +1,357 @@ +/* +** 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) + + float mRed; + float mGreen; + float mBlue; + + int mGridPosX; + int mGridPosY; + + int mStartItem; + + FColorCVar *mCVar; + +public: + + DColorPickerMenu(DMenu *parent, const char *name, FOptionMenuDescriptor *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 FOptionMenuItemStaticText(name, false); + desc->mItems[mStartItem+1] = new FOptionMenuItemStaticText(" ", false); + desc->mItems[mStartItem+2] = new FOptionMenuSliderVar("Red", &mRed, 0, 255, 15, 0); + desc->mItems[mStartItem+3] = new FOptionMenuSliderVar("Green", &mGreen, 0, 255, 15, 0); + desc->mItems[mStartItem+4] = new FOptionMenuSliderVar("Blue", &mBlue, 0, 255, 15, 0); + desc->mItems[mStartItem+5] = new FOptionMenuItemStaticText(" ", false); + desc->mItems[mStartItem+6] = new FOptionMenuItemCommand("Undo changes", "undocolorpic"); + desc->mItems[mStartItem+7] = new FOptionMenuItemStaticText(" ", false); + desc->mSelectedItem = mStartItem + 2; + Init(parent, desc); + desc->mIndent = 0; + desc->CalcIndent(); + } + + void Destroy() + { + if (mStartItem >= 0) + { + for(unsigned i=0;i<8;i++) + { + delete mDesc->mItems[mStartItem+i]; + 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)); + } + + //============================================================================= + // + // + // + //============================================================================= + + bool MenuEvent (int mkey, bool fromcontroller) + { + int &mSelectedItem = mDesc->mSelectedItem; + + switch (mkey) + { + case MKEY_Down: + if (mSelectedItem == mStartItem+6) // last valid item + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + mGridPosY = 0; + // let it point to the last static item so that the super class code still has a valid item + mSelectedItem = mStartItem+7; + return true; + } + else if (mSelectedItem == mStartItem+7) + { + if (mGridPosY < 15) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + mGridPosY++; + } + return true; + } + break; + + case MKEY_Up: + if (mSelectedItem == mStartItem+7) + { + if (mGridPosY > 0) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + mGridPosY--; + } + else + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + mSelectedItem = mStartItem+6; + } + return true; + } + break; + + case MKEY_Left: + if (mSelectedItem == mStartItem+7) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + if (--mGridPosX < 0) mGridPosX = 15; + return true; + } + break; + + case MKEY_Right: + if (mSelectedItem == mStartItem+7) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + if (++mGridPosX > 15) mGridPosX = 0; + return true; + } + break; + + case MKEY_Enter: + if (mSelectedItem == mStartItem+7) + { + // Choose selected palette entry + int index = mGridPosX + mGridPosY * 16; + mRed = GPalette.BaseColors[index].r; + mGreen = GPalette.BaseColors[index].g; + mBlue = GPalette.BaseColors[index].b; + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + return true; + } + break; + } + if (mSelectedItem >= 0 && mSelectedItem < mStartItem+7) + { + if (mDesc->mItems[mDesc->mSelectedItem]->MenuEvent(mkey, fromcontroller)) return true; + } + return Super::MenuEvent(mkey, fromcontroller); + } + + //============================================================================= + // + // + // + //============================================================================= + + bool MouseEvent(int type, int mx, int my) + { + int olditem = mDesc->mSelectedItem; + bool res = Super::MouseEvent(type, mx, my); + + if (mDesc->mSelectedItem == -1 || mDesc->mSelectedItem == mStartItem+7) + { + int y = (-mDesc->mPosition + BigFont->GetHeight() + mDesc->mItems.Size() * OptionSettings.mLinespacing) * CleanYfac_1; + int h = (screen->GetHeight() - y) / 16; + int fh = OptionSettings.mLinespacing * CleanYfac_1; + int w = fh; + int yy = y + 2 * CleanYfac_1; + int indent = (screen->GetWidth() / 2); + + if (h > fh) h = fh; + else if (h < 4) return res; // no space to draw it. + + int box_y = y - 2 * CleanYfac_1; + int box_x = indent - 16*w; + + if (mx >= box_x && mx < box_x + 16*w && my >= box_y && my < box_y + 16*h) + { + int cell_x = (mx - box_x) / w; + int cell_y = (my - box_y) / h; + + if (olditem != mStartItem+7 || cell_x != mGridPosX || cell_y != mGridPosY) + { + mGridPosX = cell_x; + mGridPosY = cell_y; + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + mDesc->mSelectedItem = mStartItem+7; + if (type == MOUSE_Release) + { + MenuEvent(MKEY_Enter, true); + if (m_use_mouse == 2) mDesc->mSelectedItem = -1; + } + res = true; + } + } + return res; + } + + //============================================================================= + // + // + // + //============================================================================= + + void Drawer() + { + Super::Drawer(); + + if (mCVar == NULL) return; + int y = (-mDesc->mPosition + BigFont->GetHeight() + mDesc->mItems.Size() * OptionSettings.mLinespacing) * CleanYfac_1; + int h = (screen->GetHeight() - y) / 16; + int fh = OptionSettings.mLinespacing * CleanYfac_1; + int w = fh; + int yy = y; + + if (h > fh) h = fh; + else if (h < 4) return; // no space to draw it. + + int indent = (screen->GetWidth() / 2); + int p = 0; + + for(int i = 0; i < 16; i++, y += h) + { + int box_x, box_y; + int x1; + + box_y = y - 2 * CleanYfac_1; + box_x = indent - 16*w; + for (x1 = 0; x1 < 16; ++x1, p++) + { + screen->Clear (box_x, box_y, box_x + w, box_y + h, p, 0); + if ((mDesc->mSelectedItem == mStartItem+7) && + (/*p == CurrColorIndex ||*/ (i == mGridPosY && x1 == mGridPosX))) + { + int r, g, b; + DWORD col; + double blinky; + if (i == mGridPosY && x1 == mGridPosX) + { + r = 255, g = 128, b = 0; + } + else + { + r = 200, g = 200, b = 255; + } + // Make sure the cursors stand out against similar colors + // by pulsing them. + blinky = fabs(sin(I_MSTime()/1000.0)) * 0.5 + 0.5; + col = MAKEARGB(255,int(r*blinky),int(g*blinky),int(b*blinky)); + + screen->Clear (box_x, box_y, box_x + w, box_y + 1, -1, col); + screen->Clear (box_x, box_y + h-1, box_x + w, box_y + h, -1, col); + screen->Clear (box_x, box_y, box_x + 1, box_y + h, -1, col); + screen->Clear (box_x + w - 1, box_y, box_x + w, box_y + h, -1, col); + } + box_x += w; + } + } + y = yy; + DWORD newColor = MAKEARGB(255, int(mRed), int(mGreen), int(mBlue)); + DWORD oldColor = DWORD(*mCVar) | 0xFF000000; + + int x = screen->GetWidth()*2/3; + + screen->Clear (x, y, x + 48*CleanXfac_1, y + 48*CleanYfac_1, -1, oldColor); + screen->Clear (x + 48*CleanXfac_1, y, x + 48*2*CleanXfac_1, y + 48*CleanYfac_1, -1, newColor); + + y += 49*CleanYfac_1; + screen->DrawText (SmallFont, CR_GRAY, x+(24-SmallFont->StringWidth("Old")/2)*CleanXfac_1, y, + "Old", DTA_CleanNoMove_1, true, TAG_DONE); + screen->DrawText (SmallFont, CR_WHITE, x+(48+24-SmallFont->StringWidth("New")/2)*CleanXfac_1, y, + "New", DTA_CleanNoMove_1, true, TAG_DONE); + } +}; + +IMPLEMENT_ABSTRACT_CLASS(DColorPickerMenu) + +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) +{ + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Colorpickermenu); + if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu) + { + return new DColorPickerMenu(parent, name, (FOptionMenuDescriptor*)(*desc), cvar); + } + else + { + return NULL; + } +} + diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp new file mode 100644 index 000000000..a29df9596 --- /dev/null +++ b/src/menu/joystickmenu.cpp @@ -0,0 +1,420 @@ +/* +** joystickmenu.cpp +** The joystick configuration menus +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#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" +#include "i_music.h" +#include "m_joy.h" + +#define NO_IMP +#include "optionmenuitems.h" + + +static TArray Joysticks; +IJoystickConfig *SELECTED_JOYSTICK; + +FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy); + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderJoySensitivity : public FOptionMenuSliderBase +{ +public: + FOptionMenuSliderJoySensitivity(const char *label, double min, double max, double step, int showval) + : FOptionMenuSliderBase(label, min, max, step, showval) + { + } + + double GetValue() + { + return SELECTED_JOYSTICK->GetSensitivity(); + } + + void SetValue(double val) + { + SELECTED_JOYSTICK->SetSensitivity(float(val)); + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderJoyScale : public FOptionMenuSliderBase +{ + int mAxis; + int mNeg; + +public: + FOptionMenuSliderJoyScale(const char *label, int axis, double min, double max, double step, int showval) + : FOptionMenuSliderBase(label, min, max, step, showval) + { + mAxis = axis; + mNeg = 1; + } + + double GetValue() + { + double d = SELECTED_JOYSTICK->GetAxisScale(mAxis); + mNeg = d < 0? -1:1; + return d; + } + + void SetValue(double val) + { + SELECTED_JOYSTICK->SetAxisScale(mAxis, float(val * mNeg)); + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderJoyDeadZone : public FOptionMenuSliderBase +{ + int mAxis; + int mNeg; + +public: + FOptionMenuSliderJoyDeadZone(const char *label, int axis, double min, double max, double step, int showval) + : FOptionMenuSliderBase(label, min, max, step, showval) + { + mAxis = axis; + mNeg = 1; + } + + double GetValue() + { + double d = SELECTED_JOYSTICK->GetAxisDeadZone(mAxis); + mNeg = d < 0? -1:1; + return d; + } + + void SetValue(double val) + { + SELECTED_JOYSTICK->SetAxisDeadZone(mAxis, float(val * mNeg)); + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuItemJoyMap : public FOptionMenuItemOptionBase +{ + int mAxis; +public: + + FOptionMenuItemJoyMap(const char *label, int axis, const char *values, int center) + : FOptionMenuItemOptionBase(label, "none", values, NULL, center) + { + mAxis = axis; + } + + int GetSelection() + { + float f = (float)(int)SELECTED_JOYSTICK->GetAxisMap(mAxis); + for(unsigned i=0;imValues.Size(); i++) + { + if (fabs(f - mValues->mValues[i].Value) < FLT_EPSILON) + { + return i; + } + } + return -1; + } + + void SetSelection(int Selection) + { + SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)Selection); + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuItemInverter : public FOptionMenuItemOptionBase +{ + int mAxis; +public: + + FOptionMenuItemInverter(const char *label, int axis, int center) + : FOptionMenuItemOptionBase(label, "none", "YesNo", NULL, center) + { + mAxis = axis; + } + + int GetSelection() + { + float f = SELECTED_JOYSTICK->GetAxisScale(mAxis); + return f > 0? 0:1; + } + + void SetSelection(int Selection) + { + float f = fabs(SELECTED_JOYSTICK->GetAxisScale(mAxis)); + if (Selection) f*=-1; + SELECTED_JOYSTICK->SetAxisScale(mAxis, f); + } +}; + +class DJoystickConfigMenu : public DOptionMenu +{ + DECLARE_CLASS(DJoystickConfigMenu, DOptionMenu) +}; + +IMPLEMENT_CLASS(DJoystickConfigMenu) + +//============================================================================= +// +// Executes a CCMD, action is a CCMD name +// +//============================================================================= + +class FOptionMenuItemJoyConfigMenu : public FOptionMenuItemSubmenu +{ + IJoystickConfig *mJoy; +public: + FOptionMenuItemJoyConfigMenu(const char *label, IJoystickConfig *joy) + : FOptionMenuItemSubmenu(label, "JoystickConfigMenu") + { + mJoy = joy; + } + + bool Activate() + { + UpdateJoystickConfigMenu(mJoy); + return FOptionMenuItemSubmenu::Activate(); + } +}; + + +/*======================================= + * + * Joystick Menu + * + *=======================================*/ + +FOptionMenuDescriptor *UpdateJoystickConfigMenu(IJoystickConfig *joy) +{ + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickConfigMenu); + if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu) + { + FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc; + FOptionMenuItem *it; + for(unsigned i=0;imItems.Size();i++) + { + delete opt->mItems[i]; + opt->mItems.Clear(); + } + opt->mTitle.Format("Configure %s", joy->GetName().GetChars()); + + if (joy == NULL) + { + it = new FOptionMenuItemStaticText("Invalid controller specified for menu", false); + opt->mItems.Push(it); + } + else + { + SELECTED_JOYSTICK = joy; + + it = new FOptionMenuSliderJoySensitivity("Overall sensitivity", 0, 2, 0.1, 3); + opt->mItems.Push(it); + it = new FOptionMenuItemStaticText(" ", false); + opt->mItems.Push(it); + + if (joy->GetNumAxes() > 0) + { + it = new FOptionMenuItemStaticText("Axis Configuration", true); + opt->mItems.Push(it); + + for (int i = 0; i < joy->GetNumAxes(); ++i) + { + it = new FOptionMenuItemStaticText(" ", false); + opt->mItems.Push(it); + + it = new FOptionMenuItemJoyMap(joy->GetAxisName(i), i, "JoyAxisMapNames", false); + opt->mItems.Push(it); + it = new FOptionMenuSliderJoyScale("Overall sensitivity", i, 0, 4, 0.1, 3); + opt->mItems.Push(it); + it = new FOptionMenuItemInverter("Invert", i, false); + opt->mItems.Push(it); + it = new FOptionMenuSliderJoyDeadZone("Dead Zone", i, 0, 0.9, 0.05, 3); + opt->mItems.Push(it); + } + } + else + { + it = new FOptionMenuItemStaticText("No configurable axes", false); + opt->mItems.Push(it); + } + } + opt->mScrollPos = 0; + opt->mSelectedItem = -1; + opt->mIndent = 0; + opt->mPosition = -25; + opt->CalcIndent(); + return opt; + } + return NULL; +} + + + +void UpdateJoystickMenu(IJoystickConfig *selected) +{ + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_JoystickOptions); + if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu) + { + FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc; + FOptionMenuItem *it; + for(unsigned i=0;imItems.Size();i++) + { + delete opt->mItems[i]; + opt->mItems.Clear(); + } + + int i; + int itemnum = -1; + + I_GetJoysticks(Joysticks); + if ((unsigned)itemnum >= Joysticks.Size()) + { + itemnum = Joysticks.Size() - 1; + } + if (selected != NULL) + { + for (i = 0; (unsigned)i < Joysticks.Size(); ++i) + { + if (Joysticks[i] == selected) + { + itemnum = i; + break; + } + } + } + + // Todo: Block joystick for changing this one. + it = new FOptionMenuItemOption("Enable controller support", "use_joystick", "YesNo", NULL, false); + opt->mItems.Push(it); + #ifdef _WIN32 + it = new FOptionMenuItemOption("Enable DirectInput controllers", "joy_dinput", "YesNo", NULL, false); + opt->mItems.Push(it); + it = new FOptionMenuItemOption("Enable XInput controllers", "joy_xinput", "YesNo", NULL, false); + opt->mItems.Push(it); + it = new FOptionMenuItemOption("Enable raw PlayStation 2 adapters", "joy_ps2raw", "YesNo", NULL, false); + opt->mItems.Push(it); + #endif + + it = new FOptionMenuItemStaticText(" ", false); + opt->mItems.Push(it); + + if (Joysticks.Size() == 0) + { + it = new FOptionMenuItemStaticText("No controllers detected", false); + opt->mItems.Push(it); + if (!use_joystick) + { + it = new FOptionMenuItemStaticText("Controller support must be", false); + opt->mItems.Push(it); + it = new FOptionMenuItemStaticText("enabled to detect any", false); + opt->mItems.Push(it); + } + } + else + { + it = new FOptionMenuItemStaticText("Configure controllers:", false); + opt->mItems.Push(it); + + for (int i = 0; i < (int)Joysticks.Size(); ++i) + { + it = new FOptionMenuItemJoyConfigMenu(Joysticks[i]->GetName(), Joysticks[i]); + opt->mItems.Push(it); + if (i == itemnum) opt->mSelectedItem = opt->mItems.Size(); + } + } + if (opt->mSelectedItem >= (int)opt->mItems.Size()) + { + opt->mSelectedItem = opt->mItems.Size() - 1; + } + + opt->CalcIndent(); + + // If the joystick config menu is open, close it if the device it's + // open for is gone. + for (i = 0; (unsigned)i < Joysticks.Size(); ++i) + { + if (Joysticks[i] == SELECTED_JOYSTICK) + { + break; + } + } + if (i == (int)Joysticks.Size()) + { + SELECTED_JOYSTICK = NULL; + if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DJoystickConfigMenu))) + { + DMenu::CurrentMenu->Close(); + } + } + } +} + diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp new file mode 100644 index 000000000..42164c16b --- /dev/null +++ b/src/menu/listmenu.cpp @@ -0,0 +1,511 @@ +/* +** listmenu.cpp +** A simple menu consisting of a list of items +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "v_video.h" +#include "v_font.h" +#include "cmdlib.h" +#include "gstrings.h" +#include "g_level.h" +#include "gi.h" +#include "d_gui.h" +#include "d_event.h" +#include "menu/menu.h" + +IMPLEMENT_CLASS(DListMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DListMenu::DListMenu(DMenu *parent, FListMenuDescriptor *desc) +: DMenu(parent) +{ + mDesc = desc; + mFocusControl = NULL; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DListMenu::Init(DMenu *parent, FListMenuDescriptor *desc) +{ + mParentMenu = parent; + GC::WriteBarrier(this, parent); + mDesc = desc; +} + +//============================================================================= +// +// +// +//============================================================================= + +FListMenuItem *DListMenu::GetItem(FName name) +{ + for(unsigned i=0;imItems.Size(); i++) + { + FName nm = mDesc->mItems[i]->GetAction(NULL); + if (nm == name) return mDesc->mItems[i]; + } + return NULL; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DListMenu::Responder (event_t *ev) +{ + if (ev->type == EV_GUI_Event) + { + if (ev->subtype == EV_GUI_KeyDown) + { + int ch = tolower (ev->data1); + + for(unsigned i = mDesc->mSelectedItem + 1; i < mDesc->mItems.Size(); i++) + { + if (mDesc->mItems[i]->CheckHotkey(ch)) + { + mDesc->mSelectedItem = i; + S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + return true; + } + } + for(int i = 0; i < mDesc->mSelectedItem; i++) + { + if (mDesc->mItems[i]->CheckHotkey(ch)) + { + mDesc->mSelectedItem = i; + S_Sound(CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + return true; + } + } + } + } + return Super::Responder(ev); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DListMenu::MenuEvent (int mkey, bool fromcontroller) +{ + int startedAt = mDesc->mSelectedItem; + + switch (mkey) + { + case MKEY_Up: + do + { + if (--mDesc->mSelectedItem < 0) mDesc->mSelectedItem = mDesc->mItems.Size()-1; + } + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + return true; + + case MKEY_Down: + do + { + if (++mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) mDesc->mSelectedItem = 0; + } + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + return true; + + case MKEY_Enter: + if (mDesc->mSelectedItem >= 0 && mDesc->mItems[mDesc->mSelectedItem]->Activate()) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + } + return true; + + default: + return Super::MenuEvent(mkey, fromcontroller); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DListMenu::MouseEvent(int type, int x, int y) +{ + int sel = -1; + + // convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture + x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160; + y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100; + + if (mFocusControl != NULL) + { + mFocusControl->MouseEvent(type, x, y); + return true; + } + else + { + if ((mDesc->mWLeft <= 0 || x > mDesc->mWLeft) && + (mDesc->mWRight <= 0 || x < mDesc->mWRight)) + { + for(unsigned i=0;imItems.Size(); i++) + { + if (mDesc->mItems[i]->CheckCoordinate(x, y)) + { + if (i != mDesc->mSelectedItem) + { + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + mDesc->mSelectedItem = i; + mDesc->mItems[i]->MouseEvent(type, x, y); + return true; + } + } + } + } + mDesc->mSelectedItem = -1; + return Super::MouseEvent(type, x, y); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DListMenu::Ticker () +{ + Super::Ticker(); + for(unsigned i=0;imItems.Size(); i++) + { + mDesc->mItems[i]->Ticker(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DListMenu::Drawer () +{ + for(unsigned i=0;imItems.Size(); i++) + { + if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == i); + } + if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size()) + mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector); + Super::Drawer(); +} + +//============================================================================= +// +// base class for menu items +// +//============================================================================= + +FListMenuItem::~FListMenuItem() +{ +} + +bool FListMenuItem::CheckCoordinate(int x, int y) +{ + return false; +} + +void FListMenuItem::Ticker() +{ +} + +void FListMenuItem::Drawer(bool selected) +{ +} + +bool FListMenuItem::Selectable() +{ + return false; +} + +void FListMenuItem::DrawSelector(int xofs, int yofs, FTextureID tex) +{ + if (tex.isNull()) + { + if ((DMenu::MenuTime%8) < 6) + { + screen->DrawText(ConFont, OptionSettings.mFontColorSelection, + mXpos + xofs, mYpos + yofs, "\xd", DTA_Clean, true, TAG_DONE); + } + } + else + { + screen->DrawTexture (TexMan(tex), mXpos + xofs, mYpos + yofs, DTA_Clean, true, TAG_DONE); + } +} + +bool FListMenuItem::Activate() +{ + return false; // cannot be activated +} + +FName FListMenuItem::GetAction(int *pparam) +{ + return mAction; +} + +bool FListMenuItem::SetString(int i, const char *s) +{ + return false; +} + +bool FListMenuItem::GetString(int i, char *s, int len) +{ + return false; +} + +bool FListMenuItem::SetValue(int i, int value) +{ + return false; +} + +bool FListMenuItem::GetValue(int i, int *pvalue) +{ + return false; +} + +void FListMenuItem::Enable(bool on) +{ + mEnabled = on; +} + +bool FListMenuItem::MenuEvent(int mkey, bool fromcontroller) +{ + return false; +} + +bool FListMenuItem::MouseEvent(int type, int x, int y) +{ + return false; +} + +bool FListMenuItem::CheckHotkey(int c) +{ + return false; +} + + +//============================================================================= +// +// static patch +// +//============================================================================= + +FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered) +: FListMenuItem(x, y) +{ + mTexture = patch; + mCentered = centered; +} + +void FListMenuItemStaticPatch::Drawer(bool selected) +{ + 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 +// +//============================================================================= + +FListMenuItemStaticText::FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered) +: FListMenuItem(x, y) +{ + mText = ncopystring(text); + mFont = font; + mColor = color; + mCentered = centered; +} + +void FListMenuItemStaticText::Drawer(bool selected) +{ + const char *text = mText; + if (text != NULL) + { + 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); + } + } +} + +FListMenuItemStaticText::~FListMenuItemStaticText() +{ + if (mText != NULL) delete [] mText; +} + +//============================================================================= +// +// base class for selectable items +// +//============================================================================= + +FListMenuItemSelectable::FListMenuItemSelectable(int x, int y, int height, FName action, int param) +: FListMenuItem(x, y, action) +{ + mHeight = height; + mParam = param; + mHotkey = 0; +} + +bool FListMenuItemSelectable::CheckCoordinate(int x, int y) +{ + return mEnabled && y >= mYpos && y < mYpos + mHeight; // no x check here +} + +bool FListMenuItemSelectable::Selectable() +{ + return mEnabled; +} + +bool FListMenuItemSelectable::Activate() +{ + M_SetMenu(mAction, mParam); + return true; +} + +FName FListMenuItemSelectable::GetAction(int *pparam) +{ + if (pparam != NULL) *pparam = mParam; + return mAction; +} + +bool FListMenuItemSelectable::CheckHotkey(int c) +{ + return c == tolower(mHotkey); +} + +bool FListMenuItemSelectable::MouseEvent(int type, int x, int y) +{ + if (type == DMenu::MOUSE_Release) + { + if (DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) + { + return true; + } + } + return false; +} + +//============================================================================= +// +// text item +// +//============================================================================= + +FListMenuItemText::FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, FName child, int param) +: FListMenuItemSelectable(x, y, height, child, param) +{ + mText = ncopystring(text); + mFont = font; + mColor = color; + mHotkey = hotkey; +} + +FListMenuItemText::~FListMenuItemText() +{ + if (mText != NULL) + { + delete [] mText; + } +} + +void FListMenuItemText::Drawer(bool selected) +{ + const char *text = mText; + if (text != NULL) + { + if (*text == '$') text = GStrings(text+1); + screen->DrawText(mFont, mColor, mXpos, mYpos, text, DTA_Clean, true, TAG_DONE); + } +} + +//============================================================================= +// +// patch item +// +//============================================================================= + +FListMenuItemPatch::FListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param) +: FListMenuItemSelectable(x, y, height, child, param) +{ + mHotkey = hotkey; + mTexture = patch; +} + +void FListMenuItemPatch::Drawer(bool selected) +{ + screen->DrawTexture (TexMan(mTexture), mXpos, mYpos, DTA_Clean, true, TAG_DONE); +} diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp new file mode 100644 index 000000000..130dd0bd5 --- /dev/null +++ b/src/menu/loadsavemenu.cpp @@ -0,0 +1,1165 @@ +/* +** loadsavemenu.cpp +** The load game and save game menus +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu/menu.h" +#include "i_system.h" +#include "lists.h" +#include "version.h" +#include "g_game.h" +#include "m_png.h" +#include "w_wad.h" +#include "v_text.h" +#include "d_event.h" +#include "gstrings.h" +#include "v_palette.h" +#include "doomstat.h" +#include "gi.h" +#include "d_gui.h" + + + + +class DLoadSaveMenu : public DListMenu +{ + DECLARE_CLASS(DLoadSaveMenu, DListMenu) + +protected: + static List SaveGames; + static FSaveGameNode *TopSaveGame; + static FSaveGameNode *lastSaveSlot; + static FSaveGameNode *SelSaveGame; + + + friend void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave); + + static FSaveGameNode *RemoveSaveSlot (FSaveGameNode *file); + static void UnloadSaveStrings(); + static void InsertSaveNode (FSaveGameNode *node); + static void ReadSaveStrings (); + static void NotifyNewSave (const char *file, const char *title, bool okForQuicksave); + + + FTexture *SavePic; + FBrokenLines *SaveComment; + bool mEntering; + char savegamestring[SAVESTRINGSIZE]; + bool mWheelScrolled; + + DLoadSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); + void Destroy(); + + void UnloadSaveData (); + void ClearSaveStuff (); + void ExtractSaveData (const FSaveGameNode *node); + void Drawer (); + bool MenuEvent (int mkey, bool fromcontroller); + bool MouseEvent(int type, int x, int y); + bool Responder(event_t *ev); + +}; + +IMPLEMENT_CLASS(DLoadSaveMenu) + +List DLoadSaveMenu::SaveGames; +FSaveGameNode *DLoadSaveMenu::TopSaveGame; +FSaveGameNode *DLoadSaveMenu::lastSaveSlot; +FSaveGameNode *DLoadSaveMenu::SelSaveGame; + +FSaveGameNode *quickSaveSlot; + +//============================================================================= +// +// Save data maintenance (stored statically) +// +//============================================================================= + +FSaveGameNode *DLoadSaveMenu::RemoveSaveSlot (FSaveGameNode *file) +{ + FSaveGameNode *next = static_cast(file->Succ); + + if (file == TopSaveGame) + { + TopSaveGame = next; + } + if (quickSaveSlot == file) + { + quickSaveSlot = NULL; + } + if (lastSaveSlot == file) + { + lastSaveSlot = NULL; + } + file->Remove (); + if (!file->bNoDelete) delete file; + return next; +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::UnloadSaveStrings() +{ + while (!SaveGames.IsEmpty()) + { + RemoveSaveSlot (static_cast(SaveGames.Head)); + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) +{ + FSaveGameNode *probe; + + if (SaveGames.IsEmpty ()) + { + SaveGames.AddHead (node); + return; + } + + if (node->bOldVersion) + { // Add node at bottom of list + probe = static_cast(SaveGames.TailPred); + while (probe->Pred != NULL && probe->bOldVersion && + stricmp (node->Title, probe->Title) < 0) + { + probe = static_cast(probe->Pred); + } + node->Insert (probe); + } + else + { // Add node at top of list + probe = static_cast(SaveGames.Head); + while (probe->Succ != NULL && !probe->bOldVersion && + stricmp (node->Title, probe->Title) > 0) + { + probe = static_cast(probe->Succ); + } + node->InsertBefore (probe); + } +} + + +//============================================================================= +// +// M_ReadSaveStrings +// +// Find savegames and read their titles +// +//============================================================================= + +void DLoadSaveMenu::ReadSaveStrings () +{ + if (SaveGames.IsEmpty ()) + { + void *filefirst; + findstate_t c_file; + FString filter; + + atterm (UnloadSaveStrings); + + filter = G_BuildSaveName ("*.zds", -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); + FILE *file = fopen (filepath, "rb"); + + if (file != NULL) + { + PNGHandle *png; + char sig[16]; + char title[SAVESTRINGSIZE+1]; + bool oldVer = true; + bool addIt = false; + bool missing = false; + + // ZDoom 1.23 betas 21-33 have the savesig first. + // Earlier versions have the savesig second. + // Later versions have the savegame encapsulated inside a PNG. + // + // Old savegame versions are always added to the menu so + // the user can easily delete them if desired. + + title[SAVESTRINGSIZE] = 0; + + if (NULL != (png = M_VerifyPNG (file))) + { + char *ver = M_GetPNGText (png, "ZDoom Save Version"); + char *engine = M_GetPNGText (png, "Engine"); + if (ver != NULL) + { + if (!M_GetPNGText (png, "Title", title, SAVESTRINGSIZE)) + { + strncpy (title, I_FindName(&c_file), SAVESTRINGSIZE); + } + if (strncmp (ver, SAVESIG, 9) == 0 && + atoi (ver+9) >= MINSAVEVER && + engine != NULL) + { + // Was saved with a compatible ZDoom version, + // so check if it's for the current game. + // If it is, add it. Otherwise, ignore it. + char *iwad = M_GetPNGText (png, "Game WAD"); + if (iwad != NULL) + { + if (stricmp (iwad, Wads.GetWadName (FWadCollection::IWAD_FILENUM)) == 0) + { + addIt = true; + oldVer = false; + missing = !G_CheckSaveGameWads (png, false); + } + delete[] iwad; + } + } + else + { // An old version + addIt = true; + } + delete[] ver; + } + if (engine != NULL) + { + delete[] engine; + } + delete png; + } + else + { + fseek (file, 0, SEEK_SET); + if (fread (sig, 1, 16, file) == 16) + { + + if (strncmp (sig, "ZDOOMSAVE", 9) == 0) + { + 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) + { + addIt = true; + } + } + } + } + + if (addIt) + { + FSaveGameNode *node = new FSaveGameNode; + node->Filename = filepath; + node->bOldVersion = oldVer; + node->bMissingWads = missing; + memcpy (node->Title, title, SAVESTRINGSIZE); + InsertSaveNode (node); + } + fclose (file); + } + } while (I_FindNext (filefirst, &c_file) == 0); + I_FindClose (filefirst); + } + } + if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) + { + SelSaveGame = static_cast(SaveGames.Head); + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okForQuicksave) +{ + FSaveGameNode *node; + + if (file == NULL) + return; + + ReadSaveStrings (); + + // See if the file is already in our list + for (node = static_cast(SaveGames.Head); + node->Succ != NULL; + node = static_cast(node->Succ)) + { +#ifdef unix + if (node->Filename.Compare (file) == 0) +#else + if (node->Filename.CompareNoCase (file) == 0) +#endif + { + strcpy (node->Title, title); + node->bOldVersion = false; + node->bMissingWads = false; + break; + } + } + + if (node->Succ == NULL) + { + node = new FSaveGameNode; + strcpy (node->Title, title); + node->Filename = file; + node->bOldVersion = false; + node->bMissingWads = false; + InsertSaveNode (node); + SelSaveGame = node; + } + + if (okForQuicksave) + { + if (quickSaveSlot == NULL) quickSaveSlot = node; + lastSaveSlot = node; + } +} + +void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave) +{ + DLoadSaveMenu::NotifyNewSave(file, title, okForQuicksave); +} + +//============================================================================= +// +// End of static savegame maintenance code +// +//============================================================================= + +DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, FListMenuDescriptor *desc) +: DListMenu(parent, desc) +{ + ReadSaveStrings(); + mWheelScrolled = false; +} + +void DLoadSaveMenu::Destroy() +{ + ClearSaveStuff (); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::UnloadSaveData () +{ + if (SavePic != NULL) + { + delete SavePic; + } + if (SaveComment != NULL) + { + V_FreeBrokenLines (SaveComment); + } + + SavePic = NULL; + SaveComment = NULL; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::ClearSaveStuff () +{ + UnloadSaveData(); + if (quickSaveSlot == (FSaveGameNode *)1) + { + quickSaveSlot = NULL; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::ExtractSaveData (const FSaveGameNode *node) +{ + FILE *file; + PNGHandle *png; + + UnloadSaveData (); + + if (node != NULL && + node->Succ != NULL && + !node->Filename.IsEmpty() && + !node->bOldVersion && + (file = fopen (node->Filename.GetChars(), "rb")) != NULL) + { + if (NULL != (png = M_VerifyPNG (file))) + { + char *time, *pcomment, *comment; + size_t commentlen, totallen, timelen; + + // Extract comment + time = M_GetPNGText (png, "Creation Time"); + pcomment = M_GetPNGText (png, "Comment"); + if (pcomment != NULL) + { + commentlen = strlen (pcomment); + } + else + { + commentlen = 0; + } + if (time != NULL) + { + timelen = strlen (time); + totallen = timelen + commentlen + 3; + } + else + { + timelen = 0; + totallen = commentlen + 1; + } + if (totallen != 0) + { + comment = new char[totallen]; + + if (timelen) + { + memcpy (comment, time, timelen); + comment[timelen] = '\n'; + comment[timelen+1] = '\n'; + timelen += 2; + } + if (commentlen) + { + memcpy (comment + timelen, pcomment, commentlen); + } + comment[timelen+commentlen] = 0; + SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, comment); + delete[] comment; + delete[] time; + delete[] pcomment; + } + + // Extract pic + SavePic = PNGTexture_CreateFromFile(png, node->Filename); + + delete png; + } + fclose (file); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DLoadSaveMenu::Drawer () +{ + Super::Drawer(); + + const int savepicLeft = 10; + const int savepicTop = 54*CleanYfac; + const int savepicWidth = 216*screen->GetWidth()/640; + const int savepicHeight = 135*screen->GetHeight()/400; + + const int rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; + const int listboxLeft = savepicLeft + savepicWidth + 14; + const int listboxTop = savepicTop; + const int listboxWidth = screen->GetWidth() - listboxLeft - 10; + const int listboxHeight1 = screen->GetHeight() - listboxTop - 10; + const int listboxRows = (listboxHeight1 - 1) / rowHeight; + const int listboxHeight = listboxRows * rowHeight + 1; + const int listboxRight = listboxLeft + listboxWidth; + const int listboxBottom = listboxTop + listboxHeight; + + const int commentLeft = savepicLeft; + const int commentTop = savepicTop + savepicHeight + 16; + const int commentWidth = savepicWidth; + const int commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac; + const int commentRight = commentLeft + commentWidth; + const int commentBottom = commentTop + commentHeight; + + FSaveGameNode *node; + int i; + bool didSeeSelected = false; + + // Draw picture area + if (gameaction == ga_loadgame || gameaction == ga_savegame) + { + return; + } + + 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 + { + screen->Clear (savepicLeft, savepicTop, + savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); + + if (!SaveGames.IsEmpty ()) + { + const char *text = + (SelSaveGame == NULL || !SelSaveGame->bOldVersion) + ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); + const int textlen = SmallFont->StringWidth (text)*CleanXfac; + + screen->DrawText (SmallFont, CR_GOLD, savepicLeft+(savepicWidth-textlen)/2, + savepicTop+(savepicHeight-rowHeight)/2, text, + DTA_CleanNoMove, true, TAG_DONE); + } + } + + // Draw comment area + V_DrawFrame (commentLeft, commentTop, commentWidth, commentHeight); + screen->Clear (commentLeft, commentTop, commentRight, commentBottom, 0, 0); + if (SaveComment != NULL) + { + // 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 + // for that. + for (i = 0; SaveComment != NULL && SaveComment[i].Width >= 0 && i < 6; ++i) + { + screen->DrawText (SmallFont, CR_GOLD, commentLeft, commentTop + + SmallFont->GetHeight()*i*CleanYfac, SaveComment[i].Text, + DTA_CleanNoMove, true, TAG_DONE); + } + } + + // Draw file area + do + { + V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); + screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + + if (SaveGames.IsEmpty ()) + { + const char * text = GStrings("MNU_NOFILES"); + const int textlen = SmallFont->StringWidth (text)*CleanXfac; + + screen->DrawText (SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, + listboxTop+(listboxHeight-rowHeight)/2, text, + DTA_CleanNoMove, true, TAG_DONE); + return; + } + + for (i = 0, node = TopSaveGame; + i < listboxRows && node->Succ != NULL; + ++i, node = static_cast(node->Succ)) + { + int color; + if (node->bOldVersion) + { + color = CR_BLUE; + } + else if (node->bMissingWads) + { + color = CR_ORANGE; + } + else if (node == SelSaveGame) + { + color = CR_WHITE; + } + else + { + color = CR_TAN; + } + if (node == SelSaveGame) + { + screen->Clear (listboxLeft, listboxTop+rowHeight*i, + listboxRight, listboxTop+rowHeight*(i+1), -1, + mEntering ? MAKEARGB(255,255,0,0) : MAKEARGB(255,0,0,255)); + didSeeSelected = true; + if (!mEntering) + { + screen->DrawText (SmallFont, color, + listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, + DTA_CleanNoMove, true, TAG_DONE); + } + else + { + screen->DrawText (SmallFont, CR_WHITE, + listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, savegamestring, + DTA_CleanNoMove, true, TAG_DONE); + + screen->DrawText (SmallFont, CR_WHITE, + listboxLeft+1+SmallFont->StringWidth (savegamestring)*CleanXfac, + listboxTop+rowHeight*i+CleanYfac, + (gameinfo.gametype & (GAME_DoomStrifeChex)) ? "_" : "[", + DTA_CleanNoMove, true, TAG_DONE); + } + } + else + { + screen->DrawText (SmallFont, color, + listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, + DTA_CleanNoMove, true, TAG_DONE); + } + } + + // This is dumb: If the selected node was not visible, + // scroll down and redraw. M_SaveLoadResponder() + // guarantees that if the node is not visible, it will + // always be below the visible list instead of above it. + // This should not really be done here, but I don't care. + + if (!didSeeSelected) + { + // no, this shouldn't be here - and that's why there's now another hack in here + // so that the mouse scrolling does not get screwed by this code... + if (mWheelScrolled) + { + didSeeSelected = true; + SelSaveGame = NULL; + mWheelScrolled = false; + } + for (i = 1; node->Succ != NULL && node != SelSaveGame; ++i) + { + node = static_cast(node->Succ); + } + if (node->Succ == NULL) + { // SelSaveGame is invalid + didSeeSelected = true; + } + else + { + do + { + TopSaveGame = static_cast(TopSaveGame->Succ); + } while (--i); + } + } + } while (!didSeeSelected); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) +{ + switch (mkey) + { + case MKEY_Up: + if (SelSaveGame == NULL) + { + SelSaveGame = TopSaveGame; + } + else if (SelSaveGame->Succ != NULL) + { + if (SelSaveGame != SaveGames.Head) + { + if (SelSaveGame == TopSaveGame) + { + TopSaveGame = static_cast(TopSaveGame->Pred); + } + SelSaveGame = static_cast(SelSaveGame->Pred); + } + else + { + SelSaveGame = static_cast(SaveGames.TailPred); + } + UnloadSaveData (); + ExtractSaveData (SelSaveGame); + } + return true; + + case MKEY_Down: + if (SelSaveGame == NULL) + { + SelSaveGame = TopSaveGame; + } + else if (SelSaveGame->Succ != NULL) + { + if (SelSaveGame != SaveGames.TailPred) + { + SelSaveGame = static_cast(SelSaveGame->Succ); + } + else + { + SelSaveGame = TopSaveGame = + static_cast(SaveGames.Head); + } + UnloadSaveData (); + ExtractSaveData (SelSaveGame); + } + return true; + + case MKEY_Enter: + return false; // This event will be handled by the subclasses + + case MKEY_MBYes: + { + if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + { + FSaveGameNode *next = static_cast(SelSaveGame->Succ); + if (next->Succ == NULL) + { + next = static_cast(SelSaveGame->Pred); + if (next->Pred == NULL) + { + next = NULL; + } + } + + remove (SelSaveGame->Filename.GetChars()); + UnloadSaveData (); + SelSaveGame = RemoveSaveSlot (SelSaveGame); + ExtractSaveData (SelSaveGame); + } + return true; + } + + default: + return Super::MenuEvent(mkey, fromcontroller); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DLoadSaveMenu::MouseEvent(int type, int x, int y) +{ + const int savepicLeft = 10; + const int savepicTop = 54*CleanYfac; + const int savepicWidth = 216*screen->GetWidth()/640; + + const int rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; + const int listboxLeft = savepicLeft + savepicWidth + 14; + const int listboxTop = savepicTop; + const int listboxWidth = screen->GetWidth() - listboxLeft - 10; + const int listboxHeight1 = screen->GetHeight() - listboxTop - 10; + const int listboxRows = (listboxHeight1 - 1) / rowHeight; + const int listboxHeight = listboxRows * rowHeight + 1; + const int listboxRight = listboxLeft + listboxWidth; + const int listboxBottom = listboxTop + listboxHeight; + + if (x >= listboxLeft && x < listboxLeft + listboxWidth && + y >= listboxTop && y < listboxTop + listboxHeight) + { + int lineno = (y - listboxTop) / rowHeight; + FSaveGameNode *top = TopSaveGame; + while (lineno > 0 && top->Succ != NULL) + { + lineno--; + top = (FSaveGameNode *)top->Succ; + } + if (lineno == 0) + { + if (SelSaveGame != top) + { + SelSaveGame = top; + UnloadSaveData (); + ExtractSaveData (SelSaveGame); + // Sound? + } + if (type == MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + return true; + } + } + } + else SelSaveGame = NULL; + } + else + { + SelSaveGame = NULL; + } + return Super::MouseEvent(type, x, y); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DLoadSaveMenu::Responder (event_t *ev) +{ + if (ev->type == EV_GUI_Event) + { + if (ev->subtype == EV_GUI_KeyDown) + { + if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + { + switch (ev->data1) + { + case GK_F1: + if (!SelSaveGame->Filename.IsEmpty()) + { + char workbuf[512]; + + mysnprintf (workbuf, countof(workbuf), "File on disk:\n%s", SelSaveGame->Filename.GetChars()); + if (SaveComment != NULL) + { + V_FreeBrokenLines (SaveComment); + } + SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, workbuf); + } + return true; + + case GK_DEL: + case '\b': + { + FString EndString; + EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", + GStrings("MNU_DELETESG"), SelSaveGame->Title, GStrings("PRESSYN")); + M_StartMessage (EndString, 0); + } + return true; + } + } + } + else if (ev->subtype == EV_GUI_WheelUp) + { + if (TopSaveGame != SaveGames.Head && TopSaveGame != NULL) + { + TopSaveGame = static_cast(TopSaveGame->Pred); + mWheelScrolled = true; + } + return true; + } + else if (ev->subtype == EV_GUI_WheelDown) + { + const int savepicTop = 54*CleanYfac; + const int listboxTop = savepicTop; + const int rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; + const int listboxHeight1 = screen->GetHeight() - listboxTop - 10; + const int listboxRows = (listboxHeight1 - 1) / rowHeight; + + FSaveGameNode *node = TopSaveGame; + if (node != NULL) + { + int count = 1; + while (node != NULL && node != SaveGames.TailPred) + { + node = (FSaveGameNode*)node->Succ; + count++; + } + if (count > listboxRows) + { + TopSaveGame = (FSaveGameNode*)TopSaveGame->Succ; + mWheelScrolled = true; + } + } + } + } + return Super::Responder(ev); +} + + +//============================================================================= +// +// +// +//============================================================================= + +class DSaveMenu : public DLoadSaveMenu +{ + DECLARE_CLASS(DSaveMenu, DLoadSaveMenu) + + FSaveGameNode NewSaveNode; + +public: + + DSaveMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); + void Destroy(); + void DoSave (FSaveGameNode *node); + bool Responder (event_t *ev); + bool MenuEvent (int mkey, bool fromcontroller); + +}; + +IMPLEMENT_CLASS(DSaveMenu) + + +//============================================================================= +// +// +// +//============================================================================= + +DSaveMenu::DSaveMenu(DMenu *parent, FListMenuDescriptor *desc) +: DLoadSaveMenu(parent, desc) +{ + strcpy (NewSaveNode.Title, ""); + NewSaveNode.bNoDelete = true; + SaveGames.AddHead (&NewSaveNode); + TopSaveGame = static_cast(SaveGames.Head); + if (lastSaveSlot == NULL) + { + SelSaveGame = &NewSaveNode; + } + else + { + SelSaveGame = lastSaveSlot; + } + ExtractSaveData (SelSaveGame); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DSaveMenu::Destroy() +{ + if (SaveGames.Head == &NewSaveNode) + { + SaveGames.RemHead (); + if (SelSaveGame == &NewSaveNode) + { + SelSaveGame = static_cast(SaveGames.Head); + } + if (TopSaveGame == &NewSaveNode) + { + TopSaveGame = static_cast(SaveGames.Head); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +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(); + BorderNeedRefresh = screen->GetPageCount (); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) +{ + if (Super::MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) + { + return false; + } + + if (mkey == MKEY_Enter) + { + if (SelSaveGame != &NewSaveNode) + { + strcpy (savegamestring, SelSaveGame->Title); + } + else + { + savegamestring[0] = 0; + } + DMenu *input = new DTextEnterMenu(this, savegamestring, SAVESTRINGSIZE, 1, fromcontroller); + M_ActivateMenu(input); + mEntering = true; + } + else if (mkey == MKEY_Input) + { + mEntering = false; + DoSave(SelSaveGame); + } + else if (mkey == MKEY_Abort) + { + mEntering = false; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DSaveMenu::Responder (event_t *ev) +{ + if (ev->subtype == EV_GUI_KeyDown) + { + if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + { + switch (ev->data1) + { + case GK_DEL: + case '\b': + // cannot delete 'new save game' item + if (SelSaveGame == &NewSaveNode) return true; + break; + + case 'N': + SelSaveGame = TopSaveGame = &NewSaveNode; + UnloadSaveData (); + return true; + } + } + } + return Super::Responder(ev); +} + +//============================================================================= +// +// +// +//============================================================================= + +class DLoadMenu : public DLoadSaveMenu +{ + DECLARE_CLASS(DLoadMenu, DLoadSaveMenu) + +public: + + DLoadMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); + + bool MenuEvent (int mkey, bool fromcontroller); +}; + +IMPLEMENT_CLASS(DLoadMenu) + + +//============================================================================= +// +// +// +//============================================================================= + +DLoadMenu::DLoadMenu(DMenu *parent, FListMenuDescriptor *desc) +: DLoadSaveMenu(parent, desc) +{ + TopSaveGame = static_cast(SaveGames.Head); + ExtractSaveData (SelSaveGame); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) +{ + if (Super::MenuEvent(mkey, fromcontroller)) + { + return true; + } + if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) + { + return false; + } + + if (mkey == MKEY_Enter) + { + G_LoadGame (SelSaveGame->Filename.GetChars()); + if (gamestate == GS_FULLCONSOLE) + { + gamestate = GS_HIDECONSOLE; + } + if (quickSaveSlot == (FSaveGameNode *)1) + { + quickSaveSlot = SelSaveGame; + } + M_ClearMenus(); + BorderNeedRefresh = screen->GetPageCount (); + return true; + } + return false; +} + diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp new file mode 100644 index 000000000..cb557512f --- /dev/null +++ b/src/menu/menu.cpp @@ -0,0 +1,949 @@ +/* +** menu.cpp +** Menu base class and global interface +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "c_dispatch.h" +#include "d_gui.h" +#include "d_player.h" +#include "g_level.h" +#include "c_console.h" +#include "c_bind.h" +#include "s_sound.h" +#include "p_tick.h" +#include "g_game.h" +#include "c_cvars.h" +#include "d_event.h" +#include "v_video.h" +#include "hu_stuff.h" +#include "gi.h" +#include "i_input.h" +#include "gameconfigfile.h" +#include "gstrings.h" +#include "r_main.h" +#include "menu/menu.h" +#include "textures/textures.h" + +// +// Todo: Move these elsewhere +// +CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE) + + +CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE) +CVAR(Int, m_use_mouse, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +DMenu *DMenu::CurrentMenu; +int DMenu::MenuTime; + +FListMenuDescriptor *MainMenu; +FGameStartup GameStartupInfo; +EMenuState menuactive; +bool M_DemoNoPlay; +FButtonStatus MenuButtons[NUM_MKEYS]; +int MenuButtonTickers[NUM_MKEYS]; +bool MenuButtonOrigin[NUM_MKEYS]; +int BackbuttonTime; +fixed_t BackbuttonAlpha; + + +#define KEY_REPEAT_DELAY (TICRATE*5/12) +#define KEY_REPEAT_RATE (3) + +//============================================================================ +// +// DMenu base class +// +//============================================================================ + +IMPLEMENT_POINTY_CLASS (DMenu) + DECLARE_POINTER(mParentMenu) +END_POINTERS + +DMenu::DMenu(DMenu *parent) +{ + mParentMenu = parent; + mMouseCapture = false; + mBackbuttonSelected = false; + GC::WriteBarrier(this, parent); +} + +bool DMenu::Responder (event_t *ev) +{ + if (ev->type == EV_GUI_Event) + { + if (ev->subtype == EV_GUI_LButtonDown) + { + MouseEventBack(MOUSE_Click, ev->data1, ev->data2); + if (MouseEvent(MOUSE_Click, ev->data1, ev->data2)) + { + SetCapture(); + } + + } + else if (ev->subtype == EV_GUI_MouseMove) + { + BackbuttonTime = BACKBUTTON_TIME; + if (mMouseCapture || m_use_mouse == 1) + { + MouseEventBack(MOUSE_Move, ev->data1, ev->data2); + return MouseEvent(MOUSE_Move, ev->data1, ev->data2); + } + } + else if (ev->subtype == EV_GUI_LButtonUp) + { + if (mMouseCapture) + { + ReleaseCapture(); + MouseEventBack(MOUSE_Release, ev->data1, ev->data2); + return MouseEvent(MOUSE_Release, ev->data1, ev->data2); + } + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMenu::MenuEvent (int mkey, bool fromcontroller) +{ + switch (mkey) + { + case MKEY_Back: + { + Close(); + S_Sound (CHAN_VOICE | CHAN_UI, + DMenu::CurrentMenu != NULL? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE); + return true; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMenu::Close () +{ + assert(DMenu::CurrentMenu == this); + DMenu::CurrentMenu = mParentMenu; + Destroy(); + if (DMenu::CurrentMenu != NULL) + { + GC::WriteBarrier(DMenu::CurrentMenu); + } + else + { + M_ClearMenus (); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMenu::MouseEvent(int type, int x, int y) +{ + return true; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMenu::MouseEventBack(int type, int x, int y) +{ + if (m_show_backbutton >= 0) + { + FTexture *tex = TexMan[gameinfo.mBackButton]; + if (tex != NULL) + { + if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetScaledWidth() * CleanXfac; + if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetScaledHeight() * CleanYfac; + mBackbuttonSelected = (x >= 0 && x < tex->GetScaledWidth() * CleanXfac && y < tex->GetScaledHeight() * CleanYfac); + if (mBackbuttonSelected && type == MOUSE_Release) + { + if (m_use_mouse == 2) mBackbuttonSelected = false; + MenuEvent(MKEY_Back, true); + } + return true; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMenu::SetCapture() +{ + if (!mMouseCapture) + { + mMouseCapture = true; + I_SetMouseCapture(); + } +} + +void DMenu::ReleaseCapture() +{ + if (mMouseCapture) + { + mMouseCapture = false; + I_ReleaseMouseCapture(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMenu::Ticker () +{ +} + +void DMenu::Drawer () +{ + if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse) + { + FTexture *tex = TexMan[gameinfo.mBackButton]; + int w = tex->GetScaledWidth() * CleanXfac; + int h = tex->GetScaledHeight() * CleanYfac; + int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w; + int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h; + if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1)) + { + screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE); + } + else + { + screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE); + } + } +} + +bool DMenu::DimAllowed() +{ + return true; +} + +bool DMenu::TranslateKeyboardEvents() +{ + return true; +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_StartControlPanel (bool makeSound) +{ + // intro might call this repeatedly + if (DMenu::CurrentMenu != NULL) + return; + + ResetButtonStates (); + for (int i = 0; i < NUM_MKEYS; ++i) + { + MenuButtons[i].ReleaseKey(0); + } + + C_HideConsole (); // [RH] Make sure console goes bye bye. + menuactive = MENU_On; + // Pause sound effects before we play the menu switch sound. + // That way, it won't be paused. + P_CheckTickerPaused (); + + if (makeSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); + } + BackbuttonTime = 0; + BackbuttonAlpha = 0; +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_ActivateMenu(DMenu *menu) +{ + if (menuactive == MENU_Off) menuactive = MENU_On; + if (DMenu::CurrentMenu != NULL) DMenu::CurrentMenu->ReleaseCapture(); + DMenu::CurrentMenu = menu; + GC::WriteBarrier(DMenu::CurrentMenu); +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_SetMenu(FName menu, int param) +{ + // some menus need some special treatment + switch (menu) + { + case NAME_Episodemenu: + // sent from the player class menu + GameStartupInfo.Skill = -1; + GameStartupInfo.Episode = -1; + GameStartupInfo.PlayerClass = + param == -1? "Random" : PlayerClasses[param].Type->Meta.GetMetaString (APMETA_DisplayName); + break; + + case NAME_Skillmenu: + // sent from the episode menu + + if ((gameinfo.flags & GI_SHAREWARE) && param > 0) + { + // Only Doom and Heretic have multi-episode shareware versions. + if (gameinfo.gametype == GAME_Doom) + { + M_StartMessage(GStrings("SWSTRING"), 1); + } + else + { + M_StartMessage(GStrings("MNU_ONLYREGISTERED"), 1); + } + return; + } + + GameStartupInfo.Episode = param; + M_StartupSkillMenu(&GameStartupInfo); // needs player class name from class menu (later) + break; + + case NAME_StartgameConfirm: + { + // sent from the skill menu for a skill that needs to be confirmed + GameStartupInfo.Skill = param; + + const char *msg = AllSkills[param].MustConfirmText; + if (*msg==0) msg = GStrings("NIGHTMARE"); + M_StartMessage (msg, 0, NAME_Startgame); + return; + } + + case NAME_Startgame: + // sent either from skill menu or confirmation screen. Skill gets only set if sent from skill menu + // Now we can finally start the game. Ugh... + if (GameStartupInfo.Skill == -1) GameStartupInfo.Skill = param; + + G_DeferedInitNew (&GameStartupInfo); + if (gamestate == GS_FULLCONSOLE) + { + gamestate = GS_HIDECONSOLE; + gameaction = ga_newgame; + } + M_ClearMenus (); + return; + + case NAME_Savegamemenu: + if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) + { + // cannot save outside the game. + M_StartMessage (GStrings("SAVEDEAD"), 1); + return; + } + } + + // End of special checks + + FMenuDescriptor **desc = MenuDescriptors.CheckKey(menu); + if (desc != NULL) + { + if ((*desc)->mNetgameMessage.IsNotEmpty() && netgame) + { + M_StartMessage((*desc)->mNetgameMessage, 1); + return; + } + + if ((*desc)->mType == MDESC_ListMenu) + { + FListMenuDescriptor *ld = static_cast(*desc); + if (ld->mAutoselect >= 0 && ld->mAutoselect < (int)ld->mItems.Size()) + { + // recursively activate the autoselected item without ever creating this menu. + ld->mItems[ld->mAutoselect]->Activate(); + } + else + { + const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DListMenu) : ld->mClass; + + DListMenu *newmenu = (DListMenu *)cls->CreateNew(); + newmenu->Init(DMenu::CurrentMenu, ld); + M_ActivateMenu(newmenu); + } + } + else if ((*desc)->mType == MDESC_OptionsMenu) + { + FOptionMenuDescriptor *ld = static_cast(*desc); + const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DOptionMenu) : ld->mClass; + + DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew(); + newmenu->Init(DMenu::CurrentMenu, ld); + M_ActivateMenu(newmenu); + } + return; + } + else + { + const PClass *menuclass = PClass::FindClass(menu); + if (menuclass != NULL) + { + if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu))) + { + DMenu *newmenu = (DMenu*)menuclass->CreateNew(); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); + return; + } + } + } + Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars()); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool M_Responder (event_t *ev) +{ + int ch = 0; + bool keyup = false; + int mkey = NUM_MKEYS; + bool fromcontroller = true; + + if (chatmodeon) + { + return false; + } + + if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off) + { + // There are a few input sources we are interested in: + // + // EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers + // EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard + // EV_GUI_Char : printable characters, which we want in string input mode + // + // This code previously listened for EV_GUI_KeyRepeat to handle repeating + // in the menus, but that doesn't work with gamepads, so now we combine + // the multiple inputs into buttons and handle the repetition manually. + if (ev->type == EV_GUI_Event) + { + fromcontroller = false; + if (ev->subtype == EV_GUI_KeyRepeat) + { + // We do our own key repeat handling but still want to eat the + // OS's repeated keys. + return true; + } + else if (ev->subtype == EV_GUI_BackButtonDown || ev->subtype == EV_GUI_BackButtonUp) + { + mkey = MKEY_Back; + keyup = ev->subtype == EV_GUI_BackButtonUp; + } + else if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp) + { + // do we want mouse input? + if (ev->subtype >= EV_GUI_FirstMouseEvent && ev->subtype <= EV_GUI_LastMouseEvent) + { + // FIXME: Mouse events in SDL code are mostly useless so mouse is + // disabled until that code is fixed + #ifdef _WIN32 + if (!m_use_mouse) + #endif + return true; + } + + // pass everything else on to the current menu + return DMenu::CurrentMenu->Responder(ev); + } + else if (DMenu::CurrentMenu->TranslateKeyboardEvents()) + { + ch = ev->data1; + keyup = ev->subtype == EV_GUI_KeyUp; + switch (ch) + { + case GK_BACK: mkey = MKEY_Back; break; + case GK_ESCAPE: mkey = MKEY_Back; break; + case GK_RETURN: mkey = MKEY_Enter; break; + case GK_UP: mkey = MKEY_Up; break; + case GK_DOWN: mkey = MKEY_Down; break; + case GK_LEFT: mkey = MKEY_Left; break; + case GK_RIGHT: mkey = MKEY_Right; break; + case GK_BACKSPACE: mkey = MKEY_Clear; break; + case GK_PGUP: mkey = MKEY_PageUp; break; + case GK_PGDN: mkey = MKEY_PageDown; break; + default: + if (!keyup) + { + return DMenu::CurrentMenu->Responder(ev); + } + break; + } + } + } + else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp) + { + keyup = ev->type == EV_KeyUp; + + ch = ev->data1; + switch (ch) + { + case KEY_JOY1: + case KEY_PAD_A: + mkey = MKEY_Enter; + break; + + case KEY_JOY2: + case KEY_PAD_B: + mkey = MKEY_Back; + break; + + case KEY_JOY3: + case KEY_PAD_X: + mkey = MKEY_Clear; + break; + + case KEY_JOY5: + case KEY_PAD_LSHOULDER: + mkey = MKEY_PageUp; + break; + + case KEY_JOY6: + case KEY_PAD_RSHOULDER: + mkey = MKEY_PageDown; + break; + + case KEY_PAD_DPAD_UP: + case KEY_PAD_LTHUMB_UP: + case KEY_JOYAXIS1MINUS: + case KEY_JOYPOV1_UP: + mkey = MKEY_Up; + break; + + case KEY_PAD_DPAD_DOWN: + case KEY_PAD_LTHUMB_DOWN: + case KEY_JOYAXIS1PLUS: + case KEY_JOYPOV1_DOWN: + mkey = MKEY_Down; + break; + + case KEY_PAD_DPAD_LEFT: + case KEY_PAD_LTHUMB_LEFT: + case KEY_JOYAXIS2MINUS: + case KEY_JOYPOV1_LEFT: + mkey = MKEY_Left; + break; + + case KEY_PAD_DPAD_RIGHT: + case KEY_PAD_LTHUMB_RIGHT: + case KEY_JOYAXIS2PLUS: + case KEY_JOYPOV1_RIGHT: + mkey = MKEY_Right; + break; + } + } + + if (mkey != NUM_MKEYS) + { + if (keyup) + { + MenuButtons[mkey].ReleaseKey(ch); + return false; + } + else + { + MenuButtons[mkey].PressKey(ch); + MenuButtonOrigin[mkey] = fromcontroller; + if (mkey <= MKEY_PageDown) + { + MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; + } + DMenu::CurrentMenu->MenuEvent(mkey, fromcontroller); + return true; + } + } + return DMenu::CurrentMenu->Responder(ev) || !keyup; + } + else + { + if (ev->type == EV_KeyDown) + { + // Pop-up menu? + if (ev->data1 == KEY_ESCAPE) + { + M_StartControlPanel(true); + M_SetMenu(NAME_Mainmenu, -1); + return true; + } + // If devparm is set, pressing F1 always takes a screenshot no matter + // what it's bound to. (for those who don't bother to read the docs) + if (devparm && ev->data1 == KEY_F1) + { + G_ScreenShot(NULL); + return true; + } + return false; + } + else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_LButtonDown && + ConsoleState != c_down && m_use_mouse) + { + M_StartControlPanel(true); + M_SetMenu(NAME_Mainmenu, -1); + return true; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_Ticker (void) +{ + DMenu::MenuTime++; + if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off) + DMenu::CurrentMenu->Ticker(); + + for (int i = 0; i < NUM_MKEYS; ++i) + { + if (MenuButtons[i].bDown) + { + if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) + { + MenuButtonTickers[i] = KEY_REPEAT_RATE; + DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]); + } + } + } + + if (BackbuttonTime > 0) + { + if (BackbuttonAlpha < FRACUNIT) BackbuttonAlpha += FRACUNIT/10; + BackbuttonTime--; + } + else + { + if (BackbuttonAlpha > 0) BackbuttonAlpha -= FRACUNIT/10; + if (BackbuttonAlpha < 0) BackbuttonAlpha = 0; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_Drawer (void) +{ + player_t *player = &players[consoleplayer]; + AActor *camera = player->camera; + PalEntry fade = 0; + + if (!screen->Accel2D && camera != NULL && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL)) + { + if (camera->player != NULL) + { + player = camera->player; + } + fade = PalEntry (BYTE(player->BlendA*255), BYTE(player->BlendR*255), BYTE(player->BlendG*255), BYTE(player->BlendB*255)); + } + + + if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off) + { + if (DMenu::CurrentMenu->DimAllowed()) screen->Dim(fade); + DMenu::CurrentMenu->Drawer(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_ClearMenus () +{ + M_DemoNoPlay = false; + if (DMenu::CurrentMenu != NULL) + { + DMenu::CurrentMenu->Destroy(); + DMenu::CurrentMenu = NULL; + } + BorderNeedRefresh = screen->GetPageCount (); + menuactive = MENU_Off; +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_Init (void) +{ + M_ParseMenuDefs(); + M_CreateMenus(); +} + + +//============================================================================= +// +// [RH] Most menus can now be accessed directly +// through console commands. +// +//============================================================================= + +CCMD (menu_main) +{ + M_StartControlPanel(true); + M_SetMenu(NAME_Mainmenu, -1); +} + +CCMD (menu_load) +{ // F3 + M_StartControlPanel (true); + M_SetMenu(NAME_Loadgamemenu, -1); +} + +CCMD (menu_save) +{ // F2 + M_StartControlPanel (true); + M_SetMenu(NAME_Savegamemenu, -1); +} + +CCMD (menu_help) +{ // F1 + M_StartControlPanel (true); + M_SetMenu(NAME_Readthismenu, -1); +} + +CCMD (menu_game) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_Playerclassmenu, -1); // The playerclass menu is the first in the 'start game' chain +} + +CCMD (menu_options) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_Optionsmenu, -1); +} + +CCMD (menu_player) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_Playermenu, -1); +} + +CCMD (menu_messages) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_MessageOptions, -1); +} + +CCMD (menu_automap) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_AutomapOptions, -1); +} + +CCMD (menu_scoreboard) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_ScoreboardOptions, -1); +} + +CCMD (menu_mapcolors) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_MapColorMenu, -1); +} + +CCMD (menu_keys) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_CustomizeControls, -1); +} + +CCMD (menu_gameplay) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_GameplayOptions, -1); +} + +CCMD (menu_compatibility) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_CompatibilityOptions, -1); +} + +CCMD (menu_mouse) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_MouseOptions, -1); +} + +CCMD (menu_joystick) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_JoystickOptions, -1); +} + +CCMD (menu_sound) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_SoundOptions, -1); +} + +CCMD (menu_advsound) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_AdvSoundOptions, -1); +} + +CCMD (menu_modreplayer) +{ + M_StartControlPanel(true); + M_SetMenu(NAME_ModReplayerOptions, -1); +} + +CCMD (menu_display) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_VideoOptions, -1); +} + +CCMD (menu_video) +{ + M_StartControlPanel (true); + M_SetMenu(NAME_VideoModeMenu, -1); +} + + + +CCMD (openmenu) +{ + if (argv.argc() < 2) + { + Printf("Usage: openmenu \"menu_name\""); + return; + } + M_StartControlPanel (true); + M_SetMenu(argv[1], -1); +} + +// +// Toggle messages on/off +// +CCMD (togglemessages) +{ + if (show_messages) + { + Printf (128, "%s\n", GStrings("MSGOFF")); + show_messages = false; + } + else + { + Printf (128, "%s\n", GStrings("MSGON")); + show_messages = true; + } +} + +EXTERN_CVAR (Int, screenblocks) + +CCMD (sizedown) +{ + screenblocks = screenblocks - 1; + S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); +} + +CCMD (sizeup) +{ + screenblocks = screenblocks + 1; + S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); +} + +CCMD(menuconsole) +{ + M_ClearMenus(); + C_ToggleConsole(); +} + +CCMD(reset2defaults) +{ + C_SetDefaultBindings (); + C_SetCVarsToDefaults (); + R_SetViewSize (screenblocks); +} + +CCMD(reset2saved) +{ + GameConfig->DoGlobalSetup (); + GameConfig->DoGameSetup (GameNames[gameinfo.gametype]); + R_SetViewSize (screenblocks); +} diff --git a/src/menu/menu.h b/src/menu/menu.h new file mode 100644 index 000000000..b1073a041 --- /dev/null +++ b/src/menu/menu.h @@ -0,0 +1,652 @@ +#ifndef __M_MENU_MENU_H__ +#define __M_MENU_MENU_H__ + + + + +#include "dobject.h" +#include "lists.h" +#include "d_player.h" +#include "r_translate.h" +#include "c_cvars.h" +#include "v_font.h" +#include "version.h" +#include "textures/textures.h" + +EXTERN_CVAR(Float, snd_menuvolume) +EXTERN_CVAR(Int, m_use_mouse); + + +struct event_t; +class FTexture; +class FFont; +enum EColorRange; +class FPlayerClass; +class FKeyBindings; + +enum EMenuKey +{ + MKEY_Up, + MKEY_Down, + MKEY_Left, + MKEY_Right, + MKEY_PageUp, + MKEY_PageDown, + //----------------- Keys past here do not repeat. + MKEY_Enter, + MKEY_Back, // Back to previous menu + MKEY_Clear, // Clear keybinding/flip player sprite preview + NUM_MKEYS, + + // These are not buttons but events sent from other menus + + MKEY_Input, // Sent when input is confirmed + MKEY_Abort, // Input aborted + MKEY_MBYes, + MKEY_MBNo, +}; + + +struct FGameStartup +{ + const char *PlayerClass; + int Episode; + int Skill; +}; + +extern FGameStartup GameStartupInfo; + +struct FSaveGameNode : public Node +{ + char Title[SAVESTRINGSIZE]; + FString Filename; + bool bOldVersion; + bool bMissingWads; + bool bNoDelete; + + FSaveGameNode() { bNoDelete = false; } +}; + + + +//============================================================================= +// +// menu descriptor. This is created from the menu definition lump +// Items must be inserted in the order they are cycled through with the cursor +// +//============================================================================= + +enum EMenuDescriptorType +{ + MDESC_ListMenu, + MDESC_OptionsMenu, +}; + +struct FMenuDescriptor +{ + FName mMenuName; + FString mNetgameMessage; + int mType; + + virtual ~FMenuDescriptor() {} +}; + +class FListMenuItem; +class FOptionMenuItem; + +struct FListMenuDescriptor : public FMenuDescriptor +{ + TDeletingArray mItems; + int mSelectedItem; + int mSelectOfsX; + int mSelectOfsY; + FTextureID mSelector; + int mDisplayTop; + int mXpos, mYpos; + int mWLeft, mWRight; + int mLinespacing; // needs to be stored for dynamically created menus + int mAutoselect; // this can only be set by internal menu creation functions + FFont *mFont; + EColorRange mFontColor; + EColorRange mFontColor2; + const PClass *mClass; + FMenuDescriptor *mRedirect; // used to redirect overlong skill and episode menus to option menu based alternatives +}; + +struct FOptionMenuSettings +{ + EColorRange mTitleColor; + EColorRange mFontColor; + EColorRange mFontColorValue; + EColorRange mFontColorMore; + EColorRange mFontColorHeader; + EColorRange mFontColorHighlight; + EColorRange mFontColorSelection; + int mLinespacing; + int mLabelOffset; +}; + +struct FOptionMenuDescriptor : public FMenuDescriptor +{ + TDeletingArray mItems; + FString mTitle; + int mSelectedItem; + int mDrawTop; + int mScrollTop; + int mScrollPos; + int mIndent; + int mPosition; + bool mDontDim; + const PClass *mClass; + + void CalcIndent(); + FOptionMenuItem *GetItem(FName name); + +}; + + +typedef TMap MenuDescriptorList; + +extern FOptionMenuSettings OptionSettings; +extern MenuDescriptorList MenuDescriptors; + +#define CURSORSPACE (14 * CleanXfac_1) + +//============================================================================= +// +// +// +//============================================================================= + +struct FMenuRect +{ + int x, y; + int width, height; + + void set(int _x, int _y, int _w, int _h) + { + x = _x; + y = _y; + width = _w; + height = _h; + } + + bool inside(int _x, int _y) + { + return _x >= x && _x < x+width && _y >= y && _y < y+height; + } + +}; + + +class DMenu : public DObject +{ + DECLARE_CLASS (DMenu, DObject) + HAS_OBJECT_POINTERS + +protected: + bool mMouseCapture; + bool mBackbuttonSelected; + +public: + enum + { + MOUSE_Click, + MOUSE_Move, + MOUSE_Release + }; + + enum + { + BACKBUTTON_TIME = 4*TICRATE + }; + + static DMenu *CurrentMenu; + static int MenuTime; + + TObjPtr mParentMenu; + + DMenu(DMenu *parent = NULL); + virtual bool Responder (event_t *ev); + virtual bool MenuEvent (int mkey, bool fromcontroller); + virtual void Ticker (); + virtual void Drawer (); + virtual bool DimAllowed (); + virtual bool TranslateKeyboardEvents(); + virtual void Close(); + virtual bool MouseEvent(int type, int x, int y); + bool MouseEventBack(int type, int x, int y); + void SetCapture(); + void ReleaseCapture(); + bool HasCapture() + { + return mMouseCapture; + } +}; + +//============================================================================= +// +// base class for menu items +// +//============================================================================= + +class FListMenuItem +{ +protected: + int mXpos, mYpos; + FName mAction; + +public: + bool mEnabled; + + FListMenuItem(int xpos = 0, int ypos = 0, FName action = NAME_None) + { + mXpos = xpos; + mYpos = ypos; + mAction = action; + mEnabled = true; + } + + virtual ~FListMenuItem(); + + 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); + void DrawSelector(int xofs, int yofs, FTextureID tex); + void OffsetPositionY(int ydelta) { mYpos += ydelta; } + int GetY() { return mYpos; } +}; + +class FListMenuItemStaticPatch : public FListMenuItem +{ +protected: + FTextureID mTexture; + bool mCentered; + +public: + FListMenuItemStaticPatch(int x, int y, FTextureID patch, bool centered); + void Drawer(bool selected); +}; + +class FListMenuItemStaticText : public FListMenuItem +{ +protected: + const char *mText; + FFont *mFont; + EColorRange mColor; + bool mCentered; + +public: + FListMenuItemStaticText(int x, int y, const char *text, FFont *font, EColorRange color, bool centered); + ~FListMenuItemStaticText(); + void Drawer(bool selected); +}; + +//============================================================================= +// +// the player sprite window +// +//============================================================================= + +class FListMenuItemPlayerDisplay : public FListMenuItem +{ + FListMenuDescriptor *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(); + +public: + + enum + { + PDF_ROTATION = 0x10001, + PDF_SKIN = 0x10002, + PDF_CLASS = 0x10003, + PDF_MODE = 0x10004, + PDF_TRANSLATE = 0x10005, + }; + + FListMenuItemPlayerDisplay(FListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action); + ~FListMenuItemPlayerDisplay(); + virtual void Ticker(); + virtual void Drawer(bool selected); + bool SetValue(int i, int value); +}; + + +//============================================================================= +// +// selectable items +// +//============================================================================= + +class FListMenuItemSelectable : public FListMenuItem +{ +protected: + int mHotkey; + int mHeight; + int mParam; + +public: + FListMenuItemSelectable(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 FListMenuItemText : public FListMenuItemSelectable +{ + const char *mText; + FFont *mFont; + EColorRange mColor; +public: + FListMenuItemText(int x, int y, int height, int hotkey, const char *text, FFont *font, EColorRange color, FName child, int param = 0); + ~FListMenuItemText(); + void Drawer(bool selected); +}; + +class FListMenuItemPatch : public FListMenuItemSelectable +{ + FTextureID mTexture; +public: + FListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param = 0); + void Drawer(bool selected); +}; + +//============================================================================= +// +// items for the player menu +// +//============================================================================= + +class FPlayerNameBox : public FListMenuItemSelectable +{ + const char *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); + +public: + + FPlayerNameBox(int x, int y, int height, int frameofs, const char *text, FFont *font, EColorRange color, FName action); + ~FPlayerNameBox(); + 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 FValueTextItem : public FListMenuItemSelectable +{ + TArray mSelections; + const char *mText; + int mSelection; + FFont *mFont; + EColorRange mFontColor; + EColorRange mFontColor2; + +public: + + FValueTextItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, EColorRange valuecolor, FName action, FName values); + ~FValueTextItem(); + 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 FSliderItem : public FListMenuItemSelectable +{ + const char *mText; + FFont *mFont; + EColorRange mFontColor; + int mMinrange, mMaxrange; + int mStep; + int mSelection; + + void DrawSlider (int x, int y); + +public: + + FSliderItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, FName action, int min, int max, int step); + ~FSliderItem(); + 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 FListMenuDescriptor +// +//============================================================================= + +class DListMenu : public DMenu +{ + DECLARE_CLASS(DListMenu, DMenu) + +protected: + FListMenuDescriptor *mDesc; + FListMenuItem *mFocusControl; + +public: + DListMenu(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); + virtual void Init(DMenu *parent = NULL, FListMenuDescriptor *desc = NULL); + FListMenuItem *GetItem(FName name); + bool Responder (event_t *ev); + bool MenuEvent (int mkey, bool fromcontroller); + bool MouseEvent(int type, int x, int y); + void Ticker (); + void Drawer (); + void SetFocus(FListMenuItem *fc) + { + mFocusControl = fc; + } + bool CheckFocus(FListMenuItem *fc) + { + return mFocusControl == fc; + } + void ReleaseFocus() + { + mFocusControl = NULL; + } +}; + + +//============================================================================= +// +// base class for menu items +// +//============================================================================= + +class FOptionMenuItem : public FListMenuItem +{ +protected: + char *mLabel; + bool mCentered; + + void drawLabel(int indent, int y, EColorRange color, bool grayed = false); +public: + + FOptionMenuItem(const char *text, FName action = NAME_None, bool center = false) + : FListMenuItem(0, 0, action) + { + mLabel = copystring(text); + mCentered = center; + } + + ~FOptionMenuItem(); + virtual bool CheckCoordinate(FOptionMenuDescriptor *desc, int x, int y); + virtual int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected); + virtual bool Selectable(); + virtual int GetIndent(); + virtual bool MouseEvent(int type, int x, int y); +}; + +//============================================================================= +// +// +// +//============================================================================= +struct FOptionValues +{ + struct Pair + { + double Value; + FString TextValue; + FString Text; + }; + + TArray mValues; +}; + +typedef TMap< FName, FOptionValues* > FOptionMap; + +extern FOptionMap OptionValues; + + +//============================================================================= +// +// Option menu class runs a menu described by a FOptionMenuDescriptor +// +//============================================================================= + +class DOptionMenu : public DMenu +{ + DECLARE_CLASS(DOptionMenu, DMenu) + + bool CanScrollUp; + bool CanScrollDown; + int VisBottom; + FOptionMenuItem *mFocusControl; + +protected: + FOptionMenuDescriptor *mDesc; + +public: + FOptionMenuItem *GetItem(FName name); + DOptionMenu(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL); + virtual void Init(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL); + bool Responder (event_t *ev); + bool MenuEvent (int mkey, bool fromcontroller); + bool MouseEvent(int type, int x, int y); + void Ticker (); + void Drawer (); + const FOptionMenuDescriptor *GetDescriptor() const { return mDesc; } + void SetFocus(FOptionMenuItem *fc) + { + mFocusControl = fc; + } + bool CheckFocus(FOptionMenuItem *fc) + { + return mFocusControl == fc; + } + void ReleaseFocus() + { + mFocusControl = NULL; + } +}; + + +//============================================================================= +// +// Input some text +// +//============================================================================= + +class DTextEnterMenu : public DMenu +{ + DECLARE_ABSTRACT_CLASS(DTextEnterMenu, DMenu) + + char *mEnterString; + unsigned int mEnterSize; + unsigned int mEnterPos; + int mSizeMode; // 1: size is length in chars. 2: also check string width + bool mInputGridOkay; + + int InputGridX; + int InputGridY; + +public: + + DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid); + + void Drawer (); + bool MenuEvent (int mkey, bool fromcontroller); + bool Responder(event_t *ev); + bool TranslateKeyboardEvents(); + bool MouseEvent(int type, int x, int y); + +}; + + + + +struct event_t; +bool M_Responder (event_t *ev); +void M_Ticker (void); +void M_Drawer (void); +void M_Init (void); +void M_CreateMenus(); +void M_ActivateMenu(DMenu *menu); +void M_ClearMenus (); +void M_ParseMenuDefs(); +void M_StartupSkillMenu(FGameStartup *gs); +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 (); +void M_InitVideoModesMenu (); + + +#endif \ No newline at end of file diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp new file mode 100644 index 000000000..31fe0fd3b --- /dev/null +++ b/src/menu/menudef.cpp @@ -0,0 +1,1389 @@ +/* +** menudef.cpp +** MENUDEF parser amd menu generation code +** +**--------------------------------------------------------------------------- +** 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 "i_system.h" +#include "c_bind.h" +#include "v_palette.h" +#include "d_event.h" +#include "d_gui.h" +#include "i_music.h" +#include "m_joy.h" +#include "gi.h" + +#include "optionmenuitems.h" + +MenuDescriptorList MenuDescriptors; +static FListMenuDescriptor DefaultListMenuSettings; // contains common settings for all list menus +static FOptionMenuDescriptor DefaultOptionMenuSettings; // contains common settings for all Option menus +FOptionMenuSettings OptionSettings; +FOptionMap OptionValues; + +static void DeinitMenus() +{ + { + MenuDescriptorList::Iterator it(MenuDescriptors); + + MenuDescriptorList::Pair *pair; + + while (it.NextPair(pair)) + { + delete pair->Value; + pair->Value = NULL; + } + } + + { + FOptionMap::Iterator it(OptionValues); + + FOptionMap::Pair *pair; + + while (it.NextPair(pair)) + { + delete pair->Value; + pair->Value = NULL; + } + } + DMenu::CurrentMenu = NULL; +} + +//============================================================================= +// +// +// +//============================================================================= + +static void SkipSubBlock(FScanner &sc) +{ + sc.MustGetStringName("{"); + int depth = 1; + while (depth > 0) + { + sc.MustGetString(); + if (sc.Compare("{")) depth++; + if (sc.Compare("}")) depth--; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static bool CheckSkipGameBlock(FScanner &sc) +{ + int filter = 0; + sc.MustGetStringName("("); + do + { + sc.MustGetString(); + if (sc.Compare("Doom")) filter |= GAME_Doom; + if (sc.Compare("Heretic")) filter |= GAME_Heretic; + if (sc.Compare("Hexen")) filter |= GAME_Hexen; + if (sc.Compare("Strife")) filter |= GAME_Strife; + if (sc.Compare("Chex")) filter |= GAME_Chex; + } + while (sc.CheckString(",")); + sc.MustGetStringName(")"); + if (!(gameinfo.gametype & filter)) + { + SkipSubBlock(sc); + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +static bool CheckSkipOptionBlock(FScanner &sc) +{ + bool filter = false; + sc.MustGetStringName("("); + do + { + sc.MustGetString(); + if (sc.Compare("ReadThis")) filter |= gameinfo.drawreadthis; + else if (sc.Compare("Windows")) + { + #ifdef _WIN32 + filter = true; + #endif + } + else if (sc.Compare("unix")) + { + #ifdef unix + filter = true; + #endif + } + else if (sc.Compare("Mac")) + { + #ifdef __APPLE__ + filter = true; + #endif + } + } + while (sc.CheckString(",")); + sc.MustGetStringName(")"); + if (!filter) + { + SkipSubBlock(sc); + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) +{ + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("ifgame")) + { + if (!CheckSkipGameBlock(sc)) + { + // recursively parse sub-block + ParseListMenuBody(sc, desc); + } + } + else if (sc.Compare("ifoption")) + { + if (!CheckSkipOptionBlock(sc)) + { + // recursively parse sub-block + ParseListMenuBody(sc, desc); + } + } + else if (sc.Compare("Class")) + { + sc.MustGetString(); + const PClass *cls = PClass::FindClass(sc.String); + if (cls == NULL || !cls->IsDescendantOf(RUNTIME_CLASS(DListMenu))) + { + sc.ScriptError("Unknown menu class '%s'", sc.String); + } + desc->mClass = cls; + } + else if (sc.Compare("Selector")) + { + sc.MustGetString(); + desc->mSelector = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + sc.MustGetStringName(","); + sc.MustGetNumber(); + desc->mSelectOfsX = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + desc->mSelectOfsY = sc.Number; + } + else if (sc.Compare("Linespacing")) + { + sc.MustGetNumber(); + desc->mLinespacing = sc.Number; + } + else if (sc.Compare("Position")) + { + sc.MustGetNumber(); + desc->mXpos = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + desc->mYpos = sc.Number; + } + else if (sc.Compare("MouseWindow")) + { + sc.MustGetNumber(); + desc->mWLeft = sc.Number; + sc.MustGetStringName(","); + 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 = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + + FListMenuItem *it = new FListMenuItemStaticPatch(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(); + FListMenuItem *it = new FListMenuItemStaticText(x, y, sc.String, desc->mFont, desc->mFontColor, centered); + desc->mItems.Push(it); + } + else if (sc.Compare("PatchItem")) + { + sc.MustGetString(); + FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + 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; + } + + FListMenuItem *it = new FListMenuItemPatch(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; + } + + FListMenuItem *it = new FListMenuItemText(desc->mXpos, desc->mYpos, desc->mLinespacing, hotkey, text, desc->mFont, desc->mFontColor, 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(); + FFont *newfont = V_GetFont(sc.String); + if (newfont != NULL) desc->mFont = newfont; + if (sc.CheckString(",")) + { + sc.MustGetString(); + desc->mFontColor2 = desc->mFontColor = V_FindFontColor((FName)sc.String); + if (sc.CheckString(",")) + { + sc.MustGetString(); + desc->mFontColor2 = V_FindFontColor((FName)sc.String); + } + } + else + { + desc->mFontColor = OptionSettings.mFontColor; + desc->mFontColor2 = OptionSettings.mFontColorValue; + } + } + else if (sc.Compare("NetgameMessage")) + { + 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(NULL, sc.String); + sc.MustGetStringName(","); + sc.MustGetString(); + PalEntry c2 = V_GetColor(NULL, sc.String); + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + noportrait = !!sc.Number; + if (sc.CheckString(",")) + { + sc.MustGetString(); + action = sc.String; + } + } + FListMenuItemPlayerDisplay *it = new FListMenuItemPlayerDisplay(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(); + FListMenuItem *it = new FPlayerNameBox(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; + } + FListMenuItem *it = new FValueTextItem(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; + FListMenuItem *it = new FSliderItem(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); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseListMenu(FScanner &sc) +{ + sc.MustGetString(); + + FListMenuDescriptor *desc = new FListMenuDescriptor; + desc->mType = MDESC_ListMenu; + desc->mMenuName = sc.String; + desc->mSelectedItem = -1; + desc->mAutoselect = -1; + desc->mSelectOfsX = DefaultListMenuSettings.mSelectOfsX; + desc->mSelectOfsY = DefaultListMenuSettings.mSelectOfsY; + desc->mSelector = DefaultListMenuSettings.mSelector; + desc->mDisplayTop = DefaultListMenuSettings.mDisplayTop; + desc->mXpos = DefaultListMenuSettings.mXpos; + desc->mYpos = DefaultListMenuSettings.mYpos; + desc->mLinespacing = DefaultListMenuSettings.mLinespacing; + desc->mNetgameMessage = DefaultListMenuSettings.mNetgameMessage; + desc->mFont = DefaultListMenuSettings.mFont; + desc->mFontColor = DefaultListMenuSettings.mFontColor; + desc->mFontColor2 = DefaultListMenuSettings.mFontColor2; + desc->mClass = NULL; + desc->mRedirect = NULL; + desc->mWLeft = 0; + desc->mWRight = 0; + + FMenuDescriptor **pOld = MenuDescriptors.CheckKey(desc->mMenuName); + if (pOld != NULL && *pOld != NULL) delete *pOld; + MenuDescriptors[desc->mMenuName] = desc; + + ParseListMenuBody(sc, desc); +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseOptionValue(FScanner &sc) +{ + FName optname; + + FOptionValues *val = new FOptionValues; + sc.MustGetString(); + optname = sc.String; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + FOptionValues::Pair &pair = val->mValues[val->mValues.Reserve(1)]; + sc.MustGetFloat(); + pair.Value = sc.Float; + sc.MustGetStringName(","); + sc.MustGetString(); + pair.Text = strbin1(sc.String); + } + FOptionValues **pOld = OptionValues.CheckKey(optname); + if (pOld != NULL && *pOld != NULL) delete *pOld; + OptionValues[optname] = val; +} + + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseOptionString(FScanner &sc) +{ + FName optname; + + FOptionValues *val = new FOptionValues; + sc.MustGetString(); + optname = sc.String; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + FOptionValues::Pair &pair = val->mValues[val->mValues.Reserve(1)]; + sc.MustGetString(); + pair.Value = DBL_MAX; + pair.TextValue = sc.String; + sc.MustGetStringName(","); + sc.MustGetString(); + pair.Text = strbin1(sc.String); + } + FOptionValues **pOld = OptionValues.CheckKey(optname); + if (pOld != NULL && *pOld != NULL) delete *pOld; + OptionValues[optname] = val; +} + + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseOptionSettings(FScanner &sc) +{ + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("ifgame")) + { + if (!CheckSkipGameBlock(sc)) + { + // recursively parse sub-block + ParseOptionSettings(sc); + } + } + else if (sc.Compare("Linespacing")) + { + sc.MustGetNumber(); + OptionSettings.mLinespacing = sc.Number; + } + else if (sc.Compare("LabelOffset")) + { + sc.MustGetNumber(); + OptionSettings.mLabelOffset = sc.Number; + } + else + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc) +{ + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("ifgame")) + { + if (!CheckSkipGameBlock(sc)) + { + // recursively parse sub-block + ParseOptionMenuBody(sc, desc); + } + } + else if (sc.Compare("ifoption")) + { + if (!CheckSkipOptionBlock(sc)) + { + // recursively parse sub-block + ParseOptionMenuBody(sc, desc); + } + } + else if (sc.Compare("Class")) + { + sc.MustGetString(); + const PClass *cls = PClass::FindClass(sc.String); + if (cls == NULL || !cls->IsDescendantOf(RUNTIME_CLASS(DOptionMenu))) + { + sc.ScriptError("Unknown menu class '%s'", sc.String); + } + desc->mClass = cls; + } + else if (sc.Compare("Title")) + { + sc.MustGetString(); + desc->mTitle = sc.String; + } + else if (sc.Compare("Position")) + { + sc.MustGetNumber(); + desc->mPosition = sc.Number; + } + else if (sc.Compare("DefaultSelection")) + { + sc.MustGetNumber(); + desc->mSelectedItem = sc.Number; + } + else if (sc.Compare("ScrollTop")) + { + sc.MustGetNumber(); + desc->mScrollTop = sc.Number; + } + else if (sc.Compare("Indent")) + { + sc.MustGetNumber(); + desc->mIndent = sc.Number; + } + else if (sc.Compare("Submenu")) + { + sc.MustGetString(); + FString label = sc.String; + sc.MustGetStringName(","); + sc.MustGetString(); + FOptionMenuItem *it = new FOptionMenuItemSubmenu(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; + } + } + FOptionMenuItem *it = new FOptionMenuItemOption(label, cvar, values, check, center); + desc->mItems.Push(it); + } + else if (sc.Compare("Command")) + { + sc.MustGetString(); + FString label = sc.String; + sc.MustGetStringName(","); + sc.MustGetString(); + FOptionMenuItem *it = new FOptionMenuItemCommand(label, sc.String); + desc->mItems.Push(it); + } + else if (sc.Compare("SafeCommand")) + { + sc.MustGetString(); + FString label = sc.String; + sc.MustGetStringName(","); + sc.MustGetString(); + FOptionMenuItem *it = new FOptionMenuItemSafeCommand(label, sc.String); + 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(); + FOptionMenuItem *it = new FOptionMenuItemControl(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(); + FOptionMenuItem *it = new FOptionMenuItemColorPicker(label, sc.String); + desc->mItems.Push(it); + } + else if (sc.Compare("StaticText")) + { + sc.MustGetString(); + FString label = sc.String; + bool cr = false; + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + cr = !!sc.Number; + } + FOptionMenuItem *it = new FOptionMenuItemStaticText(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; + bool cr = false; + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + cr = !!sc.Number; + } + FOptionMenuItem *it = new FOptionMenuItemStaticTextSwitchable(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; + bool showvalue = true; + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + showvalue = !!sc.Number; + } + FOptionMenuItem *it = new FOptionMenuSliderCVar(text, action, min, max, step, showvalue? 1:-1); + desc->mItems.Push(it); + } + else if (sc.Compare("screenresolution")) + { + sc.MustGetString(); + FOptionMenuItem *it = new FOptionMenuScreenResolutionLine(sc.String); + desc->mItems.Push(it); + } + else + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void ParseOptionMenu(FScanner &sc) +{ + sc.MustGetString(); + + FOptionMenuDescriptor *desc = new FOptionMenuDescriptor; + desc->mType = MDESC_OptionsMenu; + desc->mMenuName = sc.String; + desc->mSelectedItem = -1; + desc->mScrollPos = 0; + desc->mClass = NULL; + desc->mPosition = DefaultOptionMenuSettings.mPosition; + desc->mScrollTop = DefaultOptionMenuSettings.mScrollTop; + desc->mIndent = DefaultOptionMenuSettings.mIndent; + desc->mDontDim = DefaultOptionMenuSettings.mDontDim; + + FMenuDescriptor **pOld = MenuDescriptors.CheckKey(desc->mMenuName); + if (pOld != NULL && *pOld != NULL) delete *pOld; + MenuDescriptors[desc->mMenuName] = desc; + + ParseOptionMenuBody(sc, desc); + + if (desc->mIndent == 0) desc->CalcIndent(); +} + + +//============================================================================= +// +// +// +//============================================================================= + +void M_ParseMenuDefs() +{ + int lump, lastlump = 0; + + OptionSettings.mTitleColor = V_FindFontColor(gameinfo.mTitleColor); + OptionSettings.mFontColor = V_FindFontColor(gameinfo.mFontColor); + OptionSettings.mFontColorValue = V_FindFontColor(gameinfo.mFontColorValue); + OptionSettings.mFontColorMore = V_FindFontColor(gameinfo.mFontColorMore); + OptionSettings.mFontColorHeader = V_FindFontColor(gameinfo.mFontColorHeader); + OptionSettings.mFontColorHighlight = V_FindFontColor(gameinfo.mFontColorHighlight); + OptionSettings.mFontColorSelection = V_FindFontColor(gameinfo.mFontColorSelection); + + atterm( DeinitMenus); + while ((lump = Wads.FindLump ("MENUDEF", &lastlump)) != -1) + { + FScanner sc(lump); + + sc.SetCMode(true); + while (sc.GetString()) + { + if (sc.Compare("LISTMENU")) + { + ParseListMenu(sc); + } + else if (sc.Compare("DEFAULTLISTMENU")) + { + ParseListMenuBody(sc, &DefaultListMenuSettings); + } + else if (sc.Compare("OPTIONVALUE")) + { + ParseOptionValue(sc); + } + else if (sc.Compare("OPTIONSTRING")) + { + ParseOptionString(sc); + } + else if (sc.Compare("OPTIONMENUSETTINGS")) + { + ParseOptionSettings(sc); + } + else if (sc.Compare("OPTIONMENU")) + { + ParseOptionMenu(sc); + } + else if (sc.Compare("DEFAULTOPTIONMENU")) + { + ParseOptionMenuBody(sc, &DefaultOptionMenuSettings); + } + else + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } + } + } +} + + +//============================================================================= +// +// Creates the episode menu +// Falls back on an option menu if there's not enough screen space to show all episodes +// +//============================================================================= + +static void BuildEpisodeMenu() +{ + // Build episode menu + bool success = false; + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Episodemenu); + if (desc != NULL) + { + if ((*desc)->mType == MDESC_ListMenu) + { + FListMenuDescriptor *ld = static_cast(*desc); + int posy = ld->mYpos; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + AllEpisodes.Size() * ld->mLinespacing - topy; + + if (totalheight < 190 || AllEpisodes.Size() == 1) + { + int newtop = (200 - totalheight + topy) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + posy -= topdelta; + } + + ld->mSelectedItem = ld->mItems.Size(); + for(unsigned i = 0; i < AllEpisodes.Size(); i++) + { + FListMenuItem *it; + if (AllEpisodes[i].mPicName.IsNotEmpty()) + { + FTextureID tex = TexMan.CheckForTexture(AllEpisodes[i].mPicName, FTexture::TEX_MiscPatch); + it = new FListMenuItemPatch(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, + tex, NAME_Skillmenu, i); + } + else + { + it = new FListMenuItemText(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, + AllEpisodes[i].mEpisodeName, ld->mFont, ld->mFontColor, NAME_Skillmenu, i); + } + ld->mItems.Push(it); + posy += ld->mLinespacing; + } + if (AllEpisodes.Size() == 1) + { + ld->mAutoselect = ld->mSelectedItem; + } + success = true; + } + } + } + if (!success) + { + // Couldn't create the episode menu, either because there's too many episodes or some error occured + // Create an option menu for episode selection instead. + FOptionMenuDescriptor *od = new FOptionMenuDescriptor; + if (desc != NULL) delete *desc; + MenuDescriptors[NAME_Episodemenu] = od; + od->mType = MDESC_OptionsMenu; + od->mMenuName = NAME_Episodemenu; + od->mTitle = "$MNU_EPISODE"; + od->mSelectedItem = 0; + od->mScrollPos = 0; + od->mClass = NULL; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + for(unsigned i = 0; i < AllEpisodes.Size(); i++) + { + FOptionMenuItemSubmenu *it = new FOptionMenuItemSubmenu(AllEpisodes[i].mEpisodeName, "Skillmenu", i); + od->mItems.Push(it); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void BuildPlayerclassMenu() +{ + bool success = false; + + // Build player class menu + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Playerclassmenu); + if (desc != NULL) + { + if ((*desc)->mType == MDESC_ListMenu) + { + FListMenuDescriptor *ld = static_cast(*desc); + // add player display + ld->mSelectedItem = ld->mItems.Size(); + + int posy = ld->mYpos; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // Count the number of items this menu will show + int numclassitems = 0; + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + if (pname != NULL) + { + numclassitems++; + } + } + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + (numclassitems+1) * ld->mLinespacing - topy; + + if (totalheight <= 190 || numclassitems == 1) + { + int newtop = (200 - totalheight + topy) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + posy -= topdelta; + } + + int n = 0; + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + if (pname != NULL) + { + FListMenuItemText *it = new FListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + pname, ld->mFont,ld->mFontColor, NAME_Episodemenu, i); + ld->mItems.Push(it); + ld->mYpos += ld->mLinespacing; + n++; + } + } + } + if (n > 1) + { + FListMenuItemText *it = new FListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, 'r', + "$MNU_RANDOM", ld->mFont,ld->mFontColor, NAME_Episodemenu, -1); + ld->mItems.Push(it); + } + if (n == 0) + { + const char *pname = PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName); + if (pname != NULL) + { + FListMenuItemText *it = new FListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + pname, ld->mFont,ld->mFontColor, NAME_Episodemenu, 0); + ld->mItems.Push(it); + } + } + if (n < 2) + { + ld->mAutoselect = ld->mSelectedItem; + } + success = true; + } + } + } + if (!success) + { + // Couldn't create the playerclass menu, either because there's too many episodes or some error occured + // Create an option menu for class selection instead. + FOptionMenuDescriptor *od = new FOptionMenuDescriptor; + if (desc != NULL) delete *desc; + MenuDescriptors[NAME_Playerclassmenu] = od; + od->mType = MDESC_OptionsMenu; + od->mMenuName = NAME_Playerclassmenu; + od->mTitle = "$MNU_CHOOSECLASS"; + od->mSelectedItem = 0; + od->mScrollPos = 0; + od->mClass = NULL; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + od->mNetgameMessage = "$NETGAME"; + + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + if (pname != NULL) + { + FOptionMenuItemSubmenu *it = new FOptionMenuItemSubmenu(pname, "Episodemenu", i); + od->mItems.Push(it); + } + } + } + FOptionMenuItemSubmenu *it = new FOptionMenuItemSubmenu("Random", "Episodemenu", -1); + od->mItems.Push(it); + } +} + +//============================================================================= +// +// Reads any XHAIRS lumps for the names of crosshairs and +// adds them to the display options menu. +// +//============================================================================= + +static void InitCrosshairsList() +{ + int lastlump, lump; + + lastlump = 0; + + FOptionValues **opt = OptionValues.CheckKey(NAME_Crosshairs); + if (opt == NULL) + { + return; // no crosshair value list present. No need to go on. + } + + FOptionValues::Pair *pair = &(*opt)->mValues[(*opt)->mValues.Reserve(1)]; + pair->Value = 0; + pair->Text = "None"; + + while ((lump = Wads.FindLump("XHAIRS", &lastlump)) != -1) + { + FScanner sc(lump); + while (sc.GetNumber()) + { + FOptionValues::Pair value; + value.Value = sc.Number; + sc.MustGetString(); + value.Text = sc.String; + if (value.Value != 0) + { // Check if it already exists. If not, add it. + unsigned int i; + + for (i = 1; i < (*opt)->mValues.Size(); ++i) + { + if ((*opt)->mValues[i].Value == value.Value) + { + break; + } + } + if (i < (*opt)->mValues.Size()) + { + (*opt)->mValues[i].Text = value.Text; + } + else + { + (*opt)->mValues.Push(value); + } + } + } + } +} + +//============================================================================= +// +// With the current workings of the menu system this cannot be done any longer +// from within the respective CCMDs. +// +//============================================================================= + +static void InitKeySections() +{ + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_CustomizeControls); + if (desc != NULL) + { + if ((*desc)->mType == MDESC_OptionsMenu) + { + FOptionMenuDescriptor *menu = static_cast(*desc); + + for (unsigned i = 0; i < KeySections.Size(); i++) + { + FKeySection *sect = &KeySections[i]; + FOptionMenuItem *item = new FOptionMenuItemStaticText(" ", false); + menu->mItems.Push(item); + item = new FOptionMenuItemStaticText(sect->mTitle, true); + menu->mItems.Push(item); + for (unsigned j = 0; j < sect->mActions.Size(); j++) + { + FKeyAction *act = §->mActions[j]; + item = new FOptionMenuItemControl(act->mTitle, act->mAction, &Bindings); + menu->mItems.Push(item); + } + } + } + } +} + +//============================================================================= +// +// Special menus will be created once all engine data is loaded +// +//============================================================================= + +void M_CreateMenus() +{ + BuildEpisodeMenu(); + BuildPlayerclassMenu(); + InitCrosshairsList(); + InitKeySections(); + UpdateJoystickMenu(NULL); + + FOptionValues **opt = OptionValues.CheckKey(NAME_Mididevices); + if (opt != NULL) + { + I_BuildMIDIMenuList(*opt); + } +} + +//============================================================================= +// +// THe skill menu must be refeshed each time it starts up +// +//============================================================================= + +void M_StartupSkillMenu(FGameStartup *gs) +{ + static bool done = false; + bool success = false; + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu); + if (desc != NULL) + { + if ((*desc)->mType == MDESC_ListMenu) + { + FListMenuDescriptor *ld = static_cast(*desc); + int x = ld->mXpos; + int y = ld->mYpos; + if (gameinfo.gametype == GAME_Hexen) + { + // THere really needs to be a better way to do this... :( + if (gs->PlayerClass != NULL) + { + if (!stricmp(gs->PlayerClass, "fighter")) x = 120; + else if (!stricmp(gs->PlayerClass, "cleric")) x = 116; + else if (!stricmp(gs->PlayerClass, "mage")) x = 112; + } + } + + // Delete previous contents + for(unsigned i=0; imItems.Size(); i++) + { + FName n = ld->mItems[i]->GetAction(NULL); + if (n == NAME_Startgame || n == NAME_StartgameConfirm) + { + for(unsigned j=i; jmItems.Size(); j++) + { + delete ld->mItems[j]; + } + ld->mItems.Resize(i); + break; + } + } + + if (!done) + { + done = true; + int defskill = DefaultSkill; + if ((unsigned int)defskill >= AllSkills.Size()) + { + defskill = (AllSkills.Size() - 1) / 2; + } + ld->mSelectedItem = ld->mItems.Size() + defskill; + + int posy = y; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + AllSkills.Size() * ld->mLinespacing - topy; + + if (totalheight < 190 || AllSkills.Size() == 1) + { + int newtop = (200 - totalheight + topy) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + y = ld->mYpos = posy - topdelta; + } + } + else + { + // too large + delete ld; + desc = NULL; + done = false; + goto fail; + } + } + + unsigned firstitem = ld->mItems.Size(); + for(unsigned int i = 0; i < AllSkills.Size(); i++) + { + FSkillInfo &skill = AllSkills[i]; + FListMenuItem *li; + // Using a different name for skills that must be confirmed makes handling this easier. + FName action = skill.MustConfirm? NAME_StartgameConfirm : NAME_Startgame; + + FString *pItemText = NULL; + if (gs->PlayerClass != NULL) + { + pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); + } + + if (skill.PicName.Len() != 0 && pItemText == NULL) + { + FTextureID tex = TexMan.CheckForTexture(skill.PicName, FTexture::TEX_MiscPatch); + li = new FListMenuItemPatch(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 FListMenuItemText(x, y, ld->mLinespacing, skill.Shortcut, + pItemText? *pItemText : skill.MenuName, ld->mFont, color, action, i); + } + ld->mItems.Push(li); + y += ld->mLinespacing; + } + if (AllEpisodes[gs->Episode].mNoSkill || AllSkills.Size() == 1) + { + ld->mAutoselect = MIN(2u, AllEpisodes.Size()-1); + } + success = true; + } + } + if (success) return; +fail: + // Option menu fallback for overlong skill lists + FOptionMenuDescriptor *od; + if (desc == NULL) + { + od = new FOptionMenuDescriptor; + if (desc != NULL) delete *desc; + MenuDescriptors[NAME_Skillmenu] = od; + od->mType = MDESC_OptionsMenu; + od->mMenuName = NAME_Skillmenu; + od->mTitle = "$MNU_CHOOSESKILL"; + od->mSelectedItem = 0; + od->mScrollPos = 0; + od->mClass = NULL; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + } + else + { + od = static_cast(*desc); + for(unsigned i=0;imItems.Size(); i++) + { + delete od->mItems[i]; + } + od->mItems.Clear(); + } + for(unsigned int i = 0; i < AllSkills.Size(); i++) + { + FSkillInfo &skill = AllSkills[i]; + FOptionMenuItem *li; + // Using a different name for skills that must be confirmed makes handling this easier. + const char *action = skill.MustConfirm? "StartgameConfirm" : "Startgame"; + + FString *pItemText = NULL; + if (gs->PlayerClass != NULL) + { + pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); + } + li = new FOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, i); + od->mItems.Push(li); + if (!done) + { + done = true; + int defskill = DefaultSkill; + if ((unsigned int)defskill >= AllSkills.Size()) + { + defskill = (AllSkills.Size() - 1) / 2; + } + od->mSelectedItem = defskill; + } + } +} diff --git a/src/menu/menuinput.cpp b/src/menu/menuinput.cpp new file mode 100644 index 000000000..0f35b7e1f --- /dev/null +++ b/src/menu/menuinput.cpp @@ -0,0 +1,366 @@ +/* +** menuinput.cpp +** The string input code +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu/menu.h" +#include "v_video.h" +#include "c_cvars.h" +#include "d_event.h" +#include "d_gui.h" +#include "v_font.h" +#include "v_palette.h" + +IMPLEMENT_ABSTRACT_CLASS(DTextEnterMenu) + +#define INPUTGRID_WIDTH 13 +#define INPUTGRID_HEIGHT 5 + +// Heretic and Hexen do not, by default, come with glyphs for all of these +// characters. Oh well. Doom and Strife do. +static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] = + "ABCDEFGHIJKLM" + "NOPQRSTUVWXYZ" + "0123456789+-=" + ".,!?@'\":;[]()" + "<>^#$%&*/_ \b"; + + +CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +//============================================================================= +// +// +// +//============================================================================= + +DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid) +: DMenu(parent) +{ + mEnterString = textbuffer; + mEnterSize = maxlen; + mEnterPos = (unsigned)strlen(textbuffer); + mSizeMode = sizemode; + mInputGridOkay = showgrid || m_showinputgrid; + if (mEnterPos > 0) + { + InputGridX = INPUTGRID_WIDTH - 1; + InputGridY = INPUTGRID_HEIGHT - 1; + } + else + { + // If we are naming a new save, don't start the cursor on "end". + InputGridX = 0; + InputGridY = 0; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DTextEnterMenu::TranslateKeyboardEvents() +{ + return mInputGridOkay; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DTextEnterMenu::Responder(event_t *ev) +{ + if (ev->type == EV_GUI_Event) + { + // Save game and player name string input + if (ev->subtype == EV_GUI_Char) + { + mInputGridOkay = false; + if (mEnterPos < mEnterSize && + (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString[mEnterPos] = (char)ev->data1; + mEnterString[++mEnterPos] = 0; + } + return true; + } + char ch = (char)ev->data1; + if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b') + { + if (mEnterPos > 0) + { + mEnterPos--; + mEnterString[mEnterPos] = 0; + } + } + else if (ev->subtype == EV_GUI_KeyDown) + { + if (ch == GK_ESCAPE) + { + DMenu *parent = mParentMenu; + Close(); + parent->MenuEvent(MKEY_Abort, false); + return true; + } + else if (ch == '\r') + { + if (mEnterString[0]) + { + DMenu *parent = mParentMenu; + Close(); + parent->MenuEvent(MKEY_Input, false); + return true; + } + } + } + if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) + { + return true; + } + } + return Super::Responder(ev); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DTextEnterMenu::MouseEvent(int type, int x, int y) +{ + const int cell_width = 18 * CleanXfac; + const int cell_height = 12 * CleanYfac; + const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height; + const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2; + + if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y) + { + InputGridX = (x - screen_x) / cell_width; + InputGridY = (y - screen_y) / cell_height; + if (type == DMenu::MOUSE_Release) + { + if (MenuEvent(MKEY_Enter, true)) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + if (m_use_mouse == 2) InputGridX = InputGridY = -1; + return true; + } + } + } + else + { + InputGridX = InputGridY = -1; + } + return Super::MouseEvent(type, x, y); +} + + + +//============================================================================= +// +// +// +//============================================================================= + +bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller) +{ + if (key == MKEY_Back) + { + mParentMenu->MenuEvent(MKEY_Abort, false); + return Super::MenuEvent(key, fromcontroller); + } + if (fromcontroller) + { + mInputGridOkay = true; + } + + if (mInputGridOkay) + { + int ch; + + if (InputGridX == -1 || InputGridY == -1) + { + InputGridX = InputGridY = 0; + } + switch (key) + { + case MKEY_Down: + InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT; + return true; + + case MKEY_Up: + InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT; + return true; + + case MKEY_Right: + InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH; + return true; + + case MKEY_Left: + InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH; + return true; + + case MKEY_Clear: + if (mEnterPos > 0) + { + mEnterString[--mEnterPos] = 0; + } + return true; + + case MKEY_Enter: + assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT); + if (mInputGridOkay) + { + ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH]; + if (ch == 0) // end + { + if (mEnterString[0] != '\0') + { + DMenu *parent = mParentMenu; + Close(); + parent->MenuEvent(MKEY_Input, false); + return true; + } + } + else if (ch == '\b') // bs + { + if (mEnterPos > 0) + { + mEnterString[--mEnterPos] = 0; + } + } + else if (mEnterPos < mEnterSize && + (mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8)) + { + mEnterString[mEnterPos] = ch; + mEnterString[++mEnterPos] = 0; + } + } + return true; + + default: + break; // Keep GCC quiet + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DTextEnterMenu::Drawer () +{ + mParentMenu->Drawer(); + if (mInputGridOkay) + { + const int cell_width = 18 * CleanXfac; + const int cell_height = 12 * CleanYfac; + const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2; + + // Darken the background behind the character grid. + // Unless we frame it with a border, I think it looks better to extend the + // background across the full width of the screen. + screen->Dim(0, 0.8f, + 0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/, + screen->GetHeight() - INPUTGRID_HEIGHT * cell_height, + screen->GetWidth() /*13 * cell_width*/, + INPUTGRID_HEIGHT * cell_height); + + if (InputGridX >= 0 && InputGridY >= 0) + { + // Highlight the background behind the selected character. + screen->Dim(MAKERGB(255,248,220), 0.6f, + InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2, + InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(), + cell_width, cell_height); + } + + for (int y = 0; y < INPUTGRID_HEIGHT; ++y) + { + const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(); + for (int x = 0; x < INPUTGRID_WIDTH; ++x) + { + int width; + const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2; + const int ch = InputGridChars[y * INPUTGRID_WIDTH + x]; + FTexture *pic = SmallFont->GetChar(ch, &width); + EColorRange color; + FRemapTable *remap; + + // The highlighted character is yellow; the rest are dark gray. + color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY; + remap = SmallFont->GetColorTranslation(color); + + if (pic != NULL) + { + // Draw a normal character. + screen->DrawTexture(pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding, + DTA_Translation, remap, + DTA_CleanNoMove, true, + TAG_DONE); + } + else if (ch == ' ') + { + // Draw the space as a box outline. We also draw it 50% wider than it really is. + const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4; + const int x2 = x1 + width * 3 * CleanXfac / 2; + const int y1 = yy + top_padding; + const int y2 = y1 + SmallFont->GetHeight() * CleanYfac; + const int palentry = remap->Remap[remap->NumEntries*2/3]; + const uint32 palcolor = remap->Palette[remap->NumEntries*2/3]; + screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top + screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom + screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left + screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right + } + else if (ch == '\b' || ch == 0) + { + // Draw the backspace and end "characters". + const char *const str = ch == '\b' ? "BS" : "ED"; + screen->DrawText(SmallFont, color, + xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2, + yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE); + } + } + } + } + Super::Drawer(); +} \ No newline at end of file diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp new file mode 100644 index 000000000..bde298894 --- /dev/null +++ b/src/menu/messagebox.cpp @@ -0,0 +1,733 @@ +/* +** messagebox.cpp +** Confirmation, notification screns +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu/menu.h" +#include "d_event.h" +#include "d_gui.h" +#include "v_video.h" +#include "v_text.h" +#include "d_main.h" +#include "gstrings.h" +#include "gi.h" +#include "i_video.h" +#include "st_start.h" +#include "c_dispatch.h" +#include "g_game.h" + + +extern FSaveGameNode *quickSaveSlot; + +class DMessageBoxMenu : public DMenu +{ + DECLARE_CLASS(DMessageBoxMenu, DMenu) + + FBrokenLines *mMessage; + int mMessageMode; + int messageSelection; + int mMouseLeft, mMouseRight, mMouseY; + FName mAction; + +public: + + DMessageBoxMenu(DMenu *parent = NULL, const char *message = NULL, int messagemode = 0, bool playsound = false, FName action = NAME_None); + void Destroy(); + void Init(DMenu *parent, const char *message, int messagemode, bool playsound = false); + void Drawer(); + bool Responder(event_t *ev); + bool MenuEvent(int mkey, bool fromcontroller); + bool MouseEvent(int type, int x, int y); + void CloseSound(); + virtual void HandleResult(bool res); +}; + +IMPLEMENT_CLASS(DMessageBoxMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DMessageBoxMenu::DMessageBoxMenu(DMenu *parent, const char *message, int messagemode, bool playsound, FName action) +: DMenu(parent) +{ + mAction = action; + messageSelection = 0; + mMouseLeft = 140; + mMouseY = INT_MIN; + int mr1 = 170 + SmallFont->StringWidth(GStrings["TXT_YES"]); + int mr2 = 170 + SmallFont->StringWidth(GStrings["TXT_NO"]); + mMouseRight = MAX(mr1, mr2); + + Init(parent, message, messagemode, playsound); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMessageBoxMenu::Init(DMenu *parent, const char *message, int messagemode, bool playsound) +{ + mParentMenu = parent; + if (message != NULL) + { + if (*message == '$') message = GStrings(message+1); + mMessage = V_BreakLines(SmallFont, 300, message); + } + else mMessage = NULL; + mMessageMode = messagemode; + if (playsound) + { + S_StopSound (CHAN_VOICE); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", snd_menuvolume, ATTN_NONE); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMessageBoxMenu::Destroy() +{ + if (mMessage != NULL) V_FreeBrokenLines(mMessage); + mMessage = NULL; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMessageBoxMenu::CloseSound() +{ + S_Sound (CHAN_VOICE | CHAN_UI, + DMenu::CurrentMenu != NULL? "menu/backup" : "menu/dismiss", snd_menuvolume, ATTN_NONE); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMessageBoxMenu::HandleResult(bool res) +{ + if (mParentMenu != NULL) + { + if (mMessageMode == 0) + { + if (mAction == NAME_None) + { + mParentMenu->MenuEvent(res? MKEY_MBYes : MKEY_MBNo, false); + Close(); + } + else + { + Close(); + if (res) M_SetMenu(mAction, -1); + } + CloseSound(); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DMessageBoxMenu::Drawer () +{ + int i, y; + PalEntry fade = 0; + + int fontheight = SmallFont->GetHeight(); + //BorderNeedRefresh = screen->GetPageCount (); + //SB_state = screen->GetPageCount (); + + y = 100; + + if (mMessage != NULL) + { + for (i = 0; mMessage[i].Width >= 0; i++) + y -= SmallFont->GetHeight () / 2; + + for (i = 0; mMessage[i].Width >= 0; i++) + { + screen->DrawText (SmallFont, CR_UNTRANSLATED, 160 - mMessage[i].Width/2, y, mMessage[i].Text, + DTA_Clean, true, TAG_DONE); + y += fontheight; + } + } + + if (mMessageMode == 0) + { + y += fontheight; + mMouseY = y; + screen->DrawText(SmallFont, + messageSelection == 0? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, + 160, y, GStrings["TXT_YES"], DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, + messageSelection == 1? OptionSettings.mFontColorSelection : OptionSettings.mFontColor, + 160, y + fontheight + 1, GStrings["TXT_NO"], DTA_Clean, true, TAG_DONE); + + if (messageSelection >= 0) + { + if ((DMenu::MenuTime%8) < 6) + { + screen->DrawText(ConFont, OptionSettings.mFontColorSelection, + (150 - 160) * CleanXfac + screen->GetWidth() / 2, + (y + (fontheight + 1) * messageSelection - 100) * CleanYfac + screen->GetHeight() / 2, + "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); + } + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMessageBoxMenu::Responder(event_t *ev) +{ + if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_KeyDown) + { + if (mMessageMode == 0) + { + int ch = tolower(ev->data1); + if (ch == 'n' || ch == ' ') + { + HandleResult(false); + return true; + } + else if (ch == 'y') + { + HandleResult(true); + return true; + } + } + else + { + Close(); + return true; + } + return false; + } + else if (ev->type == EV_KeyDown) + { + Close(); + return true; + } + return Super::Responder(ev); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMessageBoxMenu::MenuEvent(int mkey, bool fromcontroller) +{ + if (mMessageMode == 0) + { + if (mkey == MKEY_Up || mkey == MKEY_Down) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + messageSelection = !messageSelection; + return true; + } + else if (mkey == MKEY_Enter) + { + // 0 is yes, 1 is no + HandleResult(!messageSelection); + return true; + } + else if (mkey == MKEY_Back) + { + HandleResult(false); + return true; + } + return false; + } + else + { + Close(); + CloseSound(); + return true; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DMessageBoxMenu::MouseEvent(int type, int x, int y) +{ + if (mMessageMode == 1) + { + if (type == MOUSE_Click) + { + return MenuEvent(MKEY_Enter, true); + } + return false; + } + else + { + int sel = -1; + int fh = SmallFont->GetHeight() + 1; + + // convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture + x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160; + y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100; + + if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + 2 * fh) + { + sel = y >= mMouseY + fh; + } + if (sel != -1 && sel != messageSelection) + { + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + messageSelection = sel; + if (type == MOUSE_Release) + { + return MenuEvent(MKEY_Enter, true); + } + return true; + } +} + +//============================================================================= +// +// +// +//============================================================================= +//============================================================================= +// +// +// +//============================================================================= + +class DQuitMenu : public DMessageBoxMenu +{ + DECLARE_CLASS(DQuitMenu, DMessageBoxMenu) + +public: + + DQuitMenu(bool playsound = false); + virtual void HandleResult(bool res); +}; + +IMPLEMENT_CLASS(DQuitMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DQuitMenu::DQuitMenu(bool playsound) +{ + int messageindex = gametic % gameinfo.quitmessages.Size(); + FString EndString; + const char *msg = gameinfo.quitmessages[messageindex]; + if (msg[0] == '$') + { + if (msg[1] == '*') + { + EndString = GStrings(msg+2); + } + else + { + EndString.Format("%s\n\n%s", GStrings(msg+1), GStrings("DOSY")); + } + } + else EndString = gameinfo.quitmessages[messageindex]; + + Init(NULL, EndString, 0, playsound); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DQuitMenu::HandleResult(bool res) +{ + if (res) + { + if (!netgame) + { + if (gameinfo.quitSound.IsNotEmpty()) + { + S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, snd_menuvolume, ATTN_NONE); + I_WaitVBL (105); + } + } + ST_Endoom(); + } + else + { + Close(); + CloseSound(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +CCMD (menu_quit) +{ // F10 + M_StartControlPanel (true); + DMenu *newmenu = new DQuitMenu(false); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); +} + + + +//============================================================================= +// +// +// +//============================================================================= +//============================================================================= +// +// +// +//============================================================================= + +class DEndGameMenu : public DMessageBoxMenu +{ + DECLARE_CLASS(DEndGameMenu, DMessageBoxMenu) + +public: + + DEndGameMenu(bool playsound = false); + virtual void HandleResult(bool res); +}; + +IMPLEMENT_CLASS(DEndGameMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DEndGameMenu::DEndGameMenu(bool playsound) +{ + int messageindex = gametic % gameinfo.quitmessages.Size(); + FString EndString = gameinfo.quitmessages[messageindex]; + + if (netgame) + { + if(gameinfo.gametype == GAME_Chex) + EndString = GStrings("CNETEND"); + else + EndString = GStrings("NETEND"); + return; + } + + if(gameinfo.gametype == GAME_Chex) + EndString = GStrings("CENDGAME"); + else + EndString = GStrings("ENDGAME"); + + + Init(NULL, EndString, 0, playsound); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DEndGameMenu::HandleResult(bool res) +{ + if (res) + { + M_ClearMenus (); + D_StartTitle (); + } + else + { + Close(); + CloseSound(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +CCMD (menu_endgame) +{ // F7 + if (!usergame) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE); + return; + } + + //M_StartControlPanel (true); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); + DMenu *newmenu = new DEndGameMenu(false); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); +} + +//============================================================================= +// +// +// +//============================================================================= +//============================================================================= +// +// +// +//============================================================================= + +class DQuickSaveMenu : public DMessageBoxMenu +{ + DECLARE_CLASS(DQuickSaveMenu, DMessageBoxMenu) + +public: + + DQuickSaveMenu(bool playsound = false); + virtual void HandleResult(bool res); +}; + +IMPLEMENT_CLASS(DQuickSaveMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DQuickSaveMenu::DQuickSaveMenu(bool playsound) +{ + FString tempstring; + + if(gameinfo.gametype == GAME_Chex) + tempstring.Format(GStrings("CQSPROMPT"), quickSaveSlot->Title); + else + tempstring.Format(GStrings("QSPROMPT"), quickSaveSlot->Title); + + Init(NULL, tempstring, 0, playsound); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DQuickSaveMenu::HandleResult(bool res) +{ + if (res) + { + G_SaveGame (quickSaveSlot->Filename.GetChars(), quickSaveSlot->Title); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); + M_ClearMenus(); + } + else + { + Close(); + CloseSound(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +CCMD (quicksave) +{ // F6 + if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/invalid", snd_menuvolume, ATTN_NONE); + return; + } + + if (gamestate != GS_LEVEL) + return; + + S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE); + if (quickSaveSlot == NULL) + { + M_StartControlPanel(false); + M_SetMenu(NAME_Savegamemenu); + return; + } + DMenu *newmenu = new DQuickSaveMenu(false); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); +} + +//============================================================================= +// +// +// +//============================================================================= +//============================================================================= +// +// +// +//============================================================================= + +class DQuickLoadMenu : public DMessageBoxMenu +{ + DECLARE_CLASS(DQuickLoadMenu, DMessageBoxMenu) + +public: + + DQuickLoadMenu(bool playsound = false); + virtual void HandleResult(bool res); +}; + +IMPLEMENT_CLASS(DQuickLoadMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DQuickLoadMenu::DQuickLoadMenu(bool playsound) +{ + FString tempstring; + + if(gameinfo.gametype == GAME_Chex) + tempstring.Format(GStrings("CQLPROMPT"), quickSaveSlot->Title); + else + tempstring.Format(GStrings("QLPROMPT"), quickSaveSlot->Title); + + Init(NULL, tempstring, 0, playsound); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DQuickLoadMenu::HandleResult(bool res) +{ + if (res) + { + G_LoadGame (quickSaveSlot->Filename.GetChars()); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/dismiss", snd_menuvolume, ATTN_NONE); + M_ClearMenus(); + } + else + { + Close(); + CloseSound(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +CCMD (quickload) +{ // F9 + M_StartControlPanel (true); + + if (netgame) + { + if(gameinfo.gametype == GAME_Chex) + M_StartMessage (GStrings("CQLOADNET"), NULL); + else + M_StartMessage (GStrings("QLOADNET"), NULL); + return; + } + + if (quickSaveSlot == NULL) + { + M_StartControlPanel(false); + // signal that whatever gets loaded should be the new quicksave + quickSaveSlot = (FSaveGameNode *)1; + M_SetMenu(NAME_Loadgamemenu); + return; + } + DMenu *newmenu = new DQuickLoadMenu(false); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_StartMessage(const char *message, int messagemode, FName action) +{ + if (DMenu::CurrentMenu == NULL) + { + // only play a sound if no menu was active before + M_StartControlPanel(menuactive == MENU_Off); + } + DMenu *newmenu = new DMessageBoxMenu(DMenu::CurrentMenu, message, messagemode, false, action); + newmenu->mParentMenu = DMenu::CurrentMenu; + M_ActivateMenu(newmenu); +} + diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp new file mode 100644 index 000000000..735b674ea --- /dev/null +++ b/src/menu/optionmenu.cpp @@ -0,0 +1,591 @@ +/* +** optionmenu.cpp +** Handler class for the option menus and associated items +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "v_video.h" +#include "v_font.h" +#include "cmdlib.h" +#include "gstrings.h" +#include "g_level.h" +#include "gi.h" +#include "v_palette.h" +#include "d_gui.h" +#include "d_event.h" +#include "c_dispatch.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_bind.h" +#include "gameconfigfile.h" +#include "menu/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) +{ + int len = (int)strlen(str); + + screen->DrawText (ConFont, color, x, y, str, + DTA_CellX, 8 * CleanXfac_1, + DTA_CellY, 8 * CleanYfac_1, + TAG_DONE); +} + +//============================================================================= +// +// Draw a slider. Set fracdigits negative to not display the current value numerically. +// +//============================================================================= + +void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits) +{ + double range; + + range = max - min; + double ccur = clamp(cur, min, max) - min; + + if (CleanXfac > CleanXfac_1) + { + M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12"); + M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), y, "\x13"); + + if (fracdigits >= 0) + { + char textbuf[16]; + mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); + screen->DrawText(SmallFont, CR_DARKGRAY, x + (12*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); + } + } + else + { + // On 320x200 we need a shorter slider + M_DrawConText(CR_WHITE, x, y, "\x10\x11\x11\x11\x11\x11\x12"); + M_DrawConText(CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), y, "\x13"); + + if (fracdigits >= 0) + { + char textbuf[16]; + mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); + screen->DrawText(SmallFont, CR_DARKGRAY, x + (7*8 + 4) * CleanXfac_1, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); + } + } +} + + + +IMPLEMENT_CLASS(DOptionMenu) + +//============================================================================= +// +// +// +//============================================================================= + +DOptionMenu::DOptionMenu(DMenu *parent, FOptionMenuDescriptor *desc) +: DMenu(parent) +{ + CanScrollUp = false; + CanScrollDown = false; + VisBottom = 0; + mFocusControl = NULL; + Init(parent, desc); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc) +{ + mParentMenu = parent; + GC::WriteBarrier(this, parent); + mDesc = desc; + if (mDesc != NULL && mDesc->mSelectedItem < 0) + { + // Go down to the first selectable item + int i = -1; + mDesc->mSelectedItem = -1; + do + { + i++; + } + while (!mDesc->mItems[i]->Selectable() && i < (int)mDesc->mItems.Size()); + if (i>=0) mDesc->mSelectedItem = i; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +FOptionMenuItem *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) + { + if (mDesc->mScrollPos > 0) + { + mDesc->mScrollPos--; + } + return true; + } + else if (ev->subtype == EV_GUI_WheelDown) + { + if (CanScrollDown) + { + 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: + do + { + --mDesc->mSelectedItem; + + if (mDesc->mScrollPos > 0 && + mDesc->mSelectedItem == mDesc->mScrollTop + mDesc->mScrollPos) + { + mDesc->mScrollPos--; + } + + 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: + do + { + ++mDesc->mSelectedItem; + + if (CanScrollDown && mDesc->mSelectedItem == VisBottom) + { + mDesc->mScrollPos++; + VisBottom++; + } + if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) + { + 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; + } + mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1; + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + { + ++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; + } + mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos; + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + { + ++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 labelofs = OptionSettings.mLabelOffset * CleanXfac_1; + //int cursorspace = 14 * CleanXfac_1; + 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 (i == mDesc->mScrollTop) + { + i += mDesc->mScrollPos; + if (i >= mDesc->mItems.Size()) break; // skipped beyond end of menu + } + int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, mDesc->mSelectedItem == i); + if (cur_indent >= 0 && mDesc->mSelectedItem == i && mDesc->mItems[i]->Selectable()) + { + if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this) + { + M_DrawConText(OptionSettings.mFontColorSelection, cur_indent + 3 * CleanXfac_1, y-CleanYfac_1+OptionSettings.mLabelOffset, "\xd"); + } + } + } + + CanScrollUp = (mDesc->mScrollPos > 0); + CanScrollDown = (i < mDesc->mItems.Size()); + VisBottom = i - 1; + + if (CanScrollUp) + { + M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, ytop + OptionSettings.mLabelOffset, "\x1a"); + } + if (CanScrollDown) + { + M_DrawConText(CR_ORANGE, 3 * CleanXfac_1, y - 8*CleanYfac_1 + OptionSettings.mLabelOffset, "\x1b"); + } + Super::Drawer(); +} + + +//============================================================================= +// +// base class for menu items +// +//============================================================================= + +FOptionMenuItem::~FOptionMenuItem() +{ + if (mLabel != NULL) delete [] mLabel; +} + +bool FOptionMenuItem::CheckCoordinate(FOptionMenuDescriptor *desc, int x, int y) +{ + return false; +} + +int FOptionMenuItem::Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected) +{ + return indent; +} + +bool FOptionMenuItem::Selectable() +{ + return true; +} + +bool FOptionMenuItem::MouseEvent(int type, int x, int y) +{ + if (Selectable() && type == DMenu::MOUSE_Release) + { + return DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true); + } + return false; +} + +int FOptionMenuItem::GetIndent() +{ + return mCentered? 0 : SmallFont->StringWidth(mLabel); +} + +void FOptionMenuItem::drawLabel(int indent, int y, EColorRange color, bool grayed) +{ + const char *label = mLabel; + 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 FOptionMenuDescriptor::CalcIndent() +{ + // calculate the menu indent + int widest = 0, thiswidth; + + for (unsigned i = 0; i < mItems.Size(); i++) + { + thiswidth = mItems[i]->GetIndent(); + if (thiswidth > widest) widest = thiswidth; + } + mIndent = widest + 4; +} + +//============================================================================= +// +// +// +//============================================================================= + +FOptionMenuItem *FOptionMenuDescriptor::GetItem(FName name) +{ + for(unsigned i=0;iGetAction(NULL); + if (nm == name) return mItems[i]; + } + return NULL; +} + + + + +class DGameplayMenu : public DOptionMenu +{ + DECLARE_CLASS(DGameplayMenu, DOptionMenu) + +public: + DGameplayMenu() + {} + + void Drawer () + { + Super::Drawer(); + + char text[64]; + mysnprintf(text, 64, "dmflags = %d dmflags2 = %d", *dmflags, *dmflags2); + screen->DrawText (SmallFont, OptionSettings.mFontColorValue, + (screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text, + DTA_CleanNoMove_1, true, TAG_DONE); + } +}; + +IMPLEMENT_CLASS(DGameplayMenu) + +class DCompatibilityMenu : public DOptionMenu +{ + DECLARE_CLASS(DCompatibilityMenu, DOptionMenu) + +public: + DCompatibilityMenu() + {} + + void Drawer () + { + Super::Drawer(); + + char text[64]; + mysnprintf(text, 64, "compatflags = %d", *compatflags); + screen->DrawText (SmallFont, OptionSettings.mFontColorValue, + (screen->GetWidth() - SmallFont->StringWidth (text) * CleanXfac_1) / 2, 0, text, + DTA_CleanNoMove_1, true, TAG_DONE); + } +}; + +IMPLEMENT_CLASS(DCompatibilityMenu) diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h new file mode 100644 index 000000000..07a72fb53 --- /dev/null +++ b/src/menu/optionmenuitems.h @@ -0,0 +1,916 @@ +/* +** 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. +**--------------------------------------------------------------------------- +** +*/ + + +void M_DrawConText (int color, int x, int y, const char *str); +void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigits); +void M_SetVideoMode(); + + + +//============================================================================= +// +// opens a submenu, action is a submenu name +// +//============================================================================= + +class FOptionMenuItemSubmenu : public FOptionMenuItem +{ + int mParam; +public: + FOptionMenuItemSubmenu(const char *label, const char *menu, int param = 0) + : FOptionMenuItem(label, menu) + { + mParam = param; + } + + int Draw(FOptionMenuDescriptor *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; + } +}; + + +//============================================================================= +// +// Executes a CCMD, action is a CCMD name +// +//============================================================================= + +class FOptionMenuItemCommand : public FOptionMenuItemSubmenu +{ +public: + FOptionMenuItemCommand(const char *label, const char *menu) + : FOptionMenuItemSubmenu(label, menu) + { + } + + bool Activate() + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + C_DoCommand(mAction); + return true; + } + +}; + +//============================================================================= +// +// Executes a CCMD after confirmation, action is a CCMD name +// +//============================================================================= + +class FOptionMenuItemSafeCommand : public FOptionMenuItemCommand +{ + // action is a CCMD +public: + FOptionMenuItemSafeCommand(const char *label, const char *menu) + : FOptionMenuItemCommand(label, menu) + { + } + + bool MenuEvent (int mkey, bool fromcontroller) + { + if (mkey == MKEY_MBYes) + { + C_DoCommand(mAction); + return true; + } + return FOptionMenuItemCommand::MenuEvent(mkey, fromcontroller); + } + + bool Activate() + { + M_StartMessage("Do you really want to do this?", 0); + return true; + } +}; + +//============================================================================= +// +// Base class for option lists +// +//============================================================================= + +class FOptionMenuItemOptionBase : public FOptionMenuItem +{ +protected: + // action is a CVAR + FOptionValues *mValues; + FBaseCVar *mGrayCheck; + int mCenter; +public: + + enum + { + OP_VALUES = 0x11001 + }; + + FOptionMenuItemOptionBase(const char *label, const char *menu, const char *values, const char *graycheck, int center) + : FOptionMenuItem(label, menu) + { + FOptionValues **opt = OptionValues.CheckKey(values); + if (opt != NULL) + { + mValues = *opt; + } + else + { + mValues = NULL; + } + mGrayCheck = (FBoolCVar*)FindCVar(graycheck, NULL); + mCenter = center; + } + + bool SetString(int i, const char *newtext) + { + if (i == OP_VALUES) + { + FOptionValues **opt = OptionValues.CheckKey(newtext); + if (opt != NULL) + { + mValues = *opt; + int s = GetSelection(); + if (s >= (int)mValues->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(FOptionMenuDescriptor *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(); + if (Selection < 0) + { + text = "Unknown"; + } + else + { + text = mValues->mValues[Selection].Text; + } + 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) + { + if (mValues->mValues.Size() > 0) + { + int Selection = GetSelection(); + if (mkey == MKEY_Left) + { + if (Selection == -1) Selection = 0; + else if (--Selection < 0) Selection = mValues->mValues.Size()-1; + } + else if (mkey == MKEY_Right || mkey == MKEY_Enter) + { + if (++Selection >= (int)mValues->mValues.Size()) Selection = 0; + } + else + { + return FOptionMenuItem::MenuEvent(mkey, fromcontroller); + } + SetSelection(Selection); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); + } + return true; + } + + bool Selectable() + { + return !(mGrayCheck != NULL && !(mGrayCheck->GetGenericRep(CVAR_Bool).Bool)); + } +}; + +//============================================================================= +// +// Change a CVAR, action is the CVAR name +// +//============================================================================= + +class FOptionMenuItemOption : public FOptionMenuItemOptionBase +{ + // action is a CVAR + FBaseCVar *mCVar; +public: + + FOptionMenuItemOption(const char *label, const char *menu, const char *values, const char *graycheck, int center) + : FOptionMenuItemOptionBase(label, menu, values, graycheck, center) + { + mCVar = FindCVar(mAction, NULL); + } + + //============================================================================= + int GetSelection() + { + int Selection = -1; + if (mValues != NULL && mCVar != NULL && mValues->mValues.Size() > 0) + { + if (mValues->mValues[0].TextValue.IsEmpty()) + { + UCVarValue cv = mCVar->GetGenericRep(CVAR_Float); + for(unsigned i=0;imValues.Size(); i++) + { + if (fabs(cv.Float - mValues->mValues[i].Value) < FLT_EPSILON) + { + Selection = i; + break; + } + } + } + else + { + UCVarValue cv = mCVar->GetGenericRep(CVAR_String); + for(unsigned i=0;imValues.Size(); i++) + { + if (mValues->mValues[i].TextValue.CompareNoCase(cv.String) == 0) + { + Selection = i; + break; + } + } + } + } + return Selection; + } + + void SetSelection(int Selection) + { + UCVarValue value; + if (mValues != NULL && mCVar != NULL && mValues->mValues.Size() > 0) + { + if (mValues->mValues[0].TextValue.IsEmpty()) + { + value.Float = (float)mValues->mValues[Selection].Value; + mCVar->SetGenericRep (value, CVAR_Float); + } + else + { + value.String = mValues->mValues[Selection].TextValue.LockBuffer(); + mCVar->SetGenericRep (value, CVAR_String); + mValues->mValues[Selection].TextValue.UnlockBuffer(); + } + } + } +}; + +//============================================================================= +// +// 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); + FListMenuItem *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->MenuEvent((ev->data1 == KEY_ESCAPE)? MKEY_Abort : MKEY_Input, 0); + return true; + } + return false; + } + + void Drawer() + { + mParentMenu->Drawer(); + } +}; + +#ifndef NO_IMP +IMPLEMENT_ABSTRACT_CLASS(DEnterKey) +#endif + +//============================================================================= +// +// // Edit a key binding, Action is the CCMD to bind +// +//============================================================================= + +class FOptionMenuItemControl : public FOptionMenuItem +{ + FKeyBindings *mBindings; + int mInput; + bool mWaiting; +public: + + FOptionMenuItemControl(const char *label, const char *menu, FKeyBindings *bindings) + : FOptionMenuItem(label, menu) + { + mBindings = bindings; + mWaiting = false; + } + + + //============================================================================= + int Draw(FOptionMenuDescriptor *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-1+OptionSettings.mLabelOffset, description); + } + else + { + screen->DrawText(SmallFont, CR_BLACK, indent + CURSORSPACE, y + OptionSettings.mLabelOffset, "---", + 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; + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuItemStaticText : public FOptionMenuItem +{ + EColorRange mColor; +public: + FOptionMenuItemStaticText(const char *label, bool header) + : FOptionMenuItem(label, NAME_None, true) + { + mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor; + } + + int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected) + { + drawLabel(indent, y, mColor); + return -1; + } + + bool Selectable() + { + return false; + } + +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuItemStaticTextSwitchable : public FOptionMenuItem +{ + EColorRange mColor; + FString mAltText; + int mCurrent; + +public: + FOptionMenuItemStaticTextSwitchable(const char *label, const char *label2, FName action, bool header) + : FOptionMenuItem(label, action, true) + { + mColor = header? OptionSettings.mFontColorHeader : OptionSettings.mFontColor; + mAltText = label2; + mCurrent = 0; + } + + int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected) + { + const char *txt = mCurrent? (const char*)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, 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; + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderBase : public FOptionMenuItem +{ + // action is a CVAR + double mMin, mMax, mStep; + int mShowValue; + int mDrawX; +public: + FOptionMenuSliderBase(const char *label, double min, double max, double step, int showval) + : FOptionMenuItem(label, NAME_None) + { + mMin = min; + mMax = max; + mStep = step; + mShowValue = showval; + mDrawX = 0; + } + + virtual double GetValue() = 0; + virtual void SetValue(double val) = 0; + + //============================================================================= + int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected) + { + drawLabel(indent, y, selected? OptionSettings.mFontColorSelection : OptionSettings.mFontColor); + mDrawX = indent + CURSORSPACE; + M_DrawSlider (mDrawX, y + OptionSettings.mLabelOffset, mMin, mMax, GetValue(), mShowValue); + return indent; + } + + //============================================================================= + bool MenuEvent (int mkey, bool fromcontroller) + { + double value = GetValue(); + + if (mkey == MKEY_Left) + { + value -= mStep; + } + else if (mkey == MKEY_Right) + { + value += mStep; + } + else + { + return FOptionMenuItem::MenuEvent(mkey, fromcontroller); + } + SetValue(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; // 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 != GetValue()) + { + SetValue(v); + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); + } + if (type == DMenu::MOUSE_Click) + { + lm->SetFocus(this); + } + return true; + } + +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderCVar : public FOptionMenuSliderBase +{ + FBaseCVar *mCVar; +public: + FOptionMenuSliderCVar(const char *label, const char *menu, double min, double max, double step, int showval) + : FOptionMenuSliderBase(label, min, max, step, showval) + { + mCVar = FindCVar(menu, NULL); + } + + double GetValue() + { + if (mCVar != NULL) + { + return mCVar->GetGenericRep(CVAR_Float).Float; + } + else + { + return 0; + } + } + + void SetValue(double val) + { + if (mCVar != NULL) + { + UCVarValue value; + value.Float = (float)val; + mCVar->SetGenericRep(value, CVAR_Float); + } + } +}; + +//============================================================================= +// +// +// +//============================================================================= + +class FOptionMenuSliderVar : public FOptionMenuSliderBase +{ + float *mPVal; +public: + + FOptionMenuSliderVar(const char *label, float *pVal, double min, double max, double step, int showval) + : FOptionMenuSliderBase(label, min, max, step, showval) + { + mPVal = pVal; + } + + double GetValue() + { + return *mPVal; + } + + void SetValue(double val) + { + *mPVal = (float)val; + } +}; + +//============================================================================= +// +// // Edit a key binding, Action is the CCMD to bind +// +//============================================================================= + +class FOptionMenuItemColorPicker : public FOptionMenuItem +{ + FColorCVar *mCVar; +public: + + enum + { + CPF_RESET = 0x20001, + }; + + FOptionMenuItemColorPicker(const char *label, const char *menu) + : FOptionMenuItem(label, menu) + { + FBaseCVar *cv = FindCVar(menu, NULL); + if (cv->GetRealType() == CVAR_Color) + { + mCVar = (FColorCVar*)cv; + } + else mCVar = NULL; + } + + //============================================================================= + int Draw(FOptionMenuDescriptor *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 + OptionSettings.mLabelOffset * CleanYfac_1 / 2; + screen->Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + (SmallFont->GetHeight() - 1) * 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; + } +}; + +class FOptionMenuScreenResolutionLine : public FOptionMenuItem +{ + FString mResTexts[3]; + int mSelection; + int mHighlight; + int mMaxValid; +public: + + enum + { + SRL_INDEX = 0x30000, + SRL_SELECTION = 0x30003, + SRL_HIGHLIGHT = 0x30004, + }; + + FOptionMenuScreenResolutionLine(const char *action) + : FOptionMenuItem("", 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/change", snd_menuvolume, ATTN_NONE); + return true; + } + else if (mkey == MKEY_Right) + { + if (++mSelection > mMaxValid) mSelection = 0; + S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); + return true; + } + else + { + return FOptionMenuItem::MenuEvent(mkey, fromcontroller); + } + return false; + } + + bool MouseEvent(int type, int x, int y) + { + int colwidth = screen->GetWidth() / 3; + mSelection = x / colwidth; + return FOptionMenuItem::MouseEvent(type, x, y); + } + + bool Activate() + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + M_SetVideoMode(); + return true; + } + + int Draw(FOptionMenuDescriptor *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; + } +}; + +#ifndef NO_IMP +CCMD(am_restorecolors) +{ + if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu))) + { + DOptionMenu *m = (DOptionMenu*)DMenu::CurrentMenu; + const FOptionMenuDescriptor *desc = m->GetDescriptor(); + // Find the color cvars by scanning the MapColors menu. + for (unsigned i = 0; i < desc->mItems.Size(); ++i) + { + desc->mItems[i]->SetValue(FOptionMenuItemColorPicker::CPF_RESET, 0); + } + } +} +#endif diff --git a/src/menu/playerdisplay.cpp b/src/menu/playerdisplay.cpp new file mode 100644 index 000000000..f9871e1ca --- /dev/null +++ b/src/menu/playerdisplay.cpp @@ -0,0 +1,561 @@ +/* +** 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 "tables.h" +#include "m_fixed.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" + + +//============================================================================= +// +// Used by the player display +// +//============================================================================= + +struct FBackdropTexture : public FTexture +{ +public: + FBackdropTexture(); + + const BYTE *GetColumn(unsigned int column, const Span **spans_out); + const BYTE *GetPixels(); + void Unload(); + bool CheckModified(); + +protected: + BYTE Pixels[144*160]; + static const Span DummySpan[2]; + int LastRenderTic; + + angle_t time1, time2, time3, time4; + angle_t t1ang, t2ang, z1ang, z2ang; + + void Render(); +}; + + + +// A 32x32 cloud rendered with Photoshop, plus some other filters +static BYTE pattern1[1024] = +{ + 5, 9, 7,10, 9,15, 9, 7, 8,10, 5, 3, 5, 7, 9, 8,14, 8, 4, 7, 8, 9, 5, 7,14, 7, 0, 7,13,13, 9, 6, + 2, 7, 9, 7, 7,10, 8, 8,11,10, 6, 7,10, 7, 5, 6, 6, 4, 7,13,15,16,11,15,11, 8, 0, 4,13,22,17,11, + 5, 9, 9, 7, 9,10, 4, 3, 6, 7, 8, 6, 5, 4, 2, 2, 1, 4, 6,11,15,15,14,13,17, 9, 5, 9,11,12,17,20, + 9,16, 9, 8,12,13, 7, 3, 7, 9, 5, 4, 2, 5, 5, 5, 7,11, 6, 7, 6,13,17,10,10, 9,12,17,14,12,16,15, + 15,13, 5, 3, 9,10, 4,10,12,12, 7, 9, 8, 8, 8,10, 7, 6, 5, 5, 5, 6,11, 9, 3,13,16,18,21,16,23,18, + 23,13, 0, 0, 0, 0, 0,12,18,14,15,16,13, 7, 7, 5, 9, 6, 6, 8, 4, 0, 0, 0, 0,14,19,17,14,20,21,25, + 19,20,14,13, 7, 5,13,19,14,13,17,15,14, 7, 3, 5, 6,11, 7, 7, 8, 8,10, 9, 9,18,17,15,14,15,18,16, + 16,29,24,23,18, 9,17,20,11, 5,12,15,15,12, 6, 3, 4, 6, 7,10,13,18,18,19,16,12,17,19,23,16,14,14, + 9,18,20,26,19, 5,18,18,10, 5,12,15,14,17,11, 6,11, 9,10,13,10,20,24,20,21,20,14,18,15,22,20,19, + 0, 6,16,18, 8, 7,15,18,10,13,17,17,13,11,15,11,19,12,13,10, 4,15,19,21,21,24,14, 9,17,20,24,17, + 18,17, 7, 7,16,21,22,15, 5,14,20,14,13,21,13, 8,12,14, 7, 8,11,15,13,11,16,17, 7, 5,12,17,19,14, + 25,23,17,16,23,18,15, 7, 0, 6,11, 6,11,15,11, 7,12, 7, 4,10,16,13, 7, 7,15,13, 9,15,21,14, 5, 0, + 18,22,21,21,21,22,12, 6,14,20,15, 6,10,19,13, 8, 7, 3, 7,12,14,16, 9,12,22,15,12,18,24,19,17, 9, + 0,15,18,21,17,25,14,13,19,21,21,11, 6,13,16,16,12,10,12,11,13,20,14,13,18,13, 9,15,16,25,31,20, + 5,20,24,16, 7,14,14,11,18,19,19, 6, 0, 5,11,14,17,16,19,14,15,21,19,15,14,14, 8, 0, 7,24,18,16, + 9,17,15, 7, 6,14,12, 7,14,16,11, 4, 7, 6,13,16,15,13,12,20,21,20,21,17,18,26,14, 0,13,23,21,11, + 9,12,18,11,15,21,13, 8,13,13,10, 7,13, 8, 8,19,13, 7, 4,15,19,18,14,12,14,15, 8, 6,16,22,22,15, + 9,17,14,19,15,14,15, 9,11, 9, 6, 8,14,13,13,12, 5, 0, 0, 6,12,13, 7, 7, 9, 7, 0,12,21,16,15,18, + 15,16,18,11, 6, 8,15, 9, 2, 0, 5,10,10,16, 9, 0, 4,12,15, 9,12, 9, 7, 7,12, 7, 0, 6,12, 6, 9,13, + 12,19,15,14,11, 7, 8, 9,12,10, 5, 5, 7,12,12,10,14,16,16,11, 8,12,10,12,10, 8,10,10,14,12,16,16, + 16,17,20,22,12,15,12,14,19,11, 6, 5,10,13,17,17,21,19,15, 9, 6, 9,15,18,10,10,18,14,20,15,16,17, + 11,19,19,18,19,14,17,13,12,12, 7,11,18,17,16,15,19,19,10, 2, 0, 8,15,12, 8,11,12,10,19,20,19,19, + 6,14,18,13,13,16,16,12, 5, 8,10,12,10,13,18,12, 9,10, 7, 6, 5,11, 8, 6, 7,13,16,13,10,15,20,14, + 0, 5,12,12, 4, 0, 9,16, 9,10,12, 8, 0, 9,13, 9, 0, 2, 4, 7,10, 6, 7, 3, 4,11,16,18,10,11,21,21, + 16,13,11,15, 8, 0, 5, 9, 8, 7, 6, 3, 0, 9,17, 9, 0, 0, 0, 3, 5, 4, 3, 5, 7,15,16,16,17,14,22,22, + 24,14,15,12, 9, 0, 5,10, 8, 4, 7,12,10,11,12, 7, 6, 8, 6, 5, 7, 8, 8,11,13,10,15,14,12,18,20,16, + 16,17,17,18,12, 9,12,16,10, 5, 6,20,13,15, 8, 4, 8, 9, 8, 7, 9,11,12,17,16,16,11,10, 9,10, 5, 0, + 0,14,18,18,15,16,14, 9,10, 9, 9,15,14,10, 4, 6,10, 8, 8, 7,10, 9,10,16,18,10, 0, 0, 7,12,10, 8, + 0,14,19,14, 9,11,11, 8, 8,10,15, 9,10, 7, 4,10,13, 9, 7, 5, 5, 7, 7, 7,13,13, 5, 5,14,22,18,16, + 0,10,14,10, 3, 6, 5, 6, 8, 9, 8, 9, 5, 9, 8, 9, 6, 8, 8, 8, 1, 0, 0, 0, 9,17,12,12,17,19,20,13, + 6,11,17,11, 5, 5, 8,10, 6, 5, 6, 6, 3, 7, 9, 7, 6, 8,12,10, 4, 8, 6, 6,11,16,16,15,16,17,17,16, + 11, 9,10,10, 5, 6,12,10, 5, 1, 6,10, 5, 3, 3, 5, 4, 7,15,10, 7,13, 7, 8,15,11,15,15,15, 8,11,15, +}; + +// Just a 32x32 cloud rendered with the standard Photoshop filter +static BYTE pattern2[1024] = +{ + 9, 9, 8, 8, 8, 8, 6, 6,13,13,11,21,19,21,23,18,23,24,19,19,24,17,18,12, 9,14, 8,12,12, 5, 8, 6, + 11,10, 6, 7, 8, 8, 9,13,10,11,17,15,23,22,23,22,20,26,27,26,17,21,20,14,12, 8,11, 8,11, 7, 8, 7, + 6, 9,13,13,10, 9,13, 7,12,13,16,19,16,20,22,25,22,25,27,22,21,23,15,10,14,14,15,13,12, 8,12, 6, + 6, 7,12,12,12,16, 9,12,12,15,16,11,21,24,19,24,23,26,28,27,26,21,14,15, 7, 7,10,15,12,11,10, 9, + 7,14,11,16,12,18,16,14,16,14,11,14,15,21,23,17,20,18,26,24,27,18,20,11,11,14,10,17,17,10, 6,10, + 13, 9,14,10,13,11,14,15,18,15,15,12,19,19,20,18,22,20,19,22,19,19,19,20,17,15,15,11,16,14,10, 8, + 13,16,12,16,17,19,17,18,15,19,14,18,15,14,15,17,21,19,23,18,23,22,18,18,17,15,15,16,12,12,15,10, + 10,12,14,10,16,11,18,15,21,20,20,17,18,19,16,19,14,20,19,14,19,25,22,21,22,24,18,12, 9, 9, 8, 6, + 10,10,13, 9,15,13,20,19,22,18,18,17,17,21,21,13,13,12,19,18,16,17,27,26,22,23,20,17,12,11, 8, 9, + 7,13,14,15,11,13,18,22,19,23,23,20,22,24,21,14,12,16,17,19,18,18,22,18,24,23,19,17,16,14, 8, 7, + 12,12, 8, 8,16,20,26,25,28,28,22,29,23,22,21,18,13,16,15,15,20,17,25,24,19,17,17,17,15,10, 8, 9, + 7,12,15,11,17,20,25,25,25,29,30,31,28,26,18,16,17,18,20,21,22,20,23,19,18,19,10,16,16,11,11, 8, + 5, 6, 8,14,14,17,17,21,27,23,27,31,27,22,23,21,19,19,21,19,20,19,17,22,13,17,12,15,10,10,12, 6, + 8, 9, 8,14,15,16,15,18,27,26,23,25,23,22,18,21,20,17,19,20,20,16,20,14,15,13,12, 8, 8, 7,11,13, + 7, 6,11,11,11,13,15,22,25,24,26,22,24,26,23,18,24,24,20,18,20,16,17,12,12,12,10, 8,11, 9, 6, 8, + 9,10, 9, 6, 5,14,16,19,17,21,26,20,23,19,19,17,20,21,26,25,23,21,17,13,12, 5,13,11, 7,12,10,12, + 6, 5, 4,10,11, 9,10,13,17,20,20,18,23,26,27,20,21,24,20,19,24,20,18,10,11, 3, 6,13, 9, 6, 8, 8, + 1, 2, 2,11,13,13,11,16,16,16,19,21,20,23,22,28,21,20,19,18,23,16,18, 7, 5, 9, 7, 6, 5,10, 8, 8, + 0, 0, 6, 9,11,15,12,12,19,18,19,26,22,24,26,30,23,22,22,16,20,19,12,12, 3, 4, 6, 5, 4, 7, 2, 4, + 2, 0, 0, 7,11, 8,14,13,15,21,26,28,25,24,27,26,23,24,22,22,15,17,12, 8,10, 7, 7, 4, 0, 5, 0, 1, + 1, 2, 0, 1, 9,14,13,10,19,24,22,29,30,28,30,30,31,23,24,19,17,14,13, 8, 8, 8, 1, 4, 0, 0, 0, 3, + 5, 2, 4, 2, 9, 8, 8, 8,18,23,20,27,30,27,31,25,28,30,28,24,24,15,11,14,10, 3, 4, 3, 0, 0, 1, 3, + 9, 3, 4, 3, 5, 6, 8,13,14,23,21,27,28,27,28,27,27,29,30,24,22,23,13,15, 8, 6, 2, 0, 4, 3, 4, 1, + 6, 5, 5, 3, 9, 3, 6,14,13,16,23,26,28,23,30,31,28,29,26,27,21,20,15,15,13, 9, 1, 0, 2, 0, 5, 8, + 8, 4, 3, 7, 2, 0,10, 7,10,14,21,21,29,28,25,27,30,28,25,24,27,22,19,13,10, 5, 0, 0, 0, 0, 0, 7, + 7, 6, 7, 0, 2, 2, 5, 6,15,11,19,24,22,29,27,31,30,30,31,28,23,18,14,14, 7, 5, 0, 0, 1, 0, 1, 0, + 5, 5, 5, 0, 0, 4, 5,11, 7,10,13,20,21,21,28,31,28,30,26,28,25,21, 9,12, 3, 3, 0, 2, 2, 2, 0, 1, + 3, 3, 0, 2, 0, 3, 5, 3,11,11,16,19,19,27,26,26,30,27,28,26,23,22,16, 6, 2, 2, 3, 2, 0, 2, 4, 0, + 0, 0, 0, 3, 3, 1, 0, 4, 5, 9,11,16,24,20,28,26,28,24,28,25,22,21,16, 5, 7, 5, 7, 3, 2, 3, 3, 6, + 0, 0, 2, 0, 2, 0, 4, 3, 8,12, 9,17,16,23,23,27,27,22,26,22,21,21,13,14, 5, 3, 7, 3, 2, 4, 6, 1, + 2, 5, 6, 4, 0, 1, 5, 8, 7, 6,15,17,22,20,24,28,23,25,20,21,18,16,13,15,13,10, 8, 5, 5, 9, 3, 7, + 7, 7, 0, 5, 1, 6, 7, 9,12, 9,12,21,22,25,24,22,23,25,24,18,24,22,17,13,10, 9,10, 9, 6,11, 6, 5, +}; + +const FTexture::Span FBackdropTexture::DummySpan[2] = { { 0, 160 }, { 0, 0 } }; + +//============================================================================= +// +// +// +//============================================================================= + +FBackdropTexture::FBackdropTexture() +{ + Width = 144; + Height = 160; + WidthBits = 8; + HeightBits = 8; + WidthMask = 255; + LastRenderTic = 0; + + time1 = ANGLE_1*180; + time2 = ANGLE_1*56; + time3 = ANGLE_1*99; + time4 = ANGLE_1*1; + t1ang = ANGLE_90; + t2ang = 0; + z1ang = 0; + z2ang = ANGLE_90/2; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FBackdropTexture::CheckModified() +{ + return LastRenderTic != gametic; +} + +void FBackdropTexture::Unload() +{ +} + +//============================================================================= +// +// +// +//============================================================================= + +const BYTE *FBackdropTexture::GetColumn(unsigned int column, const Span **spans_out) +{ + if (LastRenderTic != gametic) + { + Render(); + } + column = clamp(column, 0u, 143u); + if (spans_out != NULL) + { + *spans_out = DummySpan; + } + return Pixels + column*160; +} + +//============================================================================= +// +// +// +//============================================================================= + +const BYTE *FBackdropTexture::GetPixels() +{ + if (LastRenderTic != gametic) + { + Render(); + } + return Pixels; +} + +//============================================================================= +// +// This is one plasma and two rotozoomers. I think it turned out quite awesome. +// +//============================================================================= + +void FBackdropTexture::Render() +{ + BYTE *from; + int width, height, pitch; + + width = 160; + height = 144; + pitch = width; + + int x, y; + + const angle_t a1add = ANGLE_1/2; + const angle_t a2add = ANGLE_MAX-ANGLE_1; + const angle_t a3add = ANGLE_1*5/7; + const angle_t a4add = ANGLE_MAX-ANGLE_1*4/3; + + const angle_t t1add = ANGLE_MAX-ANGLE_1*2; + const angle_t t2add = ANGLE_MAX-ANGLE_1*3+ANGLE_1/6; + const angle_t t3add = ANGLE_1*16/7; + const angle_t t4add = ANGLE_MAX-ANGLE_1*2/3; + const angle_t x1add = 5<>ANGLETOFINESHIFT]>>2)+FRACUNIT/2; + fixed_t z2 = (finecosine[z1ang>>ANGLETOFINESHIFT]>>2)+FRACUNIT*3/4; + + tc = MulScale5 (finecosine[t1ang>>ANGLETOFINESHIFT], z1); + ts = MulScale5 (finesine[t1ang>>ANGLETOFINESHIFT], z1); + uc = MulScale5 (finecosine[t2ang>>ANGLETOFINESHIFT], z2); + us = MulScale5 (finesine[t2ang>>ANGLETOFINESHIFT], z2); + + ltx = -width/2*tc; + lty = -width/2*ts; + lux = -width/2*uc; + luy = -width/2*us; + + for (y = 0; y < height; ++y) + { + a1 = time1; + a2 = time2; + c3 = finecosine[a3>>ANGLETOFINESHIFT]; + c4 = finecosine[a4>>ANGLETOFINESHIFT]; + tx = ltx - (y-height/2)*ts; + ty = lty + (y-height/2)*tc; + ux = lux - (y-height/2)*us; + uy = luy + (y-height/2)*uc; + for (x = 0; x < width; ++x) + { + c1 = finecosine[a1>>ANGLETOFINESHIFT]; + c2 = finecosine[a2>>ANGLETOFINESHIFT]; + from[x] = ((c1 + c2 + c3 + c4) >> (FRACBITS+3-7)) + 128 // plasma + + pattern1[(tx>>27)+((ty>>22)&992)] // rotozoomer 1 + + pattern2[(ux>>27)+((uy>>22)&992)]; // rotozoomer 2 + tx += tc; + ty += ts; + ux += uc; + uy += us; + a1 += a1add; + a2 += a2add; + } + a3 += a3add; + a4 += a4add; + from += pitch; + } + + time1 += t1add; + time2 += t2add; + time3 += t3add; + time4 += t4add; + t1ang += x1add; + t2ang += x2add; + z1ang += z1add; + z2ang += z2add; + + LastRenderTic = gametic; +} + + +//============================================================================= +// +// +// +//============================================================================= + +FListMenuItemPlayerDisplay::FListMenuItemPlayerDisplay(FListMenuDescriptor *menu, int x, int y, PalEntry c1, PalEntry c2, bool np, FName action) +: FListMenuItem(x, y, action) +{ + mOwner = menu; + + 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; + mRemap.Remap[i] = ColorMatcher.Pick (r, g, b); + mRemap.Palette[i] = PalEntry(255, r, g, b); + } + mBackdrop = new FBackdropTexture; + mPlayerClass = NULL; + mPlayerState = NULL; + mNoportrait = np; + mMode = 0; + mRotation = 0; + mTranslate = false; + mSkin = 0; + mRandomClass = 0; + mRandomTimer = 0; + mClassNum = -1; +} + + +//============================================================================= +// +// +// +//============================================================================= + +FListMenuItemPlayerDisplay::~FListMenuItemPlayerDisplay() +{ + delete mBackdrop; +} + +//============================================================================= +// +// +// +//============================================================================= + +void FListMenuItemPlayerDisplay::UpdateRandomClass() +{ + if (--mRandomTimer < 0) + { + if (++mRandomClass >= (int)PlayerClasses.Size ()) mRandomClass = 0; + mPlayerClass = &PlayerClasses[mRandomClass]; + mPlayerState = GetDefaultByType (mPlayerClass->Type)->SeeState; + mPlayerTics = mPlayerState->GetTics(); + mRandomTimer = 6; + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +void FListMenuItemPlayerDisplay::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; + mPlayerTics = mPlayerState->GetTics(); + mClassNum = classnum; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FListMenuItemPlayerDisplay::UpdatePlayerClass() +{ + 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; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FListMenuItemPlayerDisplay::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 FListMenuItemPlayerDisplay::Ticker() +{ + if (mClassNum < 0) UpdateRandomClass(); + + if (mPlayerState != NULL && mPlayerState->GetTics () != -1 && mPlayerState->GetNextState () != NULL) + { + if (--mPlayerTics <= 0) + { + mPlayerState = mPlayerState->GetNextState(); + mPlayerTics = mPlayerState->GetTics(); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void FListMenuItemPlayerDisplay::Drawer(bool selected) +{ + if (mMode == 0 && !UpdatePlayerClass()) + { + return; + } + + const char *portrait = mPlayerClass->Type->Meta.GetMetaString(APMETA_Portrait); + + if (portrait != NULL && !mNoportrait) + { + FTextureID texid = TexMan.CheckForTexture(portrait, 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_Translation, &mRemap, + DTA_Masked, true, + TAG_DONE); + + V_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); + + spriteframe_t *sprframe; + fixed_t scaleX, scaleY; + + if (mSkin == 0) + { + sprframe = &SpriteFrames[sprites[mPlayerState->sprite].spriteframes + mPlayerState->GetFrame()]; + scaleX = GetDefaultByType(mPlayerClass->Type)->scaleX; + scaleY = GetDefaultByType(mPlayerClass->Type)->scaleY; + } + else + { + sprframe = &SpriteFrames[sprites[skins[mSkin].sprite].spriteframes + mPlayerState->GetFrame()]; + scaleX = skins[mSkin].ScaleX; + scaleY = skins[mSkin].ScaleY; + } + + if (sprframe != NULL) + { + FTexture *tex = TexMan(sprframe->Texture[mRotation]); + if (tex != NULL && tex->UseType != FTexture::TEX_Null) + { + FRemapTable *trans = NULL; + if (mTranslate) trans = translationtables[TRANSLATION_Players](MAXPLAYERS); + screen->DrawTexture (tex, + x + 36*CleanXfac, y + 71*CleanYfac, + DTA_DestWidth, MulScale16 (tex->GetWidth() * CleanXfac, scaleX), + DTA_DestHeight, MulScale16 (tex->GetHeight() * CleanYfac, scaleY), + DTA_Translation, trans, + TAG_DONE); + } + } +} + diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp new file mode 100644 index 000000000..b25b86f1a --- /dev/null +++ b/src/menu/playermenu.cpp @@ -0,0 +1,1142 @@ +/* +** playermenu.cpp +** The player setup menu +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu/menu.h" +#include "v_video.h" +#include "v_font.h" +#include "gi.h" +#include "gstrings.h" +#include "d_player.h" +#include "d_event.h" +#include "d_gui.h" +#include "c_dispatch.h" +#include "teaminfo.h" +#include "v_palette.h" +#include "r_state.h" +#include "r_translate.h" +#include "v_text.h" + +EXTERN_CVAR (String, playerclass) +EXTERN_CVAR (String, name) +EXTERN_CVAR (Int, team) +EXTERN_CVAR (Float, autoaim) +EXTERN_CVAR(Bool, neverswitchonpickup) +EXTERN_CVAR (Bool, cl_run) + +void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayerSkin *skin, FRemapTable *table); + +//============================================================================= +// +// Player's name +// +//============================================================================= + +FPlayerNameBox::FPlayerNameBox(int x, int y, int height, int frameofs, const char *text, FFont *font, EColorRange color, FName action) +: FListMenuItemSelectable(x, y, height, action) +{ + mText = copystring(text); + mFont = font; + mFontColor = color; + mFrameSize = frameofs; + mPlayerName[0] = 0; + mEntering = false; +} + +FPlayerNameBox::~FPlayerNameBox() +{ + if (mText != NULL) delete [] mText; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FPlayerNameBox::SetString(int i, const char *s) +{ + if (i == 0) + { + strncpy(mPlayerName, s, MAXPLAYERNAME); + mPlayerName[MAXPLAYERNAME] = 0; + return true; + } + return false; +} + +bool FPlayerNameBox::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 FPlayerNameBox::DrawBorder (int x, int y, int len) +{ + if (gameinfo.gametype & (GAME_DoomStrifeChex)) + { + int i; + + screen->DrawTexture (TexMan["M_LSLEFT"], x-8, y+7, DTA_Clean, true, TAG_DONE); + + for (i = 0; i < len; i++) + { + screen->DrawTexture (TexMan["M_LSCNTR"], x, y+7, DTA_Clean, true, TAG_DONE); + x += 8; + } + + screen->DrawTexture (TexMan["M_LSRGHT"], x, y+7, DTA_Clean, true, TAG_DONE); + } + else + { + screen->DrawTexture (TexMan["M_FSLOT"], x, y+1, DTA_Clean, true, TAG_DONE); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void FPlayerNameBox::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] = (gameinfo.gametype & (GAME_DoomStrifeChex)) ? '_' : '['; + + screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, mEditName, + DTA_Clean, true, TAG_DONE); + + mEditName[l] = 0; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FPlayerNameBox::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 +// +//============================================================================= + +FValueTextItem::FValueTextItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, EColorRange valuecolor, FName action, FName values) +: FListMenuItemSelectable(x, y, height, action) +{ + mText = copystring(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); + } + } + } +} + +FValueTextItem::~FValueTextItem() +{ + if (mText != NULL) delete [] mText; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FValueTextItem::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 FValueTextItem::SetValue(int i, int value) +{ + if (i == 0) + { + mSelection = value; + return true; + } + return false; +} + +bool FValueTextItem::GetValue(int i, int *pvalue) +{ + if (i == 0) + { + *pvalue = mSelection; + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FValueTextItem::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 false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void FValueTextItem::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) screen->DrawText(mFont, mFontColor2, x, mYpos, mSelections[mSelection], DTA_Clean, true, TAG_DONE); +} + +//============================================================================= +// +// items for the player menu +// +//============================================================================= + +FSliderItem::FSliderItem(int x, int y, int height, const char *text, FFont *font, EColorRange color, FName action, int min, int max, int step) +: FListMenuItemSelectable(x, y, height, action) +{ + mText = copystring(text); + mFont = font; + mFontColor = color; + mSelection = 0; + mMinrange = min; + mMaxrange = max; + mStep = step; +} + +FSliderItem::~FSliderItem() +{ + if (mText != NULL) delete [] mText; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FSliderItem::SetValue(int i, int value) +{ + if (i == 0) + { + mSelection = value; + return true; + } + return false; +} + +bool FSliderItem::GetValue(int i, int *pvalue) +{ + if (i == 0) + { + *pvalue = mSelection; + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool FSliderItem::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 FSliderItem::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 FSliderItem::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 FSliderItem::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; + DrawSlider (x, mYpos); +} + + +//============================================================================= +// +// +// +//============================================================================= + +class DPlayerMenu : public DListMenu +{ + DECLARE_CLASS(DPlayerMenu, DListMenu) + + int PlayerClassIndex; + FPlayerClass *PlayerClass; + TArray PlayerColorSets; + TArray PlayerSkins; + int mRotation; + + void PickPlayerClass (); + void UpdateColorsets(); + void UpdateSkins(); + void UpdateTranslation(); + void SendNewColor (int red, int green, int blue); + + void PlayerNameChanged(FListMenuItem *li); + void ColorSetChanged (FListMenuItem *li); + void ClassChanged (FListMenuItem *li); + void AutoaimChanged (FListMenuItem *li); + void SkinChanged (FListMenuItem *li); + + +public: + + DPlayerMenu() {} + void Init(DMenu *parent, FListMenuDescriptor *desc); + bool Responder (event_t *ev); + bool MenuEvent (int mkey, bool fromcontroller); + bool MouseEvent(int type, int x, int y); + void Ticker (); + void Drawer (); +}; + +IMPLEMENT_CLASS(DPlayerMenu) + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::Init(DMenu *parent, FListMenuDescriptor *desc) +{ + FListMenuItem *li; + + Super::Init(parent, desc); + PickPlayerClass(); + mRotation = 0; + + li = GetItem(NAME_Playerdisplay); + if (li != NULL) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_ROTATION, 0); + li->SetValue(FListMenuItemPlayerDisplay::PDF_MODE, 1); + li->SetValue(FListMenuItemPlayerDisplay::PDF_TRANSLATE, 1); + li->SetValue(FListMenuItemPlayerDisplay::PDF_CLASS, players[consoleplayer].userinfo.PlayerClass); + if (PlayerClass != NULL && !(GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN) && + players[consoleplayer].userinfo.PlayerClass != -1) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_SKIN, players[consoleplayer].userinfo.skin); + } + } + + li = GetItem(NAME_Playerbox); + if (li != NULL) + { + li->SetString(0, name); + } + + li = GetItem(NAME_Team); + if (li != NULL) + { + li->SetString(0, "None"); + for(unsigned i=0;iSetString(i+1, Teams[i].GetName()); + } + li->SetValue(0, team == TEAM_NONE? 0 : team + 1); + } + + int mycolorset = players[consoleplayer].userinfo.colorset; + int color = players[consoleplayer].userinfo.color; + + UpdateColorsets(); + + li = GetItem(NAME_Red); + if (li != NULL) + { + li->Enable(mycolorset == -1); + li->SetValue(0, RPART(color)); + } + + li = GetItem(NAME_Green); + if (li != NULL) + { + li->Enable(mycolorset == -1); + li->SetValue(0, GPART(color)); + } + + li = GetItem(NAME_Blue); + if (li != NULL) + { + li->Enable(mycolorset == -1); + li->SetValue(0, BPART(color)); + } + + li = GetItem(NAME_Class); + if (li != NULL) + { + if (PlayerClasses.Size() == 1) + { + li->SetString(0, PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName)); + li->SetValue(0, 0); + } + else + { + li->SetString(0, "Random"); + for(unsigned i=0; i< PlayerClasses.Size(); i++) + { + const char *cls = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + li->SetString(i+1, cls); + } + li->SetValue(0, players[consoleplayer].userinfo.PlayerClass + 1); + } + } + + UpdateSkins(); + + li = GetItem(NAME_Gender); + if (li != NULL) + { + li->SetValue(0, players[consoleplayer].userinfo.gender); + } + + li = GetItem(NAME_Autoaim); + if (li != NULL) + { + int sel = + autoaim == 0 ? 0 : + autoaim <= 0.25 ? 1 : + autoaim <= 0.5 ? 2 : + autoaim <= 1 ? 3 : + autoaim <= 2 ? 4 : + autoaim <= 3 ? 5:6; + li->SetValue(0, sel); + } + + li = GetItem(NAME_Switch); + if (li != NULL) + { + li->SetValue(0, neverswitchonpickup); + } + + li = GetItem(NAME_AlwaysRun); + if (li != NULL) + { + li->SetValue(0, cl_run); + } + + if (mDesc->mSelectedItem < 0) mDesc->mSelectedItem = 1; + +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DPlayerMenu::Responder (event_t *ev) +{ + if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 == ' ') + { + // turn the player sprite around + mRotation = 8 - mRotation; + FListMenuItem *li = GetItem(NAME_Playerdisplay); + if (li != NULL) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_ROTATION, mRotation); + } + return true; + } + return Super::Responder(ev); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::UpdateTranslation() +{ + int PlayerColor = players[consoleplayer].userinfo.color; + int PlayerSkin = players[consoleplayer].userinfo.skin; + int PlayerColorset = players[consoleplayer].userinfo.colorset; + + if (PlayerClass != NULL) + { + PlayerSkin = R_FindSkin (skins[PlayerSkin].name, int(PlayerClass - &PlayerClasses[0])); + R_GetPlayerTranslation(PlayerColor, + P_GetPlayerColorSet(PlayerClass->Type->TypeName, PlayerColorset), + &skins[PlayerSkin], translationtables[TRANSLATION_Players][MAXPLAYERS]); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::PickPlayerClass() +{ + + /* + // What's the point of this? Aren't we supposed to edit the + // userinfo? + if (players[consoleplayer].mo != NULL) + { + PlayerClassIndex = players[consoleplayer].CurrentPlayerClass; + } + else + */ + { + int pclass = 0; + // [GRB] Pick a class from player class list + if (PlayerClasses.Size () > 1) + { + pclass = players[consoleplayer].userinfo.PlayerClass; + + if (pclass < 0) + { + pclass = (MenuTime>>7) % PlayerClasses.Size (); + } + } + PlayerClassIndex = pclass; + } + PlayerClass = &PlayerClasses[PlayerClassIndex]; + UpdateTranslation(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::SendNewColor (int red, int green, int blue) +{ + char command[24]; + + players[consoleplayer].userinfo.color = MAKERGB(red, green, blue); + mysnprintf (command, countof(command), "color \"%02x %02x %02x\"", red, green, blue); + C_DoCommand (command); + UpdateTranslation(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::UpdateColorsets() +{ + FListMenuItem *li = GetItem(NAME_Color); + if (li != NULL) + { + int sel = 0; + P_EnumPlayerColorSets(PlayerClass->Type->TypeName, &PlayerColorSets); + li->SetString(0, "Custom"); + for(unsigned i=0;iType->TypeName, PlayerColorSets[i]); + li->SetString(i+1, colorset->Name); + } + int mycolorset = players[consoleplayer].userinfo.colorset; + if (mycolorset != -1) + { + for(unsigned i=0;iSetValue(0, sel); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::UpdateSkins() +{ + int sel = 0; + int skin; + FListMenuItem *li = GetItem(NAME_Skin); + if (li != NULL) + { + if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || + players[consoleplayer].userinfo.PlayerClass == -1) + { + li->SetString(0, "Base"); + li->SetValue(0, 0); + skin = 0; + } + else + { + PlayerSkins.Clear(); + for(unsigned i=0;i<(unsigned)numskins; i++) + { + if (PlayerClass->CheckSkin(i)) + { + int j = PlayerSkins.Push(i); + li->SetString(j, skins[i].name); + if (players[consoleplayer].userinfo.skin == i) + { + sel = j; + } + } + } + li->SetValue(0, sel); + skin = PlayerSkins[sel]; + } + li = GetItem(NAME_Playerdisplay); + if (li != NULL) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_SKIN, skin); + } + } + UpdateTranslation(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::PlayerNameChanged(FListMenuItem *li) +{ + char pp[MAXPLAYERNAME+1]; + const char *p; + if (li->GetString(0, pp, MAXPLAYERNAME)) + { + FString command("name \""); + + // Escape any backslashes or quotation marks before sending the name to the console. + for (p = pp; *p != '\0'; ++p) + { + if (*p == '"' || *p == '\\') + { + command << '\\'; + } + command << *p; + } + command << '"'; + C_DoCommand (command); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::ColorSetChanged (FListMenuItem *li) +{ + int sel; + + if (li->GetValue(0, &sel)) + { + int mycolorset = -1; + + if (sel > 0) mycolorset = PlayerColorSets[sel-1]; + + FListMenuItem *red = GetItem(NAME_Red); + FListMenuItem *green = GetItem(NAME_Green); + FListMenuItem *blue = GetItem(NAME_Blue); + + // disable the sliders if a valid colorset is selected + if (red != NULL) red->Enable(mycolorset == -1); + if (green != NULL) green->Enable(mycolorset == -1); + if (blue != NULL) blue->Enable(mycolorset == -1); + + char command[24]; + players[consoleplayer].userinfo.colorset = mycolorset; + mysnprintf(command, countof(command), "colorset %d", mycolorset); + C_DoCommand(command); + UpdateTranslation(); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::ClassChanged (FListMenuItem *li) +{ + if (PlayerClasses.Size () == 1) + { + return; + } + + int sel; + + if (li->GetValue(0, &sel)) + { + players[consoleplayer].userinfo.PlayerClass = sel-1; + + cvar_set ("playerclass", + sel == 0 ? "Random" : PlayerClass->Type->Meta.GetMetaString (APMETA_DisplayName)); + + PickPlayerClass(); + UpdateSkins(); + UpdateColorsets(); + UpdateTranslation(); + + li = GetItem(NAME_Playerdisplay); + if (li != NULL) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_CLASS, players[consoleplayer].userinfo.PlayerClass); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::SkinChanged (FListMenuItem *li) +{ + if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || + players[consoleplayer].userinfo.PlayerClass == -1) + { + return; + } + + int sel; + + if (li->GetValue(0, &sel)) + { + sel = PlayerSkins[sel]; + players[consoleplayer].userinfo.skin = sel; + UpdateTranslation(); + cvar_set ("skin", skins[sel].name); + + li = GetItem(NAME_Playerdisplay); + if (li != NULL) + { + li->SetValue(FListMenuItemPlayerDisplay::PDF_SKIN, sel); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::AutoaimChanged (FListMenuItem *li) +{ + static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 }; + + int sel; + + if (li->GetValue(0, &sel)) + { + autoaim = ranges[sel]; + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DPlayerMenu::MenuEvent (int mkey, bool fromcontroller) +{ + int v; + if (mDesc->mSelectedItem >= 0) + { + FListMenuItem *li = mDesc->mItems[mDesc->mSelectedItem]; + if (li->MenuEvent(mkey, fromcontroller)) + { + FName current = li->GetAction(NULL); + switch(current) + { + // item specific handling comes here + + case NAME_Playerbox: + PlayerNameChanged(li); + break; + + case NAME_Team: + if (li->GetValue(0, &v)) + { + team = v==0? TEAM_NONE : v-1; + } + break; + + case NAME_Color: + ColorSetChanged(li); + break; + + case NAME_Red: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (v, GPART(color), BPART(color)); + } + break; + + case NAME_Green: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (RPART(color), v, BPART(color)); + } + break; + + case NAME_Blue: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (RPART(color), GPART(color), v); + } + break; + + case NAME_Class: + ClassChanged(li); + break; + + case NAME_Skin: + SkinChanged(li); + break; + + case NAME_Gender: + if (li->GetValue(0, &v)) + { + cvar_set ("gender", v==0? "male" : v==1? "female" : "other"); + } + break; + + case NAME_Autoaim: + AutoaimChanged(li); + break; + + case NAME_Switch: + if (li->GetValue(0, &v)) + { + neverswitchonpickup = !!v; + } + break; + + case NAME_AlwaysRun: + if (li->GetValue(0, &v)) + { + cl_run = !!v; + } + break; + + default: + break; + } + return true; + } + } + return Super::MenuEvent(mkey, fromcontroller); +} + + +bool DPlayerMenu::MouseEvent(int type, int x, int y) +{ + int v; + FListMenuItem *li = mFocusControl; + bool res = Super::MouseEvent(type, x, y); + if (li == NULL) li = mFocusControl; + if (li != NULL) + { + // Check if the colors have changed + FName current = li->GetAction(NULL); + switch(current) + { + case NAME_Red: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (v, GPART(color), BPART(color)); + } + break; + + case NAME_Green: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (RPART(color), v, BPART(color)); + } + break; + + case NAME_Blue: + if (li->GetValue(0, &v)) + { + int color = players[consoleplayer].userinfo.color; + SendNewColor (RPART(color), GPART(color), v); + } + break; + } + } + return res; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::Ticker () +{ + + Super::Ticker(); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DPlayerMenu::Drawer () +{ + + Super::Drawer(); + + const char *str = "PRESS " TEXTCOLOR_WHITE "SPACE"; + screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 - + SmallFont->StringWidth (str)/2, + 50 + 48 + 70, str, + DTA_Clean, true, TAG_DONE); + str = mRotation ? "TO SEE FRONT" : "TO SEE BACK"; + screen->DrawText (SmallFont, CR_GOLD, 320 - 32 - 32 - + SmallFont->StringWidth (str)/2, + 50 + 48 + 70 + SmallFont->GetHeight (), str, + DTA_Clean, true, TAG_DONE); + +} diff --git a/src/menu/readthis.cpp b/src/menu/readthis.cpp new file mode 100644 index 000000000..388dca716 --- /dev/null +++ b/src/menu/readthis.cpp @@ -0,0 +1,154 @@ +/* +** readthis.cpp +** Help screens +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "menu/menu.h" +#include "v_video.h" +#include "g_level.h" +#include "gi.h" +#include "textures/textures.h" + +class DReadThisMenu : public DMenu +{ + DECLARE_CLASS(DReadThisMenu, DMenu) + int mScreen; + int mInfoTic; + +public: + + DReadThisMenu(DMenu *parent = NULL); + void Drawer(); + bool MenuEvent(int mkey, bool fromcontroller); + bool DimAllowed () { return false; } + bool MouseEvent(int type, int x, int y); +}; + +IMPLEMENT_CLASS(DReadThisMenu) + +//============================================================================= +// +// Read This Menus +// +//============================================================================= + +DReadThisMenu::DReadThisMenu(DMenu *parent) +: DMenu(parent) +{ + mScreen = 1; + mInfoTic = gametic; +} + + +//============================================================================= +// +// +// +//============================================================================= + +void DReadThisMenu::Drawer() +{ + FTexture *tex = NULL, *prevpic = NULL; + fixed_t alpha; + + // Did the mapper choose a custom help page via MAPINFO? + if ((level.info != NULL) && level.info->f1[0] != 0) + { + tex = TexMan.FindTexture(level.info->f1); + mScreen = 1; + } + + if (tex == NULL) + { + tex = TexMan[gameinfo.infoPages[mScreen-1].GetChars()]; + } + + if (mScreen > 1) + { + prevpic = TexMan[gameinfo.infoPages[mScreen-2].GetChars()]; + } + + alpha = MIN (Scale (gametic - mInfoTic, OPAQUE, TICRATE/3), OPAQUE); + if (alpha < OPAQUE && prevpic != NULL) + { + screen->DrawTexture (prevpic, 0, 0, + DTA_DestWidth, screen->GetWidth(), + DTA_DestHeight, screen->GetHeight(), + TAG_DONE); + } + screen->DrawTexture (tex, 0, 0, + DTA_DestWidth, screen->GetWidth(), + DTA_DestHeight, screen->GetHeight(), + DTA_Alpha, alpha, + TAG_DONE); + +} + + +//============================================================================= +// +// +// +//============================================================================= + +bool DReadThisMenu::MenuEvent(int mkey, bool fromcontroller) +{ + if (mkey == MKEY_Enter) + { + S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE); + mScreen++; + mInfoTic = gametic; + if ((level.info != NULL && level.info->f1[0] != 0) || mScreen > int(gameinfo.infoPages.Size())) + { + Close(); + } + return true; + } + else return Super::MenuEvent(mkey, fromcontroller); +} + +//============================================================================= +// +// +// +//============================================================================= + +bool DReadThisMenu::MouseEvent(int type, int x, int y) +{ + if (type == MOUSE_Click) + { + return MenuEvent(MKEY_Enter, true); + } + return false; +} + diff --git a/src/menu/videomenu.cpp b/src/menu/videomenu.cpp new file mode 100644 index 000000000..0d1590463 --- /dev/null +++ b/src/menu/videomenu.cpp @@ -0,0 +1,442 @@ +/* +** videomenu.cpp +** The video modes menu +** +**--------------------------------------------------------------------------- +** Copyright 2001-2010 Randy Heit +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#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" +#include "i_music.h" +#include "m_joy.h" +#include "sbar.h" +#include "hardware.h" + +#define NO_IMP +#include "optionmenuitems.h" + + +/*======================================= + * + * Video Modes Menu + * + *=======================================*/ +static void BuildModesList (int hiwidth, int hiheight, int hi_id); +static bool GetSelectedSize (int *width, int *height); +static void SetModesMenu (int w, int h, int bits); +FOptionMenuDescriptor *GetVideoModeMenu(); + +extern bool setmodeneeded; +extern int NewWidth, NewHeight, NewBits; +extern int DisplayBits; + +EXTERN_CVAR (Int, vid_defwidth) +EXTERN_CVAR (Int, vid_defheight) +EXTERN_CVAR (Int, vid_defbits) +EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Bool, vid_tft) // Defined below + +int testingmode; // Holds time to revert to old mode +int OldWidth, OldHeight, OldBits; +static FIntCVar DummyDepthCvar (NULL, 0, 0); +static BYTE BitTranslate[32]; + +CUSTOM_CVAR (Int, menu_screenratios, 0, CVAR_ARCHIVE) +{ + if (self < 0 || self > 4) + { + self = 3; + } + else if (self == 4 && !vid_tft) + { + self = 3; + } + else + { + BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); + } +} + +CUSTOM_CVAR (Bool, vid_tft, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + FOptionMenuDescriptor *opt = GetVideoModeMenu(); + if (opt != NULL) + { + FOptionMenuItem *it = opt->GetItem("menu_screenratios"); + if (it != NULL) + { + if (self) + { + it->SetString(FOptionMenuItemOptionBase::OP_VALUES, "RatiosTFT"); + } + else + { + it->SetString(FOptionMenuItemOptionBase::OP_VALUES, "Ratios"); + } + } + } + setsizeneeded = true; + if (StatusBar != NULL) + { + StatusBar->ScreenSizeChanged(); + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +class DVideoModeMenu : public DOptionMenu +{ + DECLARE_CLASS(DVideoModeMenu, DOptionMenu) + +public: + + DVideoModeMenu() + { + SetModesMenu (SCREENWIDTH, SCREENHEIGHT, 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(FOptionMenuScreenResolutionLine::SRL_SELECTION, &sel); + bool res = Super::MenuEvent(mkey, fromcontroller); + if (selected) mDesc->mItems[mDesc->mSelectedItem]->SetValue(FOptionMenuScreenResolutionLine::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 = SCREENWIDTH; + NewHeight = SCREENHEIGHT; + } + OldWidth = SCREENWIDTH; + OldHeight = SCREENHEIGHT; + 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); + } +}; + +IMPLEMENT_CLASS(DVideoModeMenu) + + +//============================================================================= +// +// +// +//============================================================================= + +FOptionMenuDescriptor *GetVideoModeMenu() +{ + FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_VideoModeMenu); + if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu) + { + return (FOptionMenuDescriptor *)*desc; + } + return NULL; +} + +//============================================================================= +// +// Set some stuff up for the video modes menu +// +//============================================================================= + +static void BuildModesList (int hiwidth, int hiheight, int hi_bits) +{ + char strtemp[32]; + int i, c; + int width, height, showbits; + bool letterbox=false; + int ratiomatch; + + if (menu_screenratios >= 0 && menu_screenratios <= 4 && menu_screenratios != 3) + { + ratiomatch = menu_screenratios; + } + else + { + ratiomatch = -1; + } + showbits = BitTranslate[DummyDepthCvar]; + + if (Video != NULL) + { + Video->StartModeIterator (showbits, screen->IsFullscreen()); + } + + FOptionMenuDescriptor *opt = GetVideoModeMenu(); + if (opt != NULL) + { + for (i = NAME_res_0; i<= NAME_res_9; i++) + { + FOptionMenuItem *it = opt->GetItem((ENamedName)i); + if (it != NULL) + { + it->SetValue(FOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, -1); + for (c = 0; c < 3; c++) + { + bool haveMode = false; + + if (Video != NULL) + { + while ((haveMode = Video->NextMode (&width, &height, &letterbox)) && + (ratiomatch >= 0 && CheckRatio (width, height) != ratiomatch)) + { + } + } + + if (haveMode) + { + if (width == hiwidth && height == hiheight) + { + it->SetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, c); + it->SetValue(FOptionMenuScreenResolutionLine::SRL_HIGHLIGHT, c); + } + + mysnprintf (strtemp, countof(strtemp), "%dx%d%s", width, height, letterbox?TEXTCOLOR_BROWN" LB":""); + it->SetString(FOptionMenuScreenResolutionLine::SRL_INDEX+c, strtemp); + } + else + { + it->SetString(FOptionMenuScreenResolutionLine::SRL_INDEX+c, ""); + } + } + } + } + } +} + + +//============================================================================= +// +// +// +//============================================================================= + +void M_RestoreMode () +{ + NewWidth = OldWidth; + NewHeight = OldHeight; + NewBits = OldBits; + setmodeneeded = true; + testingmode = 0; + SetModesMenu (OldWidth, OldHeight, OldBits); +} + +void M_SetDefaultMode () +{ + // Make current resolution the default + vid_defwidth = SCREENWIDTH; + vid_defheight = SCREENHEIGHT; + vid_defbits = DisplayBits; + testingmode = 0; + SetModesMenu (SCREENWIDTH, SCREENHEIGHT, DisplayBits); +} + + + +//============================================================================= +// +// +// +//============================================================================= + +void M_RefreshModesList () +{ + BuildModesList (SCREENWIDTH, SCREENHEIGHT, DisplayBits); +} + +void M_InitVideoModesMenu () +{ + int dummy1, dummy2; + size_t currval = 0; + + M_RefreshModesList(); + + for (unsigned int i = 1; i <= 32 && currval < countof(BitTranslate); i++) + { + Video->StartModeIterator (i, screen->IsFullscreen()); + if (Video->NextMode (&dummy1, &dummy2, NULL)) + { + BitTranslate[currval++] = i; + } + } + + /* It doesn't look like this can be anything but DISPLAY_Both, regardless of any other settings. + switch (Video->GetDisplayType ()) + { + case DISPLAY_FullscreenOnly: + case DISPLAY_WindowOnly: + // todo: gray out fullscreen option + default: + break; + } + */ +} + +//============================================================================= +// +// +// +//============================================================================= + +static bool GetSelectedSize (int *width, int *height) +{ + FOptionMenuDescriptor *opt = GetVideoModeMenu(); + if (opt != NULL) + { + int line = opt->mSelectedItem; + int hsel; + FOptionMenuItem *it = opt->mItems[line]; + if (it->GetValue(FOptionMenuScreenResolutionLine::SRL_SELECTION, &hsel)) + { + char buffer[32]; + char *breakpt; + if (it->GetString(FOptionMenuScreenResolutionLine::SRL_INDEX+hsel, buffer, sizeof(buffer))) + { + *width = strtol (buffer, &breakpt, 10); + *height = strtol (breakpt+1, NULL, 10); + return true; + } + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void M_SetVideoMode() +{ + if (!GetSelectedSize (&NewWidth, &NewHeight)) + { + NewWidth = SCREENWIDTH; + NewHeight = SCREENHEIGHT; + } + else + { + testingmode = 1; + setmodeneeded = true; + NewBits = BitTranslate[DummyDepthCvar]; + } + SetModesMenu (NewWidth, NewHeight, NewBits); +} + +//============================================================================= +// +// +// +//============================================================================= + +static int FindBits (int bits) +{ + int i; + + for (i = 0; i < 22; i++) + { + if (BitTranslate[i] == bits) + return i; + } + + return 0; +} + +static void SetModesMenu (int w, int h, int bits) +{ + DummyDepthCvar = FindBits (bits); + + FOptionMenuDescriptor *opt = GetVideoModeMenu(); + if (opt != NULL) + { + FOptionMenuItem *it; + if (testingmode <= 1) + { + it = opt->GetItem(NAME_VMEnterText); + if (it != NULL) it->SetValue(0, 0); + it = opt->GetItem(NAME_VMTestText); + if (it != NULL) it->SetValue(0, 0); + } + else + { + + it = opt->GetItem(NAME_VMTestText); + if (it != NULL) it->SetValue(0, 1); + it = opt->GetItem(NAME_VMEnterText); + if (it != NULL) + { + char strtemp[64]; + mysnprintf (strtemp, countof(strtemp), "TESTING %dx%dx%d", w, h, bits); + it->SetValue(0, 1); + it->SetString(0, strtemp); + } + } + } + BuildModesList (w, h, bits); +} diff --git a/src/namedef.h b/src/namedef.h index 99737eda5..9f7e5a87d 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -465,3 +465,64 @@ xx(Dialog) xx(Ifitem) xx(Choice) xx(Link) + +// Special menus +xx(Mainmenu) +xx(Episodemenu) +xx(Playerclassmenu) +xx(HexenDefaultPlayerclassmenu) +xx(Skillmenu) +xx(Startgame) +xx(StartgameConfirm) +xx(Loadgamemenu) +xx(Savegamemenu) +xx(Readthismenu) +xx(Optionsmenu) +xx(Quitmenu) +xx(Savemenu) +xx(Playermenu) + +xx(Playerbox) +xx(Team) +xx(Color) +xx(Red) +xx(Green) +xx(Blue) +xx(Class) +xx(Skin) +xx(Gender) +xx(Autoaim) +xx(Switch) +xx(Playerdisplay) +xx(Controlmessage) +xx(Crosshairs) +xx(Colorpickermenu) +xx(Mididevices) +xx(CustomizeControls) +xx(MessageOptions) +xx(AutomapOptions) +xx(ScoreboardOptions) +xx(MapColorMenu) +xx(GameplayOptions) +xx(CompatibilityOptions) +xx(MouseOptions) +xx(JoystickOptions) +xx(SoundOptions) +xx(AdvSoundOptions) +xx(ModReplayerOptions) +xx(VideoOptions) +xx(JoystickConfigMenu) +xx(VMEnterText) +xx(VMTestText) +xx(VideoModeMenu) +xx(res_0) +xx(res_1) +xx(res_2) +xx(res_3) +xx(res_4) +xx(res_5) +xx(res_6) +xx(res_7) +xx(res_8) +xx(res_9) +xx(AlwaysRun) diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 0d7d311b4..a2c287a42 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -41,7 +41,6 @@ #include "w_wad.h" #include "cmdlib.h" #include "s_sound.h" -#include "m_menu.h" #include "v_text.h" #include "v_video.h" #include "m_random.h" @@ -56,11 +55,13 @@ #include "d_net.h" #include "g_level.h" #include "d_event.h" +#include "d_gui.h" #include "doomstat.h" #include "c_console.h" #include "sbar.h" #include "farchive.h" #include "p_lnspec.h" +#include "menu/menu.h" // The conversations as they exist inside a SCRIPTxx lump. struct Response @@ -112,9 +113,8 @@ typedef TMap FDialogueMap; // maps actor class names to dialogue static FStrifeTypeMap StrifeTypes; static FDialogueIDMap DialogueRoots; static FDialogueMap ClassRoots; +static int ConversationMenuY; -static menu_t ConversationMenu; -static TArray ConversationItems; static int ConversationPauseTic; static bool ShowGold; @@ -123,13 +123,11 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType); static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses); static bool DrawConversationMenu (); -static void PickConversationReply (); +static void PickConversationReply (int replyindex); static void CleanupConversationMenu (); -static void ConversationMenuEscaped (); static void TerminalResponse (const char *str); -static FStrifeDialogueNode *CurNode, *PrevNode; -static FBrokenLines *DialogueLines; +static FStrifeDialogueNode *PrevNode; #define NUM_RANDOM_LINES 10 #define NUM_RANDOM_GOODBYES 3 @@ -233,7 +231,6 @@ void P_FreeStrifeConversations () DialogueRoots.Clear(); ClassRoots.Clear(); - CurNode = NULL; PrevNode = NULL; } @@ -541,10 +538,6 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses) reply->ItemCheck[k].Amount = rsp->Count[k]; } - // ReplyLines is calculated when the menu is shown. It is just Reply - // with word wrap turned on. - reply->ReplyLines = NULL; - // If the first item check has a positive amount required, then // add that to the reply string. Otherwise, use the reply as-is. if (rsp->Count[0] > 0) @@ -615,7 +608,6 @@ FStrifeDialogueReply::~FStrifeDialogueReply () if (Reply != NULL) delete[] Reply; if (QuickYes != NULL) delete[] QuickYes; if (QuickNo != NULL) delete[] QuickNo; - if (ReplyLines != NULL) V_FreeBrokenLines (ReplyLines); } //============================================================================ @@ -703,6 +695,372 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) else if (self > 1.f) self = 1.f; } +//============================================================================ +// +// The conversation menu +// +//============================================================================ + +class DConversationMenu : public DMenu +{ + DECLARE_CLASS(DConversationMenu, DMenu) + + FString mSpeaker; + FBrokenLines *mDialogueLines; + TArray mResponseLines; + TArray mResponses; + bool mShowGold; + FStrifeDialogueNode *mCurNode; + int mYpos; + +public: + static int mSelection; + + //============================================================================= + // + // + // + //============================================================================= + + DConversationMenu(FStrifeDialogueNode *CurNode) + { + menuactive = MENU_OnNoPause; + mCurNode = CurNode; + mDialogueLines = NULL; + mShowGold = false; + + // Format the speaker's message. + const char * toSay = CurNode->Dialogue; + if (strncmp (toSay, "RANDOM_", 7) == 0) + { + FString dlgtext; + + dlgtext.Format("TXT_%s_%02d", toSay, 1+(pr_randomspeech() % NUM_RANDOM_LINES)); + toSay = GStrings[dlgtext]; + if (toSay == NULL) + { + toSay = "Go away!"; // Ok, it's lame - but it doesn't look like an error to the player. ;) + } + } + else + { + // handle string table replacement + if (toSay[0] == '$') + { + toSay = GStrings(toSay + 1); + } + } + if (toSay == NULL) + { + toSay = "."; + } + mDialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay); + + FStrifeDialogueReply *reply; + int i,j; + for (reply = CurNode->Children, i = 1; reply != NULL; reply = reply->Next) + { + if (reply->Reply == NULL) + { + continue; + } + mShowGold |= reply->NeedsGold; + + const char *ReplyText = reply->Reply; + if (ReplyText[0] == '$') + { + ReplyText = GStrings(ReplyText + 1); + } + FBrokenLines *ReplyLines = V_BreakLines (SmallFont, 320-50-10, ReplyText); + + mResponses.Push(mResponseLines.Size()); + for (j = 0; ReplyLines[j].Width >= 0; ++j) + { + mResponseLines.Push(ReplyLines[j].Text); + } + ++i; + V_FreeBrokenLines (ReplyLines); + } + char goodbye[25]; + mysnprintf(goodbye, countof(goodbye), "TXT_RANDOMGOODBYE_%d", 1+(pr_randomspeech() % NUM_RANDOM_GOODBYES)); + const char *goodbyestr = GStrings[goodbye]; + if (goodbyestr == NULL) goodbyestr = "Bye."; + mResponses.Push(mResponseLines.Size()); + mResponseLines.Push(FString(goodbyestr)); + + // Determine where the top of the reply list should be positioned. + i = OptionSettings.mLinespacing; + mYpos = MIN (140, 192 - mResponseLines.Size() * i); + for (i = 0; mDialogueLines[i].Width >= 0; ++i) + { } + i = 44 + i * 10; + if (mYpos - 100 < i - screen->GetHeight() / CleanYfac / 2) + { + mYpos = i - screen->GetHeight() / CleanYfac / 2 + 100; + } + ConversationMenuY = mYpos; + //ConversationMenu.indent = 50; + } + + //============================================================================= + // + // + // + //============================================================================= + + void Destroy() + { + V_FreeBrokenLines(mDialogueLines); + mDialogueLines = NULL; + I_SetMusicVolume (1.f); + } + + bool DimAllowed() + { + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == MKEY_Up) + { + if (--mSelection < 0) mSelection = mResponses.Size() - 1; + return true; + } + else if (mkey == MKEY_Down) + { + if (++mSelection >= (int)mResponses.Size()) mSelection = 0; + return true; + } + else if (mkey == MKEY_Back) + { + Close(); + return true; + } + else if (mkey == MKEY_Enter) + { + if ((unsigned)mSelection >= mResponses.Size()) + { + Net_WriteByte(DEM_CONVCLOSE); + } + else + { + // Send dialogue and reply numbers across the wire. + assert((unsigned)mCurNode->ThisNodeNum < StrifeDialogues.Size()); + assert(StrifeDialogues[mCurNode->ThisNodeNum] == mCurNode); + Net_WriteByte(DEM_CONVREPLY); + Net_WriteWord(mCurNode->ThisNodeNum); + Net_WriteByte(mSelection); + } + Close(); + return true; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + bool MouseEvent(int type, int x, int y) + { + int sel = -1; + int fh = SmallFont->GetHeight(); + + // convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture + x = ((x - (screen->GetWidth() / 2)) / CleanXfac) + 160; + y = ((y - (screen->GetHeight() / 2)) / CleanYfac) + 100; + + if (x >= 24 && x <= 320-24 && y >= mYpos && y < mYpos + fh * (int)mResponseLines.Size()) + { + sel = (y - mYpos) / fh; + for(unsigned i=0;i sel) + { + sel = i-1; + break; + } + } + } + if (sel != -1 && sel != mSelection) + { + //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); + } + mSelection = sel; + if (type == MOUSE_Release) + { + return MenuEvent(MKEY_Enter, true); + } + return true; + } + + + //============================================================================= + // + // + // + //============================================================================= + + bool Responder(event_t *ev) + { + if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_Char && ev->data1 >= '0' && ev->data1 <= '9') + { // Activate an item of type numberedmore (dialogue only) + mSelection = ev->data1 == '0' ? 10 : ev->data1 - '0'; + return MenuEvent(MKEY_Enter, false); + } + return Super::Responder(ev); + } + + //============================================================================ + // + // DrawConversationMenu + // + //============================================================================ + + void Drawer() + { + const char *speakerName; + int i, x, y, linesize; + int width, fontheight; + int labelofs; + + player_t *cp = &players[consoleplayer]; + + assert (mDialogueLines != NULL); + assert (mCurNode != NULL); + + FStrifeDialogueNode *CurNode = mCurNode; + + if (CurNode == NULL) + { + Close (); + return; + } + + // [CW] Freeze the game depending on MAPINFO options. + if (ConversationPauseTic < gametic && !multiplayer && !(level.flags2 & LEVEL2_CONV_SINGLE_UNFREEZE)) + { + menuactive = MENU_On; + } + + if (CurNode->Backdrop.isValid()) + { + screen->DrawTexture (TexMan(CurNode->Backdrop), 0, 0, DTA_320x200, true, TAG_DONE); + } + x = 16 * screen->GetWidth() / 320; + y = 16 * screen->GetHeight() / 200; + linesize = 10 * CleanYfac; + + // Who is talking to you? + if (CurNode->SpeakerName != NULL) + { + speakerName = CurNode->SpeakerName; + } + else + { + speakerName = cp->ConversationNPC->GetTag("Person"); + } + + // Dim the screen behind the dialogue (but only if there is no backdrop). + if (!CurNode->Backdrop.isValid()) + { + for (i = 0; mDialogueLines[i].Width >= 0; ++i) + { } + screen->Dim (0, 0.45f, 14 * screen->GetWidth() / 320, 13 * screen->GetHeight() / 200, + 308 * screen->GetWidth() / 320 - 14 * screen->GetWidth () / 320, + speakerName == NULL ? linesize * i + 6 * CleanYfac + : linesize * i + 6 * CleanYfac + linesize * 3/2); + } + + // Dim the screen behind the PC's choices. + + screen->Dim (0, 0.45f, (24-160) * CleanXfac + screen->GetWidth()/2, + (mYpos - 2 - 100) * CleanYfac + screen->GetHeight()/2, + 272 * CleanXfac, + MIN(mResponseLines.Size() * OptionSettings.mLinespacing + 4, 200 - mYpos) * CleanYfac); + + if (speakerName != NULL) + { + screen->DrawText (SmallFont, CR_WHITE, x, y, speakerName, + DTA_CleanNoMove, true, TAG_DONE); + y += linesize * 3 / 2; + } + x = 24 * screen->GetWidth() / 320; + for (i = 0; mDialogueLines[i].Width >= 0; ++i) + { + screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, mDialogueLines[i].Text, + DTA_CleanNoMove, true, TAG_DONE); + y += linesize; + } + + if (ShowGold) + { + AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); + char goldstr[32]; + + mysnprintf (goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0); + screen->DrawText (SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true, + DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE); + screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), + 3, 190, DTA_320x200, true, + DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE); + screen->DrawText (SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE); + screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), + 2, 189, DTA_320x200, true, TAG_DONE); + } + + y = mYpos; + labelofs = OptionSettings.mLabelOffset; + y -= labelofs; + fontheight = OptionSettings.mLinespacing; + + int response = 0; + for (i = 0; i < (int)mResponseLines.Size(); i++, y += fontheight) + { + width = SmallFont->StringWidth(mResponseLines[i]); + x = 64; + + screen->DrawText (SmallFont, CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true, TAG_DONE); + + if (i == mResponses[response]) + { + char tbuf[16]; + + response++; + mysnprintf (tbuf, countof(tbuf), "%d.", response); + x = 50 - SmallFont->StringWidth (tbuf); + screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE); + + if (response == mSelection+1) + { + int color = ((DMenu::MenuTime%8) < 4) || DMenu::CurrentMenu != this ? CR_RED:CR_GREY; + + x = (50 + 3 - 160) * CleanXfac + screen->GetWidth() / 2; + int yy = (y-1+labelofs - 100) * CleanYfac + screen->GetHeight() / 2; + screen->DrawText (ConFont, color, x, yy, "\xd", + DTA_CellX, 8 * CleanXfac, + DTA_CellY, 8 * CleanYfac, + TAG_DONE); + } + } + } + } + +}; + +IMPLEMENT_ABSTRACT_CLASS(DConversationMenu) +int DConversationMenu::mSelection; // needs to be preserved if the same dialogue is restarted + + //============================================================================ // // P_StartConversation @@ -714,10 +1072,7 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle) { AActor *oldtarget; - FStrifeDialogueReply *reply; - menuitem_t item; - const char *toSay; - int i, j; + int i; // Make sure this is actually a player. if (pc->player == NULL) return; @@ -798,102 +1153,19 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang S_Sound (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM); } - // Set up the menu - ::CurNode = CurNode; // only set the global variable for the consoleplayer - ConversationMenu.PreDraw = DrawConversationMenu; - ConversationMenu.EscapeHandler = ConversationMenuEscaped; + DConversationMenu *cmenu = new DConversationMenu(CurNode); - // Format the speaker's message. - toSay = CurNode->Dialogue; - if (strncmp (toSay, "RANDOM_", 7) == 0) - { - FString dlgtext; - dlgtext.Format("TXT_%s_%02d", toSay, 1+(pr_randomspeech() % NUM_RANDOM_LINES)); - toSay = GStrings[dlgtext]; - if (toSay == NULL) - { - toSay = "Go away!"; // Ok, it's lame - but it doesn't look like an error to the player. ;) - } - } - else - { - // handle string table replacement - if (toSay[0] == '$') - { - toSay = GStrings(toSay + 1); - } - } - if (toSay == NULL) - { - toSay = "."; - } - DialogueLines = V_BreakLines (SmallFont, screen->GetWidth()/CleanXfac - 24*2, toSay); - - // Fill out the possible choices - ShowGold = false; - item.type = numberedmore; - item.e.mfunc = PickConversationReply; - for (reply = CurNode->Children, i = 1; reply != NULL; reply = reply->Next) - { - if (reply->Reply == NULL) - { - continue; - } - ShowGold |= reply->NeedsGold; - reply->ReplyLines = V_BreakLines (SmallFont, 320-50-10, reply->Reply); - for (j = 0; reply->ReplyLines[j].Width >= 0; ++j) - { - item.label = reply->ReplyLines[j].Text.LockBuffer(); - // handle string table replacement - if (item.label[0] == '$') - { - item.label = GStrings(item.label + 1); - } - - item.b.position = j == 0 ? i : 0; - item.c.extra = reply; - ConversationItems.Push (item); - } - ++i; - } - char goodbye[25]; - mysnprintf(goodbye, countof(goodbye), "TXT_RANDOMGOODBYE_%d", 1+(pr_randomspeech() % NUM_RANDOM_GOODBYES)); - item.label = (char*)GStrings[goodbye]; - if (item.label == NULL) item.label = "Bye."; - item.b.position = i; - item.c.extra = NULL; - ConversationItems.Push (item); - - // Determine where the top of the reply list should be positioned. - i = (gameinfo.gametype & GAME_Raven) ? 9 : 8; - ConversationMenu.y = MIN (140, 192 - ConversationItems.Size() * i); - for (i = 0; DialogueLines[i].Width >= 0; ++i) - { } - i = 44 + i * 10; - if (ConversationMenu.y - 100 < i - screen->GetHeight() / CleanYfac / 2) - { - ConversationMenu.y = i - screen->GetHeight() / CleanYfac / 2 + 100; - } - ConversationMenu.indent = 50; - - // Finish setting up the menu - ConversationMenu.items = &ConversationItems[0]; - ConversationMenu.numitems = ConversationItems.Size(); if (CurNode != PrevNode) { // Only reset the selection if showing a different menu. - ConversationMenu.lastOn = 0; + DConversationMenu::mSelection = 0; PrevNode = CurNode; } - ConversationMenu.DontDim = true; // And open the menu M_StartControlPanel (false); - OptionsActive = true; - menuactive = MENU_OnNoPause; + M_ActivateMenu(cmenu); ConversationPauseTic = gametic + 20; - - M_SwitchMenu (&ConversationMenu); } } @@ -921,193 +1193,6 @@ void P_ResumeConversation () } } -//============================================================================ -// -// DrawConversationMenu -// -//============================================================================ - -static bool DrawConversationMenu () -{ - const char *speakerName; - int i, x, y, linesize; - int width, fontheight; - menuitem_t *item; - int labelofs; - - player_t *cp = &players[consoleplayer]; - - assert (DialogueLines != NULL); - assert (CurNode != NULL); - - if (CurNode == NULL) - { - M_ClearMenus (); - return true; - } - - // [CW] Freeze the game depending on MAPINFO options. - if (ConversationPauseTic < gametic && !multiplayer && !(level.flags2 & LEVEL2_CONV_SINGLE_UNFREEZE)) - { - menuactive = MENU_On; - } - - if (CurNode->Backdrop.isValid()) - { - screen->DrawTexture (TexMan(CurNode->Backdrop), 0, 0, DTA_320x200, true, TAG_DONE); - } - x = 16 * screen->GetWidth() / 320; - y = 16 * screen->GetHeight() / 200; - linesize = 10 * CleanYfac; - - // Who is talking to you? - if (CurNode->SpeakerName != NULL) - { - speakerName = CurNode->SpeakerName; - } - else - { - speakerName = cp->ConversationNPC->GetTag("Person"); - } - - // Dim the screen behind the dialogue (but only if there is no backdrop). - if (!CurNode->Backdrop.isValid()) - { - for (i = 0; DialogueLines[i].Width >= 0; ++i) - { } - screen->Dim (0, 0.45f, 14 * screen->GetWidth() / 320, 13 * screen->GetHeight() / 200, - 308 * screen->GetWidth() / 320 - 14 * screen->GetWidth () / 320, - speakerName == NULL ? linesize * i + 6 * CleanYfac - : linesize * i + 6 * CleanYfac + linesize * 3/2); - } - - // Dim the screen behind the PC's choices. - screen->Dim (0, 0.45f, (24-160) * CleanXfac + screen->GetWidth()/2, - (ConversationMenu.y - 2 - 100) * CleanYfac + screen->GetHeight()/2, - 272 * CleanXfac, - MIN(ConversationMenu.numitems * (gameinfo.gametype & GAME_Raven ? 9 : 8) + 4, - 200 - ConversationMenu.y) * CleanYfac); - - if (speakerName != NULL) - { - screen->DrawText (SmallFont, CR_WHITE, x, y, speakerName, - DTA_CleanNoMove, true, TAG_DONE); - y += linesize * 3 / 2; - } - x = 24 * screen->GetWidth() / 320; - for (i = 0; DialogueLines[i].Width >= 0; ++i) - { - screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, DialogueLines[i].Text, - DTA_CleanNoMove, true, TAG_DONE); - y += linesize; - } - - if (ShowGold) - { - AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); - char goldstr[32]; - - mysnprintf (goldstr, countof(goldstr), "%d", coin != NULL ? coin->Amount : 0); - screen->DrawText (SmallFont, CR_GRAY, 21, 191, goldstr, DTA_320x200, true, - DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE); - screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), - 3, 190, DTA_320x200, true, - DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, TAG_DONE); - screen->DrawText (SmallFont, CR_GRAY, 20, 190, goldstr, DTA_320x200, true, TAG_DONE); - screen->DrawTexture (TexMan(((AInventory *)GetDefaultByType (RUNTIME_CLASS(ACoin)))->Icon), - 2, 189, DTA_320x200, true, TAG_DONE); - } - - y = CurrentMenu->y; - - if (gameinfo.gametype & GAME_Raven) - { - labelofs = 2; - y -= 2; - fontheight = 9; - } - else - { - labelofs = 0; - fontheight = 8; - } - for (i = 0; i < CurrentMenu->numitems; i++, y += fontheight) - { - item = CurrentMenu->items + i; - - width = SmallFont->StringWidth(item->label); - x = CurrentMenu->indent + 14; - - screen->DrawText (SmallFont, CR_GREEN, x, y, item->label, DTA_Clean, true, TAG_DONE); - - if (item->b.position != 0) - { - char tbuf[16]; - - mysnprintf (tbuf, countof(tbuf), "%d.", item->b.position); - x = CurrentMenu->indent - SmallFont->StringWidth (tbuf); - screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE); - } - - if (i == CurrentItem && - (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) - { - int x = (CurrentMenu->indent + 3 - 160) * CleanXfac + screen->GetWidth() / 2; - int yy = (y-1+labelofs - 100) * CleanYfac + screen->GetHeight() / 2; - screen->DrawText (ConFont, CR_RED, x, yy, "\xd", - DTA_CellX, 8 * CleanXfac, - DTA_CellY, 8 * CleanYfac, - TAG_DONE); - } - } - return true; -} - - -//============================================================================ -// -// PickConversationReply -// -// Run only on the local machine with the conversation menu up. -// -//============================================================================ - -static void PickConversationReply () -{ - FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra; - FStrifeDialogueReply *replyscan; - int replynum = 0; - - assert(CurNode->ThisNodeNum >= 0 && CurNode->ThisNodeNum < 65536); - assert(StrifeDialogues[CurNode->ThisNodeNum] == CurNode); - - // Determine reply number for netcode. - if (reply == NULL) - { - replyscan = NULL; - } - else - { - for (replyscan = CurNode->Children; replyscan != NULL && replyscan != reply; ++replynum, replyscan = replyscan->Next) - { } - } - - M_ClearMenus (); - if (replyscan == NULL) - { - Net_WriteByte(DEM_CONVCLOSE); - } - else - { - // Send dialogue and reply numbers across the wire. - assert(replynum < 256); - Net_WriteByte(DEM_CONVREPLY); - Net_WriteWord(CurNode->ThisNodeNum); - Net_WriteByte(replynum); - } - CleanupConversationMenu (); -} - //============================================================================ // // HandleReply @@ -1307,27 +1392,6 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply void CleanupConversationMenu () { - FStrifeDialogueReply *reply; - - if (CurNode != NULL) - { - for (reply = CurNode->Children; reply != NULL; reply = reply->Next) - { - if (reply->ReplyLines != NULL) - { - V_FreeBrokenLines (reply->ReplyLines); - reply->ReplyLines = NULL; - } - } - CurNode = NULL; - } - if (DialogueLines != NULL) - { - V_FreeBrokenLines (DialogueLines); - DialogueLines = NULL; - } - ConversationItems.Clear (); - I_SetMusicVolume (1.f); } //============================================================================ @@ -1408,7 +1472,7 @@ static void TerminalResponse (const char *str) // their dialogue screen. I think most other conversations use this // only as a response for terminating the dialogue. StatusBar->AttachMessage(new DHUDMessageFadeOut(SmallFont, str, - float(CleanWidth/2) + 0.4f, float(ConversationMenu.y - 110 + CleanHeight/2), CleanWidth, -CleanHeight, + float(CleanWidth/2) + 0.4f, float(ConversationMenuY - 110 + CleanHeight/2), CleanWidth, -CleanHeight, CR_UNTRANSLATED, 3, 1), MAKE_ID('T','A','L','K')); } else diff --git a/src/p_conversation.h b/src/p_conversation.h index 61c0ef8f4..d4fa60009 100644 --- a/src/p_conversation.h +++ b/src/p_conversation.h @@ -48,8 +48,6 @@ struct FStrifeDialogueReply char *LogString; char *QuickNo; bool NeedsGold; - - FBrokenLines *ReplyLines; }; extern TArray StrifeDialogues; diff --git a/src/r_draw.cpp b/src/r_draw.cpp index 4334f6f79..ba77ad2d8 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -1961,7 +1961,7 @@ void R_DrawBorder (int x1, int y1, int x2, int y2) int BorderNeedRefresh; void V_MarkRect (int x, int y, int width, int height); -void M_DrawFrame (int x, int y, int width, int height); +void V_DrawFrame (int x, int y, int width, int height); void R_DrawViewBorder (void) { @@ -1982,7 +1982,7 @@ void R_DrawViewBorder (void) R_DrawBorder (viewwindowx + viewwidth, viewwindowy, SCREENWIDTH, viewheight + viewwindowy); R_DrawBorder (0, viewwindowy + viewheight, SCREENWIDTH, ST_Y); - M_DrawFrame (viewwindowx, viewwindowy, viewwidth, viewheight); + V_DrawFrame (viewwindowx, viewwindowy, viewwidth, viewheight); V_MarkRect (0, 0, SCREENWIDTH, ST_Y); } diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index 3b3823e37..3e94ff045 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -147,6 +147,14 @@ static void I_CheckGUICapture () } } +void I_SetMouseCapture() +{ +} + +void I_ReleaseMouseCapture() +{ +} + static void CenterMouse () { SDL_WarpMouse (screen->GetWidth()/2, screen->GetHeight()/2); diff --git a/src/sdl/i_input.h b/src/sdl/i_input.h index 01d3673c6..124c2ca85 100644 --- a/src/sdl/i_input.h +++ b/src/sdl/i_input.h @@ -3,6 +3,8 @@ void I_PutInClipboard (const char *str); FString I_GetFromClipboard (bool use_primary_selection); +void I_SetMouseCapture(); +void I_ReleaseMouseCapture(); #endif diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index c05ed2027..ff15b1e88 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -778,3 +778,8 @@ unsigned int I_MakeRNGSeed() } return seed; } + +bool I_SetCursor(FTexture *cursorpic) +{ + return false; +} diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index 778af53f5..acdae0167 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -128,6 +128,8 @@ bool I_WriteIniFailed (); unsigned int I_MSTime (void); unsigned int I_FPSTime(); +class FTexture; +bool I_SetCursor(FTexture *); // Directory searching routines diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 88e6f61c0..5b238cd4c 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -37,13 +37,14 @@ #include "doomdef.h" class FileReader; +struct FOptionValues; // // MUSIC I/O // void I_InitMusic (); void I_ShutdownMusic (); -void I_BuildMIDIMenuList (struct value_t **values, float *numValues); +void I_BuildMIDIMenuList (FOptionValues *); void I_UpdateMusic (); // Volume. diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 39f42cc35..97eb88708 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -5,7 +5,7 @@ #include "templates.h" #include "v_text.h" -#include "m_menu.h" +#include "menu/menu.h" static DWORD nummididevices; static bool nummididevicesset; @@ -71,50 +71,43 @@ void I_ShutdownMusicWin32 () } } -void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues) -{ - if (*outValues == NULL) - { - int count = 4 + nummididevices; - value_t *values; - UINT id; - int p = 0; - - *outValues = values = new value_t[count]; - #ifdef HAVE_FLUIDSYNTH - values[p].name = "FluidSynth"; - values[p].value = -5.0; - ++p; +#define NUM_DEF_DEVICES 4 +#else +#define NUM_DEF_DEVICES 3 #endif - values[p].name = "OPL Synth Emulation"; - values[p].value = -3.0; - ++p; - values[p].name = "TiMidity++"; - values[p].value = -2.0; - ++p; - values[p].name = "FMOD"; - values[p].value = -1.0; - ++p; - for (id = 0; id < nummididevices; ++id) +void I_BuildMIDIMenuList (FOptionValues *opt) +{ + int p; + FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(NUM_DEF_DEVICES)]; +#ifdef HAVE_FLUIDSYNTH + pair[0].Text = "FluidSynth"; + pair[0].Value = -5.0; + p = 1; +#else + p = 0; +#endif + pair[p].Text = "OPL Synth Emulation"; + pair[p].Value = -3.0; + pair[p+1].Text = "TiMidity++"; + pair[p+1].Value = -2.0; + pair[p+2].Text = "FMOD"; + pair[p+2].Value = -1.0; + + + for (DWORD id = 0; id < nummididevices; ++id) + { + MIDIOUTCAPS caps; + MMRESULT res; + + res = midiOutGetDevCaps (id, &caps, sizeof(caps)); + assert(res == MMSYSERR_NOERROR); + if (res == MMSYSERR_NOERROR) { - MIDIOUTCAPS caps; - MMRESULT res; - - res = midiOutGetDevCaps (id, &caps, sizeof(caps)); - assert(res == MMSYSERR_NOERROR); - if (res == MMSYSERR_NOERROR) - { - size_t len = strlen (caps.szPname) + 1; - char *name = new char[len]; - - memcpy (name, caps.szPname, len); - values[p].name = name; - values[p].value = (float)id; - ++p; - } + pair = &opt->mValues[opt->mValues.Reserve(1)]; + pair->Text = caps.szPname; + pair->Value = (float)id; } - *numValues = float(p); } } @@ -195,31 +188,28 @@ CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) self = -1; } -void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues) -{ - if (*outValues == NULL) - { - value_t *values; - int p = 0; - - *outValues = values = new value_t[4]; - #ifdef HAVE_FLUIDSYNTH - values[p].name = "FluidSynth"; - values[p].value = -5.0; - ++p; +#define NUM_DEF_DEVICES 4 +#else +#define NUM_DEF_DEVICES 3 #endif - values[p].name = "OPL Synth Emulation"; - values[p].value = -3.0; - ++p; - values[p].name = "TiMidity++"; - values[p].value = -2.0; - ++p; - values[p].name = "FMOD"; - values[p].value = -1.0; - ++p; - *numValues = float(p); - } +void I_BuildMIDIMenuList (FOptionValues *opt) +{ + int p; + FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(NUM_DEF_DEVICES)]; +#ifdef HAVE_FLUIDSYNTH + pair[0].Text = "FluidSynth"; + pair[0].Value = -5.0; + p = 1; +#else + p = 0; +#endif + pair[p].Text = "OPL Synth Emulation"; + pair[p].Value = -3.0; + pair[p+1].Text = "TiMidity++"; + pair[p+1].Value = -2.0; + pair[p+2].Text = "FMOD"; + pair[p+2].Value = -1.0; } CCMD (snd_listmididevices) diff --git a/src/textures/bitmap.h b/src/textures/bitmap.h index 4bd7f32bb..da039fd0f 100644 --- a/src/textures/bitmap.h +++ b/src/textures/bitmap.h @@ -100,6 +100,7 @@ public: Width = w; Height = h; data = new BYTE[4*w*h]; + memset(data, 0, 4*w*h); FreeBuffer = true; ClipRect.x = ClipRect.y = 0; ClipRect.width = w; diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 90c52dd63..4debfa24a 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -189,7 +189,7 @@ protected: private: void CheckForHacks (); - void ParsePatch(FScanner &sc, TexPart & part); + void ParsePatch(FScanner &sc, TexPart & part, bool silent); }; //========================================================================== @@ -970,7 +970,7 @@ void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) // //========================================================================== -void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part) +void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent) { FString patchname; sc.MustGetString(); @@ -1011,7 +1011,7 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part) } if (part.Texture == NULL) { - Printf("Unknown patch '%s' in texture '%s'\n", sc.String, Name); + if (!silent) Printf("Unknown patch '%s' in texture '%s'\n", sc.String, Name); } sc.MustGetStringName(","); sc.MustGetNumber(); @@ -1164,6 +1164,14 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part) bComplex |= (part.op != OP_COPY); bTranslucentPatches = bComplex; } + else if (sc.Compare("useoffsets")) + { + if (part.Texture != NULL) + { + part.OriginX -= part.Texture->LeftOffset; + part.OriginY -= part.Texture->TopOffset; + } + } } } if (Mirror & 2) @@ -1187,10 +1195,23 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) : Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) { TArray parts; + bool bSilent = false; bMultiPatch = true; sc.SetCMode(true); sc.MustGetString(); + if (sc.Compare("optional")) + { + bSilent = true; + sc.MustGetString(); + if (sc.Compare(",")) + { + // this is not right. Apparently a texture named 'optional' is being defined right now... + sc.UnGet(); + sc.String = "optional"; + bSilent = false; + } + } uppercopy(Name, sc.String); Name[8] = 0; sc.MustGetStringName(","); @@ -1231,7 +1252,7 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) else if (sc.Compare("Patch")) { TexPart part; - ParsePatch(sc, part); + ParsePatch(sc, part, bSilent); if (part.Texture != NULL) parts.Push(part); part.Texture = NULL; part.Translation = NULL; diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 1e47dfd86..d41d79891 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -2182,6 +2182,15 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, hexenarmor, FFFFF, PlayerPawn) } } +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY_PREFIX(player, portrait, S, PlayerPawn) +{ + PROP_STRING_PARM(val, 0); + info->Class->Meta.SetMetaString (APMETA_Portrait, val); +} + //========================================================================== // //========================================================================== diff --git a/src/v_draw.cpp b/src/v_draw.cpp index af6b804d1..f22c9c928 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -45,6 +45,7 @@ #include "r_translate.h" #include "doomstat.h" #include "v_palette.h" +#include "gi.h" #include "i_system.h" #include "i_video.h" @@ -1351,3 +1352,35 @@ bool DCanvas::ClipBox (int &x, int &y, int &w, int &h, const BYTE *&src, const i } return false; } + +// Draw a frame around the specified area using the view border +// frame graphics. The border is drawn outside the area, not in it. +void V_DrawFrame (int left, int top, int width, int height) +{ + FTexture *p; + const gameborder_t *border = gameinfo.border; + // Sanity check for incomplete gameinfo + if (border == NULL) + return; + int offset = border->offset; + int right = left + width; + int bottom = top + height; + + // Draw top and bottom sides. + p = TexMan[border->t]; + screen->FlatFill(left, top - p->GetHeight(), right, top, p, true); + p = TexMan[border->b]; + screen->FlatFill(left, bottom, right, bottom + p->GetHeight(), p, true); + + // Draw left and right sides. + p = TexMan[border->l]; + screen->FlatFill(left - p->GetWidth(), top, left, bottom, p, true); + p = TexMan[border->r]; + screen->FlatFill(right, top, right + p->GetWidth(), bottom, p, true); + + // Draw beveled corners. + screen->DrawTexture (TexMan[border->tl], left-offset, top-offset, TAG_DONE); + screen->DrawTexture (TexMan[border->tr], left+width, top-offset, TAG_DONE); + screen->DrawTexture (TexMan[border->bl], left-offset, top+height, TAG_DONE); + screen->DrawTexture (TexMan[border->br], left+width, top+height, TAG_DONE); +} \ No newline at end of file diff --git a/src/v_font.cpp b/src/v_font.cpp index ea6feb149..3815c8ae4 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -2043,6 +2043,15 @@ void V_InitFontColors () while ((lump = Wads.FindLump ("TEXTCOLO", &lastlump)) != -1) { + if (gameinfo.flags & GI_NOTEXTCOLOR) + { + // Chex3 contains a bad TEXTCOLO lump, probably to force all text to be green. + // This renders the Gray, Gold, Red and Yellow color range inoperable, some of + // which are used by the menu. So we have no choice but to skip this lump so that + // all colors work properly. + // The text colors should be the end user's choice anyway. + if (Wads.GetLumpFile(lump) == 1) continue; + } FScanner sc(lump); while (sc.GetString()) { diff --git a/src/v_palette.cpp b/src/v_palette.cpp index 3149f5df5..7761a3dec 100644 --- a/src/v_palette.cpp +++ b/src/v_palette.cpp @@ -118,6 +118,21 @@ CUSTOM_CVAR (Float, Gamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } +CCMD (bumpgamma) +{ + // [RH] Gamma correction tables are now generated + // on the fly for *any* gamma level. + // Q: What are reasonable limits to use here? + + float newgamma = Gamma + 0.1f; + + if (newgamma > 3.0) + newgamma = 1.0; + + Gamma = newgamma; + Printf ("Gamma correction level %g\n", *Gamma); +} + /****************************/ /* Palette management stuff */ diff --git a/src/v_video.cpp b/src/v_video.cpp index 1e8993e74..c51f13cfc 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -42,7 +42,6 @@ #include "m_argv.h" #include "m_bbox.h" #include "m_swap.h" -#include "m_menu.h" #include "i_video.h" #include "v_video.h" @@ -63,6 +62,7 @@ #include "colormatcher.h" #include "v_palette.h" #include "r_sky.h" +#include "menu/menu.h" IMPLEMENT_ABSTRACT_CLASS (DCanvas) diff --git a/src/v_video.h b/src/v_video.h index b1ba7a863..14782519c 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -412,6 +412,7 @@ public: virtual void WipeEndScreen(); virtual bool WipeDo(int ticks); virtual void WipeCleanup(); + virtual int GetPixelDoubling() const { return 1; } uint32 GetLastFPS() const { return LastCount; } @@ -489,6 +490,7 @@ FString V_GetColorStringByName (const char *name); // Tries to get color by name, then by string int V_GetColor (const DWORD *palette, const char *str); +void V_DrawFrame (int left, int top, int width, int height); #if defined(X86_ASM) || defined(X64_ASM) extern "C" void ASM_PatchPitch (void); diff --git a/src/win32/i_dijoy.cpp b/src/win32/i_dijoy.cpp index 89ddd1600..b78fea64e 100644 --- a/src/win32/i_dijoy.cpp +++ b/src/win32/i_dijoy.cpp @@ -21,7 +21,6 @@ #include "doomdef.h" #include "doomstat.h" #include "win32iface.h" -#include "m_menu.h" #include "templates.h" #include "gameconfigfile.h" #include "cmdlib.h" @@ -236,8 +235,6 @@ protected: // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- -extern void UpdateJoystickMenu(); - // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -246,7 +243,6 @@ static void MapAxis(FIntCVar &var, int num); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern menu_t JoystickMenu; extern LPDIRECTINPUT8 g_pdi; extern HWND Window; diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index d01547dae..f302d807d 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -86,7 +86,6 @@ #include "i_input.h" #include "v_video.h" #include "i_sound.h" -#include "m_menu.h" #include "g_game.h" #include "d_main.h" #include "d_gui.h" @@ -145,6 +144,7 @@ extern HWND EAXEditWindow; EXTERN_CVAR (String, language) EXTERN_CVAR (Bool, lookstrafe) EXTERN_CVAR (Bool, use_joystick) +EXTERN_CVAR (Bool, use_mouse) static int WheelDelta; extern bool CursorState; @@ -191,6 +191,16 @@ static void I_CheckGUICapture () } } +void I_SetMouseCapture() +{ + SetCapture(Window); +} + +void I_ReleaseMouseCapture() +{ + ReleaseCapture(); +} + bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) { event_t ev = { EV_GUI_Event }; @@ -228,28 +238,29 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU { switch (wParam) { - case VK_PRIOR: ev.data1 = GK_PGUP; break; - case VK_NEXT: ev.data1 = GK_PGDN; break; - case VK_END: ev.data1 = GK_END; break; - case VK_HOME: ev.data1 = GK_HOME; break; - case VK_LEFT: ev.data1 = GK_LEFT; break; - case VK_RIGHT: ev.data1 = GK_RIGHT; break; - case VK_UP: ev.data1 = GK_UP; break; - case VK_DOWN: ev.data1 = GK_DOWN; break; - case VK_DELETE: ev.data1 = GK_DEL; break; - case VK_ESCAPE: ev.data1 = GK_ESCAPE; break; - case VK_F1: ev.data1 = GK_F1; break; - case VK_F2: ev.data1 = GK_F2; break; - case VK_F3: ev.data1 = GK_F3; break; - case VK_F4: ev.data1 = GK_F4; break; - case VK_F5: ev.data1 = GK_F5; break; - case VK_F6: ev.data1 = GK_F6; break; - case VK_F7: ev.data1 = GK_F7; break; - case VK_F8: ev.data1 = GK_F8; break; - case VK_F9: ev.data1 = GK_F9; break; - case VK_F10: ev.data1 = GK_F10; break; - case VK_F11: ev.data1 = GK_F11; break; - case VK_F12: ev.data1 = GK_F12; break; + case VK_PRIOR: ev.data1 = GK_PGUP; break; + case VK_NEXT: ev.data1 = GK_PGDN; break; + case VK_END: ev.data1 = GK_END; break; + case VK_HOME: ev.data1 = GK_HOME; break; + case VK_LEFT: ev.data1 = GK_LEFT; break; + case VK_RIGHT: ev.data1 = GK_RIGHT; break; + case VK_UP: ev.data1 = GK_UP; break; + case VK_DOWN: ev.data1 = GK_DOWN; break; + case VK_DELETE: ev.data1 = GK_DEL; break; + case VK_ESCAPE: ev.data1 = GK_ESCAPE; break; + case VK_F1: ev.data1 = GK_F1; break; + case VK_F2: ev.data1 = GK_F2; break; + case VK_F3: ev.data1 = GK_F3; break; + case VK_F4: ev.data1 = GK_F4; break; + case VK_F5: ev.data1 = GK_F5; break; + case VK_F6: ev.data1 = GK_F6; break; + case VK_F7: ev.data1 = GK_F7; break; + case VK_F8: ev.data1 = GK_F8; break; + case VK_F9: ev.data1 = GK_F9; break; + case VK_F10: ev.data1 = GK_F10; break; + case VK_F11: ev.data1 = GK_F11; break; + case VK_F12: ev.data1 = GK_F12; break; + case VK_BROWSER_BACK: ev.data1 = GK_BACK; break; } if (ev.data1 != 0) { @@ -278,6 +289,9 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + case WM_MOUSEMOVE: if (message >= WM_LBUTTONDOWN && message <= WM_LBUTTONDBLCLK) { ev.subtype = message - WM_LBUTTONDOWN + EV_GUI_LButtonDown; @@ -290,11 +304,35 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU { ev.subtype = message - WM_MBUTTONDOWN + EV_GUI_MButtonDown; } - D_PostEvent(&ev); + else if (message >= WM_XBUTTONDOWN && message <= WM_XBUTTONUP) + { + ev.subtype = message - WM_XBUTTONDOWN + EV_GUI_BackButtonDown; + if (GET_XBUTTON_WPARAM(wParam) == 2) + { + ev.subtype += EV_GUI_FwdButtonDown - EV_GUI_BackButtonDown; + } + else if (GET_XBUTTON_WPARAM(wParam) != 1) + { + break; + } + } + else if (message == WM_MOUSEMOVE) + { + ev.subtype = EV_GUI_MouseMove; + } + ev.data1 = LOWORD(lParam) >> screen->GetPixelDoubling(); + ev.data2 = HIWORD(lParam) >> screen->GetPixelDoubling(); + + if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT; + if (wParam & MK_CONTROL) ev.data3 |= GKM_CTRL; + if (GetKeyState(VK_MENU) & 0x8000) ev.data3 |= GKM_ALT; + + if (use_mouse) D_PostEvent(&ev); return true; // Note: If the mouse is grabbed, it sends the mouse wheel events itself. case WM_MOUSEWHEEL: + if (!use_mouse) return false; if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT; if (wParam & MK_CONTROL) ev.data3 |= GKM_CTRL; if (GetKeyState(VK_MENU) & 0x8000) ev.data3 |= GKM_ALT; @@ -385,6 +423,15 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return result; } + if ((gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL) && message == WM_LBUTTONDOWN) + { + if (GUIWndProcHook(hWnd, message, wParam, lParam, &result)) + { + return result; + } + } + + switch (message) { case WM_DESTROY: @@ -425,6 +472,10 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) SetCursor(NULL); // turn off window cursor return TRUE; // Prevent Windows from setting cursor to window class cursor } + else + { + return DefWindowProc(hWnd, message, wParam, lParam); + } break; case WM_SIZE: diff --git a/src/win32/i_input.h b/src/win32/i_input.h index 1c21d78c3..b0d409844 100644 --- a/src/win32/i_input.h +++ b/src/win32/i_input.h @@ -37,6 +37,9 @@ #include "doomtype.h" #include "doomdef.h" +void I_SetMouseCapture(); +void I_ReleaseMouseCapture(); + bool I_InitInput (void *hwnd); void I_ShutdownInput (); void I_PutInClipboard (const char *str); diff --git a/src/win32/i_mouse.cpp b/src/win32/i_mouse.cpp index d347a4d72..0fa46c1db 100644 --- a/src/win32/i_mouse.cpp +++ b/src/win32/i_mouse.cpp @@ -1,8 +1,3 @@ -#ifdef _MSC_VER -// Turn off "conversion from 'LONG_PTR' to 'LONG', possible loss of data" -// generated by SetClassLongPtr(). -#pragma warning(disable:4244) -#endif // HEADER FILES ------------------------------------------------------------ @@ -22,6 +17,7 @@ #include "doomstat.h" #include "win32iface.h" #include "rawinput.h" +#include "menu/menu.h" // MACROS ------------------------------------------------------------------ @@ -265,18 +261,17 @@ void I_CheckNativeMouse(bool preferNative) if (!windowed) { - want_native = false; + // ungrab mouse when in the menu with mouse control on. + want_native = m_use_mouse && (menuactive == MENU_On || menuactive == MENU_OnNoPause); } else { want_native = (GetForegroundWindow() != Window) || - !CaptureMode_InGame() || - GUICapture || - paused || preferNative || !use_mouse || - demoplayback; + ((!m_use_mouse || menuactive != MENU_WaitKey) && + (!CaptureMode_InGame() || GUICapture || paused || demoplayback)); } //Printf ("%d %d %d\n", wantNative, preferNative, NativeMouse); @@ -592,7 +587,7 @@ void FRawMouse::Ungrab() bool FRawMouse::ProcessRawInput(RAWINPUT *raw, int code) { - if (!Grabbed || raw->header.dwType != RIM_TYPEMOUSE) + if (!Grabbed || raw->header.dwType != RIM_TYPEMOUSE || !use_mouse) { return false; } @@ -806,7 +801,7 @@ void FDInputMouse::ProcessInput() dx = 0; dy = 0; - if (!Grabbed) + if (!Grabbed || !use_mouse) return; event_t ev = { 0 }; @@ -948,7 +943,7 @@ void FWin32Mouse::ProcessInput() POINT pt; int x, y; - if (!Grabbed || !GetCursorPos(&pt)) + if (!Grabbed || !use_mouse || !GetCursorPos(&pt)) { return; } @@ -1006,6 +1001,11 @@ bool FWin32Mouse::WndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa return true; } } + else if (!use_mouse) + { + // all following messages should only be processed if the mouse is in use + return false; + } else if (message == WM_MOUSEWHEEL) { WheelMoved(0, (SHORT)HIWORD(wParam)); diff --git a/src/win32/i_rawps2.cpp b/src/win32/i_rawps2.cpp index d060b869d..cab2844a6 100644 --- a/src/win32/i_rawps2.cpp +++ b/src/win32/i_rawps2.cpp @@ -16,7 +16,6 @@ #include "doomdef.h" #include "doomstat.h" #include "win32iface.h" -#include "m_menu.h" #include "templates.h" #include "gameconfigfile.h" #include "cmdlib.h" diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index c8b496656..f2178ffbf 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -83,9 +83,17 @@ #include "doomstat.h" #include "v_palette.h" #include "stats.h" +#include "r_data.h" +#include "textures/bitmap.h" // MACROS ------------------------------------------------------------------ +#ifdef _MSC_VER +// Turn off "conversion from 'LONG_PTR' to 'LONG', possible loss of data" +// generated by SetClassLongPtr(). +#pragma warning(disable:4244) +#endif + // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -108,6 +116,11 @@ static int I_WaitForTicEvent(int prevtic); static void I_FreezeTimeEventDriven(bool frozen); static void CALLBACK TimerTicked(UINT id, UINT msg, DWORD_PTR user, DWORD_PTR dw1, DWORD_PTR dw2); +static HCURSOR CreateCompatibleCursor(FTexture *cursorpic); +static HCURSOR CreateAlphaCursor(FTexture *cursorpic); +static HCURSOR CreateBitmapCursor(int xhot, int yhot, HBITMAP and_mask, HBITMAP color_mask); +static void DestroyCustomCursor(); + // EXTERNAL DATA DECLARATIONS ---------------------------------------------- EXTERN_CVAR(String, language); @@ -158,6 +171,8 @@ static WadStuff *WadList; static int NumWads; static int DefaultWad; +static HCURSOR CustomCursor; + // CODE -------------------------------------------------------------------- //========================================================================== @@ -1175,6 +1190,208 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad) return defaultiwad; } +//========================================================================== +// +// I_SetCursor +// +// Returns true if the cursor was successfully changed. +// +//========================================================================== + +bool I_SetCursor(FTexture *cursorpic) +{ + HCURSOR cursor; + + // Must be no larger than 32x32. + if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) + { + return false; + } + + cursor = CreateAlphaCursor(cursorpic); + if (cursor == NULL) + { + cursor = CreateCompatibleCursor(cursorpic); + } + if (cursor == NULL) + { + return false; + } + // Replace the existing cursor with the new one. + if (CustomCursor != NULL) + { + DestroyCursor(CustomCursor); + } + CustomCursor = cursor; + atterm(DestroyCustomCursor); + SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor); + return true; +} + +//========================================================================== +// +// CreateCompatibleCursor +// +// Creates a cursor with a 1-bit alpha channel. +// +//========================================================================== + +static HCURSOR CreateCompatibleCursor(FTexture *cursorpic) +{ + int picwidth = cursorpic->GetWidth(); + int picheight = cursorpic->GetHeight(); + + // Create bitmap masks for the cursor from the texture. + HDC dc = GetDC(NULL); + if (dc == NULL) + { + return false; + } + HDC and_mask_dc = CreateCompatibleDC(dc); + HDC xor_mask_dc = CreateCompatibleDC(dc); + HBITMAP and_mask = CreateCompatibleBitmap(dc, 32, 32); + HBITMAP xor_mask = CreateCompatibleBitmap(dc, 32, 32); + ReleaseDC(NULL, dc); + + SelectObject(and_mask_dc, and_mask); + SelectObject(xor_mask_dc, xor_mask); + + // Initialize with an invisible cursor. + SelectObject(and_mask_dc, GetStockObject(WHITE_PEN)); + SelectObject(and_mask_dc, GetStockObject(WHITE_BRUSH)); + Rectangle(and_mask_dc, 0, 0, 32, 32); + SelectObject(xor_mask_dc, GetStockObject(BLACK_PEN)); + SelectObject(xor_mask_dc, GetStockObject(BLACK_BRUSH)); + Rectangle(xor_mask_dc, 0, 0, 32, 32); + + FBitmap bmp; + const BYTE *pixels; + + bmp.Create(picwidth, picheight); + cursorpic->CopyTrueColorPixels(&bmp, 0, 0); + pixels = bmp.GetPixels(); + + // Copy color data from the source texture to the cursor bitmaps. + for (int y = 0; y < picheight; ++y) + { + for (int x = 0; x < picwidth; ++x) + { + const BYTE *bgra = &pixels[x*4 + y*bmp.GetPitch()]; + if (bgra[3] != 0) + { + SetPixelV(and_mask_dc, x, y, RGB(0,0,0)); + SetPixelV(xor_mask_dc, x, y, RGB(bgra[2], bgra[1], bgra[0])); + } + } + } + DeleteDC(and_mask_dc); + DeleteDC(xor_mask_dc); + + // Create the cursor from the bitmaps. + return CreateBitmapCursor(cursorpic->LeftOffset, cursorpic->TopOffset, and_mask, xor_mask); +} + +//========================================================================== +// +// CreateAlphaCursor +// +// Creates a cursor with a full alpha channel. +// +//========================================================================== + +static HCURSOR CreateAlphaCursor(FTexture *cursorpic) +{ + HDC dc; + BITMAPV5HEADER bi; + HBITMAP color, mono; + void *bits; + + memset(&bi, 0, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = 32; + bi.bV5Height = 32; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00FF0000; + bi.bV5GreenMask = 0x0000FF00; + bi.bV5BlueMask = 0x000000FF; + bi.bV5AlphaMask = 0xFF000000; + + dc = GetDC(NULL); + if (dc == NULL) + { + return NULL; + } + + // Create the DIB section with an alpha channel. + color = CreateDIBSection(dc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &bits, NULL, 0); + ReleaseDC(NULL, dc); + + if (color == NULL) + { + return NULL; + } + + // Create an empty mask bitmap, since CreateIconIndirect requires this. + mono = CreateBitmap(32, 32, 1, 1, NULL); + if (mono == NULL) + { + DeleteObject(color); + return NULL; + } + + // Copy cursor to the color bitmap. Note that GDI bitmaps are upside down compared + // to normal conventions, so we create the FBitmap pointing at the last row and use + // a negative pitch so that CopyTrueColorPixels will use GDI's orientation. + FBitmap bmp((BYTE *)bits + 31*32*4, -32*4, 32, 32); + cursorpic->CopyTrueColorPixels(&bmp, 0, 0); + + return CreateBitmapCursor(cursorpic->LeftOffset, cursorpic->TopOffset, mono, color); +} + +//========================================================================== +// +// CreateBitmapCursor +// +// Create the cursor from the bitmaps. Deletes the bitmaps before returning. +// +//========================================================================== + +static HCURSOR CreateBitmapCursor(int xhot, int yhot, HBITMAP and_mask, HBITMAP color_mask) +{ + ICONINFO iconinfo = + { + FALSE, // fIcon + xhot, // xHotspot + yhot, // yHotspot + and_mask, // hbmMask + color_mask // hbmColor + }; + HCURSOR cursor = CreateIconIndirect(&iconinfo); + + // Delete the bitmaps. + DeleteObject(and_mask); + DeleteObject(color_mask); + + return cursor; +} + +//========================================================================== +// +// DestroyCustomCursor +// +//========================================================================== + +static void DestroyCustomCursor() +{ + if (CustomCursor != NULL) + { + DestroyCursor(CustomCursor); + CustomCursor = NULL; + } +} + //========================================================================== // // I_WriteIniFailed diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 66b2bac7d..1a5e755e3 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -114,6 +114,10 @@ void STACK_ARGS I_FatalError (const char *error, ...) GCCPRINTF(1,2); void atterm (void (*func)(void)); void popterm (); +// Set the mouse cursor. The texture must be 32x32. +class FTexture; +bool I_SetCursor(FTexture *cursor); + // Repaint the pre-game console void I_PaintConsole (void); diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index e3ad1eed0..5a02d7af4 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -19,7 +19,6 @@ #include "doomdef.h" #include "doomstat.h" #include "win32iface.h" -#include "m_menu.h" #include "templates.h" #include "gameconfigfile.h" #include "cmdlib.h" diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 6193c641c..e3710b234 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -372,6 +372,7 @@ private: void EndLineBatch(); void EndBatch(); void CopyNextFrontBuffer(); + int GetPixelDoubling() const { return PixelDoubling; } D3DCAPS9 DeviceCaps; diff --git a/wadsrc/static/actors/hexen/clericplayer.txt b/wadsrc/static/actors/hexen/clericplayer.txt index 4fe6629e2..73276e981 100644 --- a/wadsrc/static/actors/hexen/clericplayer.txt +++ b/wadsrc/static/actors/hexen/clericplayer.txt @@ -23,6 +23,7 @@ ACTOR ClericPlayer : PlayerPawn Player.HealRadiusType "Health" Player.Hexenarmor 10, 10, 25, 5, 20 Player.StartItem "CWeapMace" + Player.Portrait "P_CWALK1" Player.WeaponSlot 1, CWeapMace Player.WeaponSlot 2, CWeapStaff Player.WeaponSlot 3, CWeapFlame diff --git a/wadsrc/static/actors/hexen/fighterplayer.txt b/wadsrc/static/actors/hexen/fighterplayer.txt index 2253ba5b6..809b1f9e9 100644 --- a/wadsrc/static/actors/hexen/fighterplayer.txt +++ b/wadsrc/static/actors/hexen/fighterplayer.txt @@ -23,6 +23,7 @@ ACTOR FighterPlayer : PlayerPawn Player.StartItem "FWeapFist" Player.ForwardMove 1.08, 1.2 Player.SideMove 1.125, 1.475 + Player.Portrait "P_FWALK1" Player.WeaponSlot 1, FWeapFist Player.WeaponSlot 2, FWeapAxe Player.WeaponSlot 3, FWeapHammer diff --git a/wadsrc/static/actors/hexen/mageplayer.txt b/wadsrc/static/actors/hexen/mageplayer.txt index a34698366..d843b00a9 100644 --- a/wadsrc/static/actors/hexen/mageplayer.txt +++ b/wadsrc/static/actors/hexen/mageplayer.txt @@ -25,6 +25,7 @@ ACTOR MagePlayer : PlayerPawn Player.StartItem "MWeapWand" Player.ForwardMove 0.88, 0.92 Player.SideMove 0.875, 0.925 + Player.Portrait "P_MWALK1" Player.WeaponSlot 1, MWeapWand Player.WeaponSlot 2, MWeapFrost Player.WeaponSlot 3, MWeapLightning diff --git a/wadsrc/static/animdefs.txt b/wadsrc/static/animdefs.txt index a2a4498e2..8bd2ee99a 100644 --- a/wadsrc/static/animdefs.txt +++ b/wadsrc/static/animdefs.txt @@ -101,6 +101,85 @@ pic PTN1A0 tics 3 pic PTN1B0 tics 3 pic PTN1C0 tics 3 +// Hexen's player portraits +texture optional P_FWALK1 + pic P_FWALK1 tics 8 + pic P_FWALK2 tics 8 + pic P_FWALK3 tics 8 + pic P_FWALK4 tics 8 + +texture optional P_CWALK1 + pic P_CWALK1 tics 8 + pic P_CWALK2 tics 8 + pic P_CWALK3 tics 8 + pic P_CWALK4 tics 8 + +texture optional P_MWALK1 + pic P_MWALK1 tics 8 + pic P_MWALK2 tics 8 + pic P_MWALK3 tics 8 + pic P_MWALK4 tics 8 + +// Heretic's menu skulls +texture optional M_SKL00 + pic M_SKL00 tics 3 + pic M_SKL01 tics 3 + pic M_SKL02 tics 3 + pic M_SKL03 tics 3 + pic M_SKL04 tics 3 + pic M_SKL05 tics 3 + pic M_SKL06 tics 3 + pic M_SKL07 tics 3 + pic M_SKL08 tics 3 + pic M_SKL09 tics 3 + pic M_SKL10 tics 3 + pic M_SKL11 tics 3 + pic M_SKL12 tics 3 + pic M_SKL13 tics 3 + pic M_SKL14 tics 3 + pic M_SKL15 tics 3 + pic M_SKL16 tics 3 + pic M_SKL17 tics 3 + +texture optional M_SKL01 + pic M_SKL17 tics 3 + pic M_SKL16 tics 3 + pic M_SKL15 tics 3 + pic M_SKL14 tics 3 + pic M_SKL13 tics 3 + pic M_SKL12 tics 3 + pic M_SKL11 tics 3 + pic M_SKL10 tics 3 + pic M_SKL09 tics 3 + pic M_SKL08 tics 3 + pic M_SKL07 tics 3 + pic M_SKL06 tics 3 + pic M_SKL05 tics 3 + pic M_SKL04 tics 3 + pic M_SKL03 tics 3 + pic M_SKL02 tics 3 + pic M_SKL01 tics 3 + pic M_SKL00 tics 3 + +// Hexen's Fire Bulls +texture optional FBULA0 + pic FBULA0 tics 5 + pic FBULB0 tics 5 + pic FBULC0 tics 5 + pic FBULD0 tics 5 + pic FBULE0 tics 5 + pic FBULF0 tics 5 + pic FBULG0 tics 5 + +texture optional FBULB0 + pic FBULC0 tics 5 + pic FBULD0 tics 5 + pic FBULE0 tics 5 + pic FBULF0 tics 5 + pic FBULG0 tics 5 + pic FBULA0 tics 5 + pic FBULB0 tics 5 + // The Wings of Wrath are not included, because they stop spinning when // you stop flying, so they can't be a simple animation. diff --git a/wadsrc/static/graphics/cursor.png b/wadsrc/static/graphics/cursor.png new file mode 100644 index 000000000..c9ee9b6e3 Binary files /dev/null and b/wadsrc/static/graphics/cursor.png differ diff --git a/wadsrc/static/graphics/m_back_d.png b/wadsrc/static/graphics/m_back_d.png new file mode 100644 index 000000000..2fb4a827d Binary files /dev/null and b/wadsrc/static/graphics/m_back_d.png differ diff --git a/wadsrc/static/graphics/m_back_h.png b/wadsrc/static/graphics/m_back_h.png new file mode 100644 index 000000000..fc45dcd7b Binary files /dev/null and b/wadsrc/static/graphics/m_back_h.png differ diff --git a/wadsrc/static/graphics/m_back_s.png b/wadsrc/static/graphics/m_back_s.png new file mode 100644 index 000000000..ab63b4310 Binary files /dev/null and b/wadsrc/static/graphics/m_back_s.png differ diff --git a/wadsrc/static/graphics/m_back_x.png b/wadsrc/static/graphics/m_back_x.png new file mode 100644 index 000000000..d785b6788 Binary files /dev/null and b/wadsrc/static/graphics/m_back_x.png differ diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 58cc87049..5f0eff88e 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1559,6 +1559,8 @@ MNU_DELETESG = "Do you really want to delete the savegame\n"; MNU_ONLYREGISTERED = "ONLY AVAILABLE IN THE REGISTERED VERSION"; +MNU_EPISODE = "Select Episode"; + // Bloodbath announcer BBA_BONED = "%k boned %o like a fish"; diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index deefea4d3..5a76298cb 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -43,6 +43,15 @@ gameinfo player5start = 4001 drawreadthis = true pickupcolor = "d6 ba 45" + quitmessages = "$QUITMSG", "$QUITMSG23", "$QUITMSG24", "$QUITMSG25", "$QUITMSG26", "$QUITMSG27", "$QUITMSG28", "$QUITMSG29" + menufontcolor_title = "GREEN" + menufontcolor_label = "UNTRANSLATED" + menufontcolor_value = "GRAY" + menufontcolor_action = "GRAY" + menufontcolor_header = "YELLOW" + menufontcolor_highlight = "BLUE" + menufontcolor_selection = "GOLD" + menubackbutton = "M_BACK_H" } skill baby diff --git a/wadsrc/static/mapinfo/doom1.txt b/wadsrc/static/mapinfo/doom1.txt index b3e631650..064f3777a 100644 --- a/wadsrc/static/mapinfo/doom1.txt +++ b/wadsrc/static/mapinfo/doom1.txt @@ -14,6 +14,7 @@ gameinfo borderflat = "FLOOR7_2" drawreadthis = true intermissionmusic = "$MUSIC_INTER" + quitmessages = "$QUITMSG", "$QUITMSG1", "$QUITMSG2", "$QUITMSG3", "$QUITMSG4", "$QUITMSG5", "$QUITMSG6", "$QUITMSG7" } clearepisodes diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index f67783509..9fd4b9637 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -41,6 +41,17 @@ gameinfo endoom = "ENDOOM" player5start = 4001 pickupcolor = "d6 ba 45" + quitmessages = "$QUITMSG", "$QUITMSG1", "$QUITMSG2", "$QUITMSG3", "$QUITMSG4", "$QUITMSG5", "$QUITMSG6", "$QUITMSG7", + "$QUITMSG8", "$QUITMSG9", "$QUITMSG10", "$QUITMSG11", "$QUITMSG12", "$QUITMSG13", "$QUITMSG14" + + menufontcolor_title = "RED" + menufontcolor_label = "UNTRANSLATED" + menufontcolor_value = "GRAY" + menufontcolor_action = "GRAY" + menufontcolor_header = "GOLD" + menufontcolor_highlight = "YELLOW" + menufontcolor_selection = "BRICK" + menubackbutton = "M_BACK_D" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 4ca727272..b9f88e2b9 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -42,6 +42,15 @@ gameinfo endoom = "ENDTEXT" player5start = 4001 pickupcolor = "d6 ba 45" + quitmessages = "$*RAVENQUITMSG" + menufontcolor_title = "UNTRANSLATED" + menufontcolor_label = "GREEN" + menufontcolor_value = "UNTRANSLATED" + menufontcolor_action = "UNTRANSLATED" + menufontcolor_header = "GOLD" + menufontcolor_highlight = "YELLOW" + menufontcolor_selection = "DARKGREEN" + menubackbutton = "M_BACK_H" } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 16ebcf5b4..8f0962a50 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -40,6 +40,15 @@ gameinfo defaultdropstyle = 1 player5start = 9100 pickupcolor = "d6 ba 45" + quitmessages = "$*RAVENQUITMSG" + menufontcolor_title = "UNTRANSLATED" + menufontcolor_label = "RED" + menufontcolor_value = "UNTRANSLATED" + menufontcolor_action = "UNTRANSLATED" + menufontcolor_header = "GOLD" + menufontcolor_highlight = "YELLOW" + menufontcolor_selection = "BRICK" + menubackbutton = "M_BACK_X" } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index e5c9043ee..ffb70e696 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -43,6 +43,15 @@ gameinfo endoom = "ENDSTRF" player5start = 5 pickupcolor = "d6 ba 45" + quitmessages = "$QUITMSG", "$QUITMSG15", "$QUITMSG16", "$QUITMSG17", "$QUITMSG18", "$QUITMSG19", "$QUITMSG20", "$QUITMSG21", "$QUITMSG22" + menufontcolor_title = "UNTRANSLATED" + menufontcolor_label = "UNTRANSLATED" + menufontcolor_value = "GRAY" + menufontcolor_action = "GRAY" + menufontcolor_header = "RED" + menufontcolor_highlight = "GREEN" + menufontcolor_selection = "GOLD" + menubackbutton = "M_BACK_S" } skill baby diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt new file mode 100644 index 000000000..ee624ed75 --- /dev/null +++ b/wadsrc/static/menudef.txt @@ -0,0 +1,1333 @@ +//------------------------------------------------------------------------------------------- +// +// Note: +// Much of the menu structure defined here is accessed internally by CCMDs +// and menu generation code. If you want to design your own menus make sure +// that they are named identically and that links to all important submenus +// are present. +// +//------------------------------------------------------------------------------------------- + +DEFAULTLISTMENU +{ + Font "BigFont", "Untranslated" + IfGame(Doom, Chex) + { + Selector "M_SKULL1", -32, -5 + Linespacing 16 + Font "BigFont", "Red" + } + IfGame(Strife) + { + Selector "M_CURS1", -28, -5 + Linespacing 19 + } + IfGame(Heretic, Hexen) + { + Selector "M_SLCTR1", -28, -1 + Linespacing 20 + } +} + +//------------------------------------------------------------------------------------------- +// +// The main menu. There's a lot of differences here between the games +// +//------------------------------------------------------------------------------------------- + +LISTMENU "MainMenu" +{ + IfGame(Doom, Chex) + { + StaticPatch 94, 2, "M_DOOM" + Position 97, 72 + IfOption(ReadThis) + { + Position 97, 64 + } + } + IfGame(Strife) + { + StaticPatch 84, 2, "M_STRIFE" + Position 97, 45 + } + IfGame(Heretic) + { + StaticPatch 88, 0, "M_HTIC" + StaticPatch 40, 10, "M_SKL01" + StaticPatch 232, 10, "M_SKL00" + Position 110, 56 + } + IfGame(Hexen) + { + StaticPatch 88, 0, "M_HTIC" + StaticPatch 37, 80, "FBULB0" + StaticPatch 278, 80, "FBULA0" + Position 110, 56 + } + + IfGame(Doom, Strife, Chex) + { + PatchItem "M_NGAME", "n", "PlayerclassMenu" + PatchItem "M_LOADG", "l", "LoadGameMenu" + PatchItem "M_SAVEG", "s", "SaveGameMenu" + PatchItem "M_OPTION","o", "OptionsMenu" + ifOption(ReadThis) + { + PatchItem "M_RDTHIS","r", "ReadThisMenu" + } + PatchItem "M_QUITG", "q", "QuitMenu" + } + + IfGame(Heretic, Hexen) + { + TextItem "$MNU_NEWGAME", "n", "PlayerclassMenu" + TextItem "$MNU_OPTIONS", "o", "OptionsMenu" + TextItem "$MNU_GAMEFILES", "g", "GameFilesMenu" + TextItem "$MNU_INFO", "i", "ReadThisMenu" + TextItem "$MNU_QUITGAME", "q", "QuitMenu" + } +} + +//------------------------------------------------------------------------------------------- +// +// Important note about the following template menus: +// Don't even think about replacing them with something that's not an empty menu +// with some static elements only. Proper function is not guaranteed then. +// +//------------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------- +// +// The player class menu +// The init code will set the first item to 'autoselect' if it's the only one. +// +//------------------------------------------------------------------------------------------- + +ListMenu "PlayerclassMenu" +{ + IfGame(Doom, Heretic, Hexen, Strife) + { + NetgameMessage "$NETGAME" + } + IfGame(Chex) + { + NetgameMessage "$CNETGAME" + } + + IfGame(Doom, Strife, Chex) + { + StaticTextCentered 160, 15, "$MNU_CHOOSECLASS" + Position 48, 63 + PlayerDisplay 220, 63, "20 00 00", "80 00 40" + MouseWindow 0, 220 + } + IfGame(Heretic) + { + StaticTextCentered 160, 15, "$MNU_CHOOSECLASS" + Position 80, 50 + PlayerDisplay 220, 50, "20 00 00", "80 00 40" + MouseWindow 0, 220 + } + IfGame(Hexen) + { + StaticText 34, 24, "$MNU_CHOOSECLASS" + Position 66, 58 + PlayerDisplay 174, 8, "00 07 00", "40 53 40" + MouseWindow 0, 174 + } + // The rest of this menu will be set up based on the actual player definitions. +} + +//------------------------------------------------------------------------------------------- +// +// The episode menu +// The init code will set the first item to 'autoselect' if it's the only one. +// +//------------------------------------------------------------------------------------------- + +ListMenu "EpisodeMenu" +{ + IfGame(Doom, Heretic, Hexen, Strife) + { + NetgameMessage "$NETGAME" + } + IfGame(Chex) + { + NetgameMessage "$CNETGAME" + } + + IfGame(Doom, Chex) + { + Position 48, 63 + StaticPatch 54, 38, "M_EPISOD" + } + IfGame(Strife) + { + Position 48, 63 + StaticText 54, 38, "$MNU_EPISODE" + } + IfGame(Heretic, Hexen) + { + Position 80, 50 + } + // items will be filled in by MAPINFO +} + +//------------------------------------------------------------------------------------------- +// +// The skill menu +// Most of this will be filled in at runtime +// +//------------------------------------------------------------------------------------------- + +ListMenu "SkillMenu" +{ + + IfGame(Doom, Chex) + { + StaticPatch 96, 14, "M_NEWG" + } + IfGame(Strife) + { + StaticPatch 96, 14, "M_NGAME" + } + IfGame(Doom, Strife, Chex) + { + StaticPatch 54, 38, "M_SKILL" + Position 48, 63 + } + IfGame (Heretic) + { + Position 38, 30 + } + IfGame (Hexen) + { + StaticText 74, 16, "$MNU_CHOOSESKILL" + Position 38, 44 + } +} + +//------------------------------------------------------------------------------------------- +// +// Raven's game files menu +// +//------------------------------------------------------------------------------------------- + +ListMenu "GameFilesMenu" +{ + Position 110, 60 + TextItem "$MNU_LOADGAME", "l", "LoadGameMenu" + TextItem "$MNU_SAVEGAME", "s", "SaveGameMenu" +} + +//------------------------------------------------------------------------------------------- +// +// Base definition for load game menu. Only the configurable part is done here +// +//------------------------------------------------------------------------------------------- + +ListMenu "LoadGameMenu" +{ + IfGame(Doom, Heretic, Hexen, Strife) + { + NetgameMessage "$LOADNET" + } + IfGame(Chex) + { + NetgameMessage "$CLOADNET" + } + IfGame(Doom, Strife, Chex) + { + StaticPatchCentered 160, -20, "M_LOADG" + } + IfGame(Heretic, Hexen) + { + StaticTextCentered 160, -10, "$MNU_LOADGAME" + } + Position 80,54 + Class "LoadMenu" // uses its own implementation +} + +//------------------------------------------------------------------------------------------- +// +// Base definition for save game menu. Only the configurable part is done here +// +//------------------------------------------------------------------------------------------- + +ListMenu "SaveGameMenu" +{ + IfGame(Doom, Strife, Chex) + { + StaticPatchCentered 160, -20, "M_SAVEG" + } + IfGame(Heretic, Hexen) + { + StaticTextCentered 160, -10, "$MNU_SAVEGAME" + } + Position 80,54 + Class "SaveMenu" // uses its own implementation +} + +//------------------------------------------------------------------------------------------- +// +// The option menu +// +//------------------------------------------------------------------------------------------- + +OptionValue "YesNo" +{ + 0, "No" + 1, "Yes" +} + +OptionValue "NoYes" +{ + 0, "Yes" + 1, "No" +} + +OptionValue "OnOff" +{ + 0, "Off" + 1, "On" +} + +OptionValue "OffOn" +{ + 0, "On" + 1, "Off" +} + +OptionMenuSettings +{ + // These can be overridden if a different menu fonts requires it. + Linespacing 8 + LabelOffset 0 + IfGame(Heretic, Hexen) + { + Linespacing 9 + LabelOffset 2 + } +} + +DefaultOptionMenu +{ + Position -15 + IfGame(Heretic, Hexen) + { + Position -13 + } +} + +OptionMenu "OptionsMenu" +{ + Title "OPTIONS" + Submenu "Customize Controls", "CustomizeControls" + Submenu "Mouse options", "MouseOptions" + Submenu "Joystick options", "JoystickOptions" + StaticText " " + Submenu "Player Setup", "PlayerMenu" + Submenu "Gameplay Options", "GameplayOptions" + Submenu "Compatibility Options", "CompatibilityOptions" + Submenu "Automap Options", "AutomapOptions" + Submenu "Sound Options", "SoundOptions" + Submenu "Display Options", "VideoOptions" + Submenu "Set video mode", "VideoModeMenu" + StaticText " " + SafeCommand "Reset to defaults", "reset2defaults" + SafeCommand "Reset to last saved", "reset2saved" + Command "Go to console", "menuconsole" +} + +//------------------------------------------------------------------------------------------- +// +// The player menu +// +//------------------------------------------------------------------------------------------- + +OptionValue "Gender" +{ + 0, "Male" + 1, "Female" + 2, "Other" +} + +OptionValue "Autoaim" +{ + 0, "Never" + 1, "Very low" + 2, "Low" + 3, "Medium" + 4, "High" + 5, "Very high" + 6, "Always" +} + + +ListMenu "PlayerMenu" +{ + StaticTextCentered 160, 6, "$MNU_PLAYERSETUP" + Font "SmallFont" + Linespacing 14 + Position 48, 36 + + IfGame (Doom, Strife, Chex) + { + PlayerNameBox "Name", 0, "Playerbox" + Selector "-", -16, -1 + } + IfGame(Heretic, Hexen) + { + PlayerNameBox "Name", 5, "Playerbox" + Selector "-", -16, 1 + } + IfGame(Doom, Heretic, Strife, Chex) + { + MouseWindow 0, 220 + PlayerDisplay 220, 80, "20 00 00", "80 00 40", 1, "PlayerDisplay" + } + IfGame(Hexen) + { + MouseWindow 0, 220 + PlayerDisplay 220, 80, "00 07 00", "40 53 40", 1, "PlayerDisplay" + } + + ValueText "Team", "Team" + ValueText "Color", "Color" + Linespacing 10 + Slider "Red", "Red", 0, 255, 16 + Slider "Green", "Green", 0, 255, 16 + Linespacing 14 + Slider "Blue", "Blue", 0, 255, 16 + ValueText "Class", "Class" + ValueText "Skin", "Skin" + ValueText "Gender", "Gender", "Gender" + ValueText "Autoaim", "Autoaim", "Autoaim" + ValueText "Switch on pickup", "Switch", "OffOn" + ValueText "Always Run", "AlwaysRun", "OnOff" + Class "PlayerMenu" +} + +//------------------------------------------------------------------------------------------- +// +// Controls Menu +// +//------------------------------------------------------------------------------------------- + +OptionMenu "CustomizeControls" +{ + Title "CUSTOMIZE CONTROLS" + ScrollTop 2 + StaticTextSwitchable "ENTER to change, BACKSPACE to clear", "Press new key for control, ESC to cancel", "ControlMessage" + StaticText "" + StaticText "Controls", 1 + Control "Fire", "+attack" + Control "Secondary Fire", "+altattack" + Control "Use / Open", "+use" + Control "Move forward", "+forward" + Control "Move backward", "+back" + Control "Strafe left", "+moveleft" + Control "Strafe right", "+moveright" + Control "Turn left", "+left" + Control "Turn right", "+right" + Control "Jump", "+jump" + Control "Crouch", "+crouch" + Control "Crouch Toggle", "crouch" + Control "Fly / Swim up", "+moveup" + Control "Fly / Swim down", "+movedown" + Control "Stop flying", "land" + Control "Mouse look", "+mlook" + Control "Keyboard look", "+klook" + Control "Look up", "+lookup" + Control "Look down", "+lookdown" + Control "Center view", "centerview" + Control "Run", "+speed" + Control "Strafe", "+strafe" + Control "Show Scoreboard", "+showscores" + StaticText "" + StaticText "Chat", 1 + Control "Say", "messagemode" + Control "Team say", "messagemode2" + StaticText "" + StaticText "Weapons", 1 + Control "Next weapon", "weapnext" + Control "Previous weapon", "weapprev" + StaticText "" + StaticText "Inventory", 1 + Control "Activate item", "invuse" + Control "Activate all items", "invuseall" + Control "Next item", "invnext" + Control "Previous item", "invprev" + Control "Drop item", "invdrop" + Control "Query item", "invquery" + Control "Drop weapon", "weapdrop" + StaticText "" + StaticText "Other", 1 + Control "Toggle automap", "togglemap" + Control "Chasecam", "chase" + Control "Coop spy", "spynext" + Control "Screenshot", "screenshot" + Control "Open console", "toggleconsole" + StaticText "" + StaticText "Strife Popup Screens", 1 + Control "Mission objectives", "showpop 1" + Control "Keys list", "showpop 2" + Control "Weapons/ammo/stats", "showpop 3" +} + +//------------------------------------------------------------------------------------------- +// +// Mouse Menu +// +//------------------------------------------------------------------------------------------- + +OptionValue "Corners" +{ + -1, "Off" + 0, "Upper left" + 1, "Upper right" + 2, "Lower left" + 3, "Lower right" +} + +OptionValue "MenuMouse" +{ + 0, "No" + 1, "Yes" + 2, "Touchscreen-like" +} + +OptionMenu "MouseOptions" +{ + Title "MOUSE OPTIONS" + Option "Enable mouse", "use_mouse", "YesNo" + IfOption(Windows) // GUI mouse not operable in SDL interface right now. + { + Option "Enable mouse in menus", "m_use_mouse", "MenuMouse", "use_mouse" + Option "Show back button", "m_show_backbutton", "Corners", "use_mouse" + } + StaticText "" + Slider "Overall sensitivity", "mouse_sensitivity", 0.5, 2.5, 0.1 + Option "Prescale mouse movement", "m_noprescale", "NoYes" + Option "Smooth mouse movement", "smooth_mouse", "YesNo" + StaticText "" + Slider "Turning speed", "m_yaw", 0, 2.5, 0.1 + Slider "Mouselook speed", "m_pitch", 0, 2.5, 0.1 + Slider "Forward/Backward speed", "m_forward", 0, 2.5, 0.1 + Slider "Strafing speed", "m_side", 0, 2.5, 0.1 + StaticText "" + Option "Always Mouselook", "freelook", "OnOff" + Option "Invert Mouse", "invertmouse", "OnOff" + Option "Lookspring", "lookspring", "OnOff" + Option "Lookstrafe", "lookstrafe", "OnOff" +} + + +//------------------------------------------------------------------------------------------- +// +// Joystick Menu +// +//------------------------------------------------------------------------------------------- + +OptionMenu "JoystickOptions" +{ + Title "CONTROLLER OPTIONS" + // Will be filled in by joystick code. +} + +OptionValue "JoyAxisMapNames" +{ + -1, "None" + 0, "Turning" + 1, "Looking Up/Down" + 2, "Moving Forward" + 3, "Strafing" + 4, "Moving Up/Down" +} + +OptionValue "Inversion" +{ + 0, "Not Inverted" + 1, "Inverted" +} + +OptionMenu "JoystickConfigMenu" +{ + Title "CONFIGURE CONTROLLER" + Class "JoystickConfigMenu" + // Will be filled in by joystick code. +} + + +//------------------------------------------------------------------------------------------- +// +// Video Menu +// +//------------------------------------------------------------------------------------------- + +OptionValue ColumnMethods +{ + 0.0, "Original" + 1.0, "Optimized" +} + +OptionValue RocketTrailTypes +{ + 0.0, "Off" + 1.0, "Particles" + 2.0, "Sprites" + 3.0, "Sprites & Particles" +} + +OptionValue BloodTypes +{ + 0.0, "Sprites" + 1.0, "Sprites & Particles" + 2.0, "Particles" +} + +OptionValue PuffTypes +{ + 0.0, "Sprites" + 1.0, "Particles" +} + +OptionValue Wipes +{ + 0.0, "None" + 1.0, "Melt" + 2.0, "Burn" + 3.0, "Crossfade" +} + +OptionValue Endoom +{ + 0.0, "Off" + 1.0, "On" + 2.0, "Only modified" +} + +OptionValue Contrast +{ + 0.0, "Off" + 1.0, "On" + 2.0, "Smooth" +} + +OptionValue DisplayTagsTypes +{ + 0.0, "None" + 1.0, "Items" + 2.0, "Weapons" + 3.0, "Both" +} + +OptionValue TextColors +{ + 0.0, "\cabrick" + 1.0, "\cbtan" + 2.0, "\ccgray" + 3.0, "\cdgreen" + 4.0, "\cebrown" + 5.0, "\cfgold" + 6.0, "\cgred" + 7.0, "\chblue" + 8.0, "\ciorange" + 9.0, "\cjwhite" + 10.0, "\ckyellow" + 11.0, "\cldefault" + 12.0, "\cmblack" + 13.0, "\cnlight blue" + 14.0, "\cocream" + 15.0, "\cpolive" + 16.0, "\cqdark green" + 17.0, "\crdark red" + 18.0, "\csdark brown" + 19.0, "\ctpurple" + 20.0, "\cudark gray" + 21.0, "\cvcyan" +} + +OptionValue Crosshairs +{ + // will be filled in from the XHAIRS lump +} + +OptionMenu "VideoOptions" +{ + Title "DISPLAY OPTIONS" + Submenu "Message Options", "MessageOptions" + Submenu "Scoreboard Options", "ScoreboardOptions" + StaticText " " + Slider "Screen size", "screenblocks", 3.0, 12.0, 1.0 + Slider "Brightness", "Gamma", 1.0, 3.0, 0.1 + Option "Vertical Sync", "vid_vsync", "OnOff" + Option "Column render mode", "r_columnmethod", "ColumnMethods" + + StaticText " " + Option "Crosshair", "crosshair", "Crosshairs" + Option "Display nametags", "displaynametags", "DisplayTagsTypes" + Option "Nametag color", "nametagcolor", "TextColors", "displaynametags" + Option "Stretch status bar", "st_scale", "OnOff" + Option "Alternative HUD", "hud_althud", "OnOff" + Option "Screen wipe style", "wipetype", "Wipes" + + IfOption(Windows) + { + Option "Show ENDOOM screen", "showendoom", "Endoom" + //Option "DirectDraw palette hack", "vid_palettehack", "OnOff" + //Option "Use attached surfaces", "vid_attachedsurfaces", "OnOff" + } + + StaticText " " + Option "Stretch short skies", "r_stretchsky", "OnOff" + Option "Use fuzz effect", "r_drawfuzz", "YesNo" + Option "Use fake contrast", "r_fakecontrast", "Contrast" + Option "Rocket Trails", "cl_rockettrails", "RocketTrailTypes" + Option "Blood Type", "cl_bloodtype", "BloodTypes" + Option "Bullet Puff Type", "cl_pufftype", "PuffTypes" + Option "Interpolate monster movement", "nomonsterinterpolation", "NoYes" +} + +//------------------------------------------------------------------------------------------- +// +// Automap Menu +// +//------------------------------------------------------------------------------------------- + +OptionValue MapColorTypes +{ + 0, "Custom" + 1, "Traditional Doom" + 2, "Traditional Strife" + 3, "Traditional Raven" +} + +OptionValue SecretTypes +{ + 0, "Never" + 1, "Only when found" + 2, "Always" +} + +OptionValue RotateTypes +{ + 0, "Off" + 1, "On" + 2, "On for overlay only" +} + +OptionValue OverlayTypes +{ + 0, "Off" + 1, "Overlay+Normal" + 2, "Overlay Only" +} + +OptionMenu AutomapOptions +{ + Title "AUTOMAP OPTIONS" + Option "Map color set", "am_colorset", "MapColorTypes" + Submenu "Set custom colors", "MapColorMenu" + Submenu "Customize map controls", "MapControlsMenu" + StaticText " " + Option "Rotate automap", "am_rotate", "RotateTypes" + Option "Overlay automap", "am_overlay", "OverlayTypes" + Option "Enable textured display", "am_textured", "OnOff" + StaticText " " + Option "Show item counts", "am_showitems", "OnOff" + Option "Show monster counts", "am_showmonsters", "OnOff" + Option "Show secret counts", "am_showsecrets", "OnOff" + Option "Show time elapsed", "am_showtime", "OnOff" + Option "Show total time elapsed", "am_showtotaltime", "OnOff" + Option "Show secrets on map", "am_map_secrets", "SecretTypes" + Option "Draw map background", "am_drawmapback", "OnOff" + Option "Show keys (cheat)", "am_showkeys", "OnOff" +} + +//------------------------------------------------------------------------------------------- +// +// Automap Controls +// +//------------------------------------------------------------------------------------------- + +OptionMenu MapControlsMenu +{ + Title "CUSTOMIZE MAP CONTROLS" + ScrollTop 2 + StaticTextSwitchable "ENTER to change, BACKSPACE to clear", "Press new key for control, ESC to cancel", "ControlMessage" + StaticText "" + StaticText "Map Controls", 1 + MapControl "Pan left", "+am_panleft" + MapControl "Pan right", "+am_panright" + MapControl "Pan up", "+am_panup" + MapControl "Pan down", "+am_pandown" + MapControl "Zoom in", "+am_zoomin" + MapControl "Zoom out", "+am_zoomout" + MapControl "Toggle zoom", "am_gobig" + MapControl "Toggle follow", "am_togglefollow" + MapControl "Toggle grid", "am_togglegrid" + MapControl "Toggle texture","am_toggletexture" + MapControl "Set mark", "am_setmark" + MapControl "Clear mark", "am_clearmarks" +} + +//------------------------------------------------------------------------------------------- +// +// Automap Colors +// +//------------------------------------------------------------------------------------------- + +OptionMenu MapColorMenu +{ + Title "CUSTOMIZE MAP COLORS" + SafeCommand "Restore default custom colors", "am_restorecolors" + StaticText " " + ColorPicker "Background", "am_backcolor" + ColorPicker "You", "am_yourcolor" + ColorPicker "1-sided walls", "am_wallcolor" + ColorPicker "2-sided walls with different floors", "am_fdwallcolor" + ColorPicker "2-sided walls with different ceilings", "am_cdwallcolor" + ColorPicker "Map grid", "am_gridcolor" + ColorPicker "Center point", "am_xhaircolor" + ColorPicker "Not-yet-seen walls", "am_notseencolor" + ColorPicker "Locked doors", "am_lockedcolor" + ColorPicker "Teleporter to the same map", "am_intralevelcolor" + ColorPicker "Teleporter to a different map", "am_interlevelcolor" + ColorPicker "Secret sector", "am_secretsectorcolor" + StaticText " " + StaticText "Cheat Mode", 1 + ColorPicker "Invisible 2-sided walls", "am_tswallcolor" + ColorPicker "Secret walls", "am_secretwallcolor" + ColorPicker "Actors", "am_thingcolor" + ColorPicker "Monsters", "am_thingcolor_monster" + ColorPicker "Friends", "am_thingcolor_friend" + ColorPicker "Items", "am_thingcolor_item" + ColorPicker "Count Items", "am_thingcolor_citem" + StaticText " " + StaticText "Overlay Mode", 1 + ColorPicker "You", "am_ovyourcolor" + ColorPicker "1-sided walls", "am_ovwallcolor" + ColorPicker "2-sided walls", "am_ovotherwallscolor" + ColorPicker "Not-yet-seen walls", "am_ovunseencolor" + ColorPicker "Teleporter", "am_ovtelecolor" + ColorPicker "Secret sector", "am_ovsecretsectorcolor" + StaticText " " + StaticText "Overlay Cheat Mode", 1 + ColorPicker "Actors", "am_ovthingcolor" + ColorPicker "Monsters", "am_ovthingcolor_monster" + ColorPicker "Friends", "am_ovthingcolor_friend" + ColorPicker "Items", "am_ovthingcolor_item" + ColorPicker "Count Items", "am_ovthingcolor_citem" +} + +//------------------------------------------------------------------------------------------- +// +// Color Picker +// +//------------------------------------------------------------------------------------------- + +OptionMenu ColorPickerMenu +{ + Title "SELECT COLOR" + // This menu will be created by the calling code +} + +//------------------------------------------------------------------------------------------- +// +// Messages +// +//------------------------------------------------------------------------------------------- + + +OptionValue ScaleValues +{ + 0, "Off" + 1, "On" + 2, "Double" +} + +OptionValue MessageLevels +{ + 0.0, "Item Pickup" + 1.0, "Obituaries" + 2.0, "Critical Messages" +} + +OptionMenu MessageOptions +{ + Title "MESSAGES" + Option "Show messages", "show_messages", "OnOff" + Option "Show obituaries", "show_obituaries", "OnOff" + Option "Scale text in high res", "con_scaletext", "ScaleValues" + Option "Minimum message level", "msg", "MessageLevels" + Option "Center messages", "con_centernotify", "OnOff" + StaticText " " + StaticText "Message Colors", 1 + StaticText " " + Option "Item Pickup", "msg0color", "TextColors" + Option "Obituaries", "msg1color", "TextColors" + Option "Critical Messages", "msg2color", "TextColors" + Option "Chat Messages", "msg3color", "TextColors" + Option "Team Messages", "msg4color", "TextColors" + Option "Centered Messages", "msgmidcolor", "TextColors" + StaticText " " + Option "Screenshot messages", "screenshot_quiet", "OffOn" + Option "Detailed save messages", "longsavemessages", "OnOff" +} + +//------------------------------------------------------------------------------------------- +// +// Scoreboard +// +//------------------------------------------------------------------------------------------- + +OptionMenu ScoreboardOptions +{ + Title "SCOREBOARD OPTIONS" + StaticText "Cooperative Options", 1 + StaticText " " + Option "Enable Scoreboard", "sb_cooperative_enable", "YesNo" + Option "Header Color", "sb_cooperative_headingcolor", "TextColors" + Option "Your Player Color", "sb_cooperative_yourplayercolor", "TextColors" + Option "Other Players' Color", "sb_cooperative_otherplayercolor", "TextColors" + StaticText " " + StaticText " " + StaticText "Deathmatch Options", 1 + StaticText " " + Option "Enable Scoreboard", "sb_deathmatch_enable", "YesNo" + Option "Header Color", "sb_deathmatch_headingcolor", "TextColors" + Option "Your Player Color", "sb_deathmatch_yourplayercolor", "TextColors" + Option "Other Players' Color", "sb_deathmatch_otherplayercolor", "TextColors" + StaticText " " + StaticText " " + StaticText "Team Deathmatch Options", 1 + StaticText " " + Option "Enable Scoreboard", "sb_teamdeathmatch_enable", "YesNo" + Option "Header Color", "sb_teamdeathmatch_headingcolor", "TextColors" +} + +/*======================================= + * + * Gameplay Options (dmflags) Menu + * + *=======================================*/ + +OptionValue SmartAim +{ + 0.0, "Off" + 1.0, "On" + 2.0, "Never friends" + 3.0, "Only monsters" +} + +OptionValue FallingDM +{ + 0, "Off" + 1, "Old" + 2, "Hexen" + 3, "Strife" +} + +OptionValue JumpCrouch +{ + 0, "Default" + 1, "Off" + 2, "On" +} + + +OptionMenu GameplayOptions +{ + Title "GAMEPLAY OPTIONS" + Indent 222 + Option "Teamplay", "teamplay", "OnOff" + Slider "Team damage scalar", "teamdamage", 0, 1, 0.05 + StaticText " " + Option "Smart Autoaim", "sv_smartaim", "SmartAim" + StaticText " " + Option "Falling damage", "sv_fallingdamage", "FallingDM" + Option "Drop weapon", "sv_weapondrop", "YesNo" + Option "Double ammo", "sv_doubleammo", "YesNo" + Option "Infinite ammo", "sv_infiniteammo", "YesNo" + Option "Infinite inventory", "sv_infiniteinventory", "YesNo" + Option "No monsters", "sv_nomonsters", "YesNo" + Option "No monsters to exit", "sv_killallmonsters", "YesNo" + Option "Monsters respawn", "sv_monsterrespawn", "YesNo" + Option "No respawn", "sv_norespawn", "YesNo" + Option "Items respawn", "sv_itemrespawn", "YesNo" + Option "Big powerups respawn", "sv_respawnsuper", "YesNo" + Option "Fast monsters", "sv_fastmonsters", "YesNo" + Option "Degeneration", "sv_degeneration", "YesNo" + Option "Allow Autoaim", "sv_noautoaim", "NoYes" + Option "Allow Suicide", "sv_disallowsuicide", "NoYes" + Option "Allow jump", "sv_jump", "JumpCrouch" + Option "Allow crouch", "sv_crouch", "JumpCrouch" + Option "Allow freelook", "sv_nofreelook", "NoYes" + Option "Allow FOV", "sv_nofov", "NoYes" + Option "Allow BFG aiming", "sv_nobfgaim", "NoYes" + Option "Allow automap", "sv_noautomap", "NoYes" + Option "Automap allies", "sv_noautomapallies", "NoYes" + Option "Allow spying", "sv_disallowspying", "NoYes" + Option "Chasecam cheat", "sv_chasecam", "YesNo" + Option "Check ammo for weapon switch", "sv_dontcheckammo", "NoYes" + Option "Killing Romero kills all his spawns", "sv_killbossmonst", "YesNo" + + StaticText " " + StaticText "Deathmatch Settings",1 + Option "Weapons stay", "sv_weaponstay", "YesNo" + Option "Allow powerups", "sv_noitems", "NoYes" + Option "Allow health", "sv_nohealth", "YesNo" + Option "Allow armor", "sv_noarmor", "YesNo" + Option "Spawn farthest", "sv_spawnfarthest", "YesNo" + Option "Same map", "sv_samelevel", "YesNo" + Option "Force respawn", "sv_forcerespawn", "YesNo" + Option "Allow exit", "sv_noexit", "YesNo" + Option "Barrels respawn", "sv_barrelrespawn", "YesNo" + Option "Respawn protection", "sv_respawnprotect", "YesNo" + Option "Lose frag if fragged", "sv_losefrag", "YesNo" + Option "Keep frags gained", "sv_keepfrags", "YesNo" + Option "No team switching", "sv_noteamswitch", "YesNo" + + StaticText " " + StaticText "Cooperative Settings",1 + Option "Spawn multi. weapons", "sv_noweaponspawn", "NoYes" + Option "Lose entire inventory", "sv_cooploseinventory", "YesNo" + Option "Keep keys", "sv_cooplosekeys", "NoYes" + Option "Keep weapons", "sv_cooploseweapons", "NoYes" + Option "Keep armor", "sv_cooplosearmor", "NoYes" + Option "Keep powerups", "sv_cooplosepowerups", "NoYes" + Option "Keep ammo", "sv_cooploseammo", "NoYes" + Option "Lose half ammo", "sv_coophalveammo", "YesNo" + Option "Spawn where died", "sv_samespawnspot", "YesNo" + Class "GameplayMenu" +} + +/*======================================= + * + * Compatibility Options Menu + * + *=======================================*/ + + +OptionValue CompatModes +{ + 0, "Default" + 1, "Doom" + 2, "Doom (strict)" + 3, "Boom" + 6, "Boom (strict)" + 5, "MBF" + 4, "ZDoom 2.0.63" +} + +OptionMenu "CompatibilityOptions" +{ + Title "COMPATIBILITY OPTIONS" + Option "Compatibility mode", "compatmode", "CompatModes", "", 1 + StaticText " " + Option "Find shortest textures like Doom", "compat_SHORTTEX", "YesNo" + Option "Use buggier stair building", "compat_stairs", "YesNo" + Option "Find neighboring light like Doom", "compat_LIGHT", "YesNo" + Option "Limit Pain Elementals' Lost Souls", "compat_LIMITPAIN", "YesNo" + Option "Don't let others hear your pickups", "compat_SILENTPICKUP", "YesNo" + Option "Actors are infinitely tall", "compat_nopassover", "YesNo" + Option "Enable wall running", "compat_WALLRUN", "YesNo" + Option "Spawn item drops on the floor", "compat_NOTOSSDROPS", "YesNo" + Option "All special lines can block ", "compat_USEBLOCKING", "YesNo" + Option "Disable BOOM door light effect", "compat_NODOORLIGHT", "YesNo" + Option "Raven scrollers use original speed", "compat_RAVENSCROLL", "YesNo" + Option "Use original sound target handling", "compat_SOUNDTARGET", "YesNo" + Option "DEH health settings like Doom2.exe", "compat_DEHHEALTH", "YesNo" + Option "Self ref. sectors don't block shots", "compat_TRACE", "YesNo" + Option "Monsters get stuck over dropoffs", "compat_DROPOFF", "YesNo" + Option "Monsters cannot cross dropoffs", "compat_CROSSDROPOFF", "YesNo" + Option "Monsters see invisible players", "compat_INVISIBILITY", "YesNo" + Option "Boom scrollers are additive", "compat_BOOMSCROLL", "YesNo" + Option "Inst. moving floors are not silent", "compat_silentinstantfloors", "YesNo" + Option "Sector sounds use center as source", "compat_SECTORSOUNDS", "YesNo" + Option "Use Doom heights for missile clipping", "compat_MISSILECLIP", "YesNo" + Option "Allow any bossdeath for level special", "compat_ANYBOSSDEATH", "YesNo" + Option "No Minotaur floor flames in water", "compat_MINOTAUR", "YesNo" + Option "Original A_Mushroom speed in DEH mods", "compat_MUSHROOM", "YesNo" + Option "Monster movement is affected by effects", "compat_MBFMONSTERMOVE", "YesNo" + Option "Crushed monsters can be resurrected", "compat_CORPSEGIBS", "YesNo" + Option "Friendly monsters aren't blocked", "compat_NOBLOCKFRIENDS", "YesNo" + Option "Invert sprite sorting", "compat_SPRITESORT", "YesNo" + Option "Use Doom code for hitscan checks", "compat_HITSCAN", "YesNo" + Option "Cripple sound for silent BFG trick", "compat_soundslots", "YesNo" + Option "Draw polyobjects like Hexen", "compat_POLYOBJ", "YesNo" + + Class "CompatibilityMenu" +} + +/*======================================= + * + * Sound Options Menu + * + *=======================================*/ + +OptionValue SampleRates +{ + 0, "Default" + 4000, "4000 Hz" + 8000, "8000 Hz" + 11025, "11025 Hz" + 22050, "22050 Hz" + 32000, "32000 Hz" + 44100, "44100 Hz" + 48000, "48000 Hz" +} + + +OptionValue BufferSizes +{ + 0, "Default" + 64, "64 samples" + 128, "128 samples" + 256, "256 samples" + 512, "512 samples" + 1024, "1024 samples" + 2048, "2048 samples" + 4096, "4096 samples" +} + + +OptionValue BufferCounts +{ + 0, "Default" + 2, "2" + 3, "3" + 4, "4" + 5, "5" + 6, "6" + 7, "7" + 8, "8" + 9, "9" + 10, "10" + 11, "11" + 12, "12" +} + + +OptionString SoundOutputsWindows +{ + "Default", "Default" + "DirectSound", "DirectSound" + "WASAPI", "Vista WASAPI" + "ASIO", "ASIO" + "WaveOut", "WaveOut" + "OpenAL", "OpenAL (very beta)" + "No sound", "No sound" +} + + +OptionString SoundOutputsUnix +{ + "Default", "Default" + "OSS", "OSS" + "ALSA", "ALSA" + "SDL", "SDL" + "ESD", "ESD" + "No sound", "No sound" +} + +OptionString SoundOutputsMac +{ + "Sound Manager", "Sound Manager" + "Core Audio", "Core Audio" + "No sound", "No sound" +} + +OptionString OutputFormats +{ + "PCM-8", "8-bit" + "PCM-16", "16-bit" + "PCM-24", "24-bit" + "PCM-32", "32-bit" + "PCM-Float", "32-bit float" +} + + +OptionString SpeakerModes +{ + "Auto", "Auto" + "Mono", "Mono" + "Stereo", "Stereo" + "Prologic", "Dolby Prologic Decoder" + "Quad", "Quad" + "Surround", "5 speakers" + "5.1", "5.1 speakers" + "7.1", "7.1 speakers" +} + + +OptionString Resamplers +{ + "NoInterp", "No interpolation" + "Linear", "Linear" + "Cubic", "Cubic" + "Spline", "Spline" +} + +OptionValue MidiDevices +{ + // filled in by the sound code +} + +OptionMenu SoundOptions +{ + Title "SOUND OPTIONS" + Slider "Sounds volume", "snd_sfxvolume", 0, 1, 0.05 + Slider "Menu volume", "snd_menuvolume", 0, 1, 0.05 + Slider "Music volume", "snd_musicvolume", 0, 1, 0.05 + Option "MIDI device", "snd_mididevice", "MidiDevices" + StaticText " " + Option "Underwater reverb", "snd_waterreverb", "OnOff" + Slider "Underwater cutoff", "snd_waterlp", 0, 2000, 50 + Option "Randomize pitches", "snd_pitched", "OnOff" + Slider "Sound channels", "snd_channels", 8, 256, 8 + StaticText " " + Command "Restart sound", "snd_reset" + StaticText " " + IfOption(Windows) + { + Option "Output system", "snd_output", "SoundOutputsWindows" + } + IfOption(Unix) + { + Option "Output system", "snd_output", "SoundOutputsUnix" + } + IfOption(Mac) + { + Option "Output system", "snd_output", "SoundOutputsMac" + } + Option "Output format", "snd_output_format", "OutputFormats" + Option "Speaker mode", "snd_speakermode", "SpeakerModes" + Option "Resampler", "snd_resampler", "Resamplers" + Option "HRTF filter", "snd_hrtf", "OnOff" + + StaticText " " + Submenu "Advanced options", "AdvSoundOptions" + Submenu "Module replayer options", "ModReplayerOptions" +} + +/*======================================= + * + * Advanced Sound Options Menu + * + *=======================================*/ + +OptionMenu AdvSoundOptions +{ + Title "ADVANCED SOUND OPTIONS" + Option "Sample rate", "snd_samplerate", "SampleRates" + Option "Buffer size", "snd_buffersize", "BufferSizes" + Option "Buffer count", "snd_buffercount", "BufferCounts" + StaticText " " + StaticText "OPL Synthesis", 1 + Option "Only emulate one OPL chip", "opl_onechip", "OnOff" +} + +/*======================================= + * + * Module Replayer Options Menu + * + *=======================================*/ + +OptionValue ModReplayers +{ + 0.0, "FMOD" + 1.0, "foo_dumb" +} + + +OptionValue ModInterpolations +{ + 0.0, "None" + 1.0, "Linear" + 2.0, "Cubic" +} + + +OptionValue ModVolumeRamps +{ + 0.0, "None" + 1.0, "Logarithmic" + 2.0, "Linear" + 3.0, "XM=lin, else none" + 4.0, "XM=lin, else log" +} + + +OptionMenu ModReplayerOptions +{ + Title "MODULE REPLAYER OPTIONS" + Option "Replayer engine", "mod_dumb", "ModReplayers" + StaticText " " + Option "Sample rate", "mod_samplerate", "SampleRates" + Option "Interpolation", "mod_interp", "ModInterpolations" + Option "Volume ramping", "mod_volramp", "ModVolumeRamps" + StaticText " " + Option "Chip-o-matic", "mod_autochip", "OnOff" + // TODO if the menu system is ever rewritten: Provide a decent + // mechanism to edit the chip-o-matic settings like you can with + // the foo_dumb preferences in foobar2000. +} + +/*======================================= + * + * Video mode menu + * + *=======================================*/ + +OptionValue ForceRatios +{ + 0.0, "Off" + 3.0, "4:3" + 1.0, "16:9" + 2.0, "16:10" + 4.0, "5:4" +} +OptionValue Ratios +{ + 0.0, "4:3" + 1.0, "16:9" + 2.0, "16:10" + 3.0, "All" +} +OptionValue RatiosTFT +{ + 0.0, "4:3" + 4.0, "5:4" + 1.0, "16:9" + 2.0, "16:10" + 3.0, "All" +} + +OptionMenu VideoModeMenu +{ + Title "VIDEO MODE" + + Option "Fullscreen", "fullscreen", "YesNo" + Option "Aspect ratio", "menu_screenratios", "Ratios" + Option "Force aspect ratio", "vid_aspect", "ForceRatios" + Option "Enable 5:4 aspect ratio","vid_tft", "YesNo" + StaticText " " + ScreenResolution "res_0" + ScreenResolution "res_1" + ScreenResolution "res_2" + ScreenResolution "res_3" + ScreenResolution "res_4" + ScreenResolution "res_5" + ScreenResolution "res_6" + ScreenResolution "res_7" + ScreenResolution "res_8" + ScreenResolution "res_9" + StaticTextSwitchable "Press ENTER to set mode", "", "VMEnterText" + StaticText " " + StaticTextSwitchable "T to test mode for 5 seconds", "Please wait 5 seconds...", "VMTestText" + class VideoModeMenu +} + diff --git a/wadsrc/static/textures.txt b/wadsrc/static/textures.txt new file mode 100644 index 000000000..19f27e84e --- /dev/null +++ b/wadsrc/static/textures.txt @@ -0,0 +1,72 @@ + +Graphic optional P_FWALK1, 112, 136 +{ + Patch "M_FBOX", 0, 0 + Patch "M_FWALK1", 24, 12 { useoffsets } +} + +Graphic optional P_FWALK2, 112, 136 +{ + Patch "M_FBOX", 0, 0 + Patch "M_FWALK2", 24, 12 { useoffsets } +} + +Graphic optional P_FWALK3, 112, 136 +{ + Patch "M_FBOX", 0, 0 + Patch "M_FWALK3", 24, 12 { useoffsets } +} + +Graphic optional P_FWALK4, 112, 136 +{ + Patch "M_FBOX", 0, 0 + Patch "M_FWALK4", 24, 12 { useoffsets } +} + +Graphic optional P_CWALK1, 112, 136 +{ + Patch "M_CBOX", 0, 0 + Patch "M_CWALK1", 24, 12 { useoffsets } +} + +Graphic optional P_CWALK2, 112, 136 +{ + Patch "M_CBOX", 0, 0 + Patch "M_CWALK2", 24, 12 { useoffsets } +} + +Graphic optional P_CWALK3, 112, 136 +{ + Patch "M_CBOX", 0, 0 + Patch "M_CWALK3", 24, 12 { useoffsets } +} + +Graphic optional P_CWALK4, 112, 136 +{ + Patch "M_CBOX", 0, 0 + Patch "M_CWALK4", 24, 12 { useoffsets } +} + +Graphic optional P_MWALK1, 112, 136 +{ + Patch "M_MBOX", 0, 0 + Patch "M_MWALK1", 24, 12 { useoffsets } +} + +Graphic optional P_MWALK2, 112, 136 +{ + Patch "M_MBOX", 0, 0 + Patch "M_MWALK2", 24, 12 { useoffsets } +} + +Graphic optional P_MWALK3, 112, 136 +{ + Patch "M_MBOX", 0, 0 + Patch "M_MWALK3", 24, 12 { useoffsets } +} + +Graphic optional P_MWALK4, 112, 136 +{ + Patch "M_MBOX", 0, 0 + Patch "M_MWALK4", 24, 12 { useoffsets } +} diff --git a/zdoom.vcproj b/zdoom.vcproj index 4df675804..94a7a92dd 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + @@ -688,18 +692,10 @@ RelativePath=".\src\m_joy.cpp" > - - - - @@ -966,7 +962,7 @@ /> + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -1876,14 +1880,6 @@ Outputs="$(IntDir)/$(InputName).obj" /> - - - @@ -2073,14 +2069,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -2091,6 +2079,14 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + - - - + + + - - - @@ -5391,6 +5379,14 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> + + + @@ -5681,7 +5677,7 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +