From 579502ab74a9b7db95f0840b50da1e0832619d5a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Sep 2010 17:28:18 +0000 Subject: [PATCH 001/815] - merged menu branch back into trunk. SVN r2768 (trunk) --- src/CMakeLists.txt | 15 +- src/c_bind.cpp | 4 +- src/c_bind.h | 19 +- src/c_cmds.cpp | 1 + src/c_cvars.cpp | 110 +- src/c_cvars.h | 24 + src/d_gui.h | 39 +- src/d_iwad.cpp | 2 +- src/d_main.cpp | 20 +- src/d_net.cpp | 2 +- src/d_player.h | 1 + src/dobjgc.cpp | 2 + src/g_game.cpp | 15 +- src/g_level.cpp | 12 +- src/g_level.h | 12 + src/g_mapinfo.cpp | 75 +- src/gi.cpp | 15 +- src/gi.h | 10 + src/keysections.cpp | 146 + src/m_joy.h | 1 + src/m_menu.cpp | 4152 ------------------ src/m_menu.h | 280 -- src/m_misc.cpp | 3 - src/m_misc.h | 1 + src/m_options.cpp | 3958 ----------------- src/menu/colorpickermenu.cpp | 357 ++ src/menu/joystickmenu.cpp | 420 ++ src/menu/listmenu.cpp | 511 +++ src/menu/loadsavemenu.cpp | 1165 +++++ src/menu/menu.cpp | 949 ++++ src/menu/menu.h | 652 +++ src/menu/menudef.cpp | 1389 ++++++ src/menu/menuinput.cpp | 366 ++ src/menu/messagebox.cpp | 733 ++++ src/menu/optionmenu.cpp | 591 +++ src/menu/optionmenuitems.h | 916 ++++ src/menu/playerdisplay.cpp | 561 +++ src/menu/playermenu.cpp | 1142 +++++ src/menu/readthis.cpp | 154 + src/menu/videomenu.cpp | 442 ++ src/namedef.h | 61 + src/p_conversation.cpp | 688 +-- src/p_conversation.h | 2 - src/r_draw.cpp | 4 +- src/sdl/i_input.cpp | 8 + src/sdl/i_input.h | 2 + src/sdl/i_system.cpp | 5 + src/sdl/i_system.h | 2 + src/sound/i_music.h | 3 +- src/sound/music_midi_base.cpp | 118 +- src/textures/bitmap.h | 1 + src/textures/multipatchtexture.cpp | 29 +- src/thingdef/thingdef_properties.cpp | 9 + src/v_draw.cpp | 33 + src/v_font.cpp | 9 + src/v_palette.cpp | 15 + src/v_video.cpp | 2 +- src/v_video.h | 2 + src/win32/i_dijoy.cpp | 4 - src/win32/i_input.cpp | 99 +- src/win32/i_input.h | 3 + src/win32/i_mouse.cpp | 26 +- src/win32/i_rawps2.cpp | 1 - src/win32/i_system.cpp | 217 + src/win32/i_system.h | 4 + src/win32/i_xinput.cpp | 1 - src/win32/win32iface.h | 1 + wadsrc/static/actors/hexen/clericplayer.txt | 1 + wadsrc/static/actors/hexen/fighterplayer.txt | 1 + wadsrc/static/actors/hexen/mageplayer.txt | 1 + wadsrc/static/animdefs.txt | 79 + wadsrc/static/graphics/cursor.png | Bin 0 -> 264 bytes wadsrc/static/graphics/m_back_d.png | Bin 0 -> 204 bytes wadsrc/static/graphics/m_back_h.png | Bin 0 -> 213 bytes wadsrc/static/graphics/m_back_s.png | Bin 0 -> 235 bytes wadsrc/static/graphics/m_back_x.png | Bin 0 -> 169 bytes wadsrc/static/language.enu | 2 + wadsrc/static/mapinfo/chex.txt | 9 + wadsrc/static/mapinfo/doom1.txt | 1 + wadsrc/static/mapinfo/doomcommon.txt | 11 + wadsrc/static/mapinfo/heretic.txt | 9 + wadsrc/static/mapinfo/hexen.txt | 9 + wadsrc/static/mapinfo/strife.txt | 9 + wadsrc/static/menudef.txt | 1333 ++++++ wadsrc/static/textures.txt | 72 + zdoom.vcproj | 826 ++-- 86 files changed, 13680 insertions(+), 9299 deletions(-) create mode 100644 src/keysections.cpp delete mode 100644 src/m_menu.cpp create mode 100644 src/menu/colorpickermenu.cpp create mode 100644 src/menu/joystickmenu.cpp create mode 100644 src/menu/listmenu.cpp create mode 100644 src/menu/loadsavemenu.cpp create mode 100644 src/menu/menu.cpp create mode 100644 src/menu/menu.h create mode 100644 src/menu/menudef.cpp create mode 100644 src/menu/menuinput.cpp create mode 100644 src/menu/messagebox.cpp create mode 100644 src/menu/optionmenu.cpp create mode 100644 src/menu/optionmenuitems.h create mode 100644 src/menu/playerdisplay.cpp create mode 100644 src/menu/playermenu.cpp create mode 100644 src/menu/readthis.cpp create mode 100644 src/menu/videomenu.cpp create mode 100644 wadsrc/static/graphics/cursor.png create mode 100644 wadsrc/static/graphics/m_back_d.png create mode 100644 wadsrc/static/graphics/m_back_h.png create mode 100644 wadsrc/static/graphics/m_back_s.png create mode 100644 wadsrc/static/graphics/m_back_x.png create mode 100644 wadsrc/static/menudef.txt create mode 100644 wadsrc/static/textures.txt 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 0000000000000000000000000000000000000000..c9ee9b6e3125e54fdca9d5e972b087500da7c9f9 GIT binary patch literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(9O*@lNkEzrh?xQ}sr~}eyFFbT zLn`8?UNYo5WFX`A@y)yg+AG3Ou*PiOvGW$Q#rEYL7417JnoG*oxMzHK`u0Q0MJDD|;l>-PzICnhYrTfRr}D&w=qX>T7z9dI_#JHU9!a7q9A9@nQExA1eDt`I(-zuz0^4F*qF KKbLh*2~7ZAXKRH3 literal 0 HcmV?d00001 diff --git a/wadsrc/static/graphics/m_back_d.png b/wadsrc/static/graphics/m_back_d.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb4a827d8340232ed00a5749b83122152187b6b GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XEGms3L`dJ)ENeB3ZxU&5FW6j9)nu+Zk6I&!B z^F}7t$xJL|jLdS3jQR>^^?=G4OM?7@862M7NCR@>JzX3_DsGvcb>wPL5Mj9xS=Ai% zT+rzSLt~u7u7)3b9c~0^A51paZP_mM?+p8bDs_hQn*^EP3NvoEJmI)@(w3H{v=*5Q zH4B*bD1DipvnR(!@~X}eWz$smn03E>B=*~#+b5UuP593>pj8Z>u6{1-oD!M~mo>t^* zFyLu@IDNqbkvUxx6ACr%sBmUQ$b9xarn4q#`eRd-`5FJVF(}Mt+!fsET;-l5P~P!a zOLty_+B93Sw%zNFTn;noxb@w%a?=#kC-$NK)7siC65hRJR*YAdXLIK~477^D)78&q Iol`;+0MC|1wEzGB literal 0 HcmV?d00001 diff --git a/wadsrc/static/graphics/m_back_s.png b/wadsrc/static/graphics/m_back_s.png new file mode 100644 index 0000000000000000000000000000000000000000..ab63b43105985e3022e75825332debffdc6fd026 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XEGms3L`dJ)EX$AO%xU&5FQ^Lj5k{9E{#FE3t zGN&u2r!K{Yi6w@OMTLo_B*|yVtP&F@77;F< zdimYd9bs{^FVdQ&MBb@02aSGqW}N^ literal 0 HcmV?d00001 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 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8308a35345d6282b2df7d60bbfba63b36a49a241 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Sep 2010 17:29:54 +0000 Subject: [PATCH 002/815] - deleted empty old files. SVN r2769 (trunk) --- src/CMakeLists.txt | 1 - src/m_menu.h | 0 src/m_options.cpp | 0 3 files changed, 1 deletion(-) delete mode 100644 src/m_menu.h delete mode 100644 src/m_options.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b811e8a59..e34d8ea3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -657,7 +657,6 @@ add_executable( zdoom WIN32 m_cheat.cpp m_joy.cpp m_misc.cpp - m_options.cpp m_png.cpp m_random.cpp md5.cpp diff --git a/src/m_menu.h b/src/m_menu.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/m_options.cpp b/src/m_options.cpp deleted file mode 100644 index e69de29bb..000000000 From ecb34b8302bc5598238ab4c359ce4b75d0c34913 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Sep 2010 19:08:21 +0000 Subject: [PATCH 003/815] - relaxed conditions for shortening the sliders so that they remain at full size at 800x600. SVN r2771 (trunk) --- src/menu/optionmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 735b674ea..a64f77431 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -79,7 +79,7 @@ void M_DrawSlider (int x, int y, double min, double max, double cur,int fracdigi range = max - min; double ccur = clamp(cur, min, max) - min; - if (CleanXfac > CleanXfac_1) + if (CleanXfac > CleanXfac_1 || CleanXfac_1 * 320 < screen->GetWidth()) { 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"); From 5fcac9b5f1848aa7f90a274e67ac06dab69b3175 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Sep 2010 20:53:12 +0000 Subject: [PATCH 004/815] - fixed: The default GetPixelDoubling function in DFrameBuffer returned 1 instead of 0. - copied a NULL pointer check for screen from GZDoom. SVN r2776 (trunk) --- src/v_video.h | 2 +- src/win32/i_input.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/v_video.h b/src/v_video.h index 14782519c..8d492c7b4 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -412,7 +412,7 @@ public: virtual void WipeEndScreen(); virtual bool WipeDo(int ticks); virtual void WipeCleanup(); - virtual int GetPixelDoubling() const { return 1; } + virtual int GetPixelDoubling() const { return 0; } uint32 GetLastFPS() const { return LastCount; } diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index f302d807d..49915bf29 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -320,8 +320,12 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU { ev.subtype = EV_GUI_MouseMove; } - ev.data1 = LOWORD(lParam) >> screen->GetPixelDoubling(); - ev.data2 = HIWORD(lParam) >> screen->GetPixelDoubling(); + + { + int shift = screen? screen->GetPixelDoubling() : 0; + ev.data1 = LOWORD(lParam) >> shift; + ev.data2 = HIWORD(lParam) >> shift; + } if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT; if (wParam & MK_CONTROL) ev.data3 |= GKM_CTRL; From 087979d6cc42d04ad915893e778f4551d43e660b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Sep 2010 22:00:02 +0000 Subject: [PATCH 005/815] - fixed: skipping the skill menu locked up the menu sequence for starting a game. SVN r2777 (trunk) --- src/menu/menudef.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 31fe0fd3b..033ad599b 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -1327,7 +1327,11 @@ void M_StartupSkillMenu(FGameStartup *gs) } if (AllEpisodes[gs->Episode].mNoSkill || AllSkills.Size() == 1) { - ld->mAutoselect = MIN(2u, AllEpisodes.Size()-1); + ld->mAutoselect = firstitem + MIN(2u, AllEpisodes.Size()-1); + } + else + { + ld->mAutoselect = -1; } success = true; } From d5f9b0874ed1c20b06accef1355b53dde1e75fc6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 10:22:38 +0000 Subject: [PATCH 006/815] - fixed: Lines in the option menu that covered the same vertical space as the back button rendered the back button inoperable. SVN r2779 (trunk) --- src/menu/menu.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index cb557512f..f74ac4794 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -104,12 +104,16 @@ DMenu::DMenu(DMenu *parent) bool DMenu::Responder (event_t *ev) { + bool res = false; 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)) + res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2); + // make the menu's mouse handler believe that the current coordinate is outside the valid range + if (res) ev->data2 = -1; + res |= MouseEvent(MOUSE_Click, ev->data1, ev->data2); + if (res) { SetCapture(); } @@ -120,8 +124,9 @@ bool DMenu::Responder (event_t *ev) BackbuttonTime = BACKBUTTON_TIME; if (mMouseCapture || m_use_mouse == 1) { - MouseEventBack(MOUSE_Move, ev->data1, ev->data2); - return MouseEvent(MOUSE_Move, ev->data1, ev->data2); + res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2); + if (res) ev->data2 = -1; + res |= MouseEvent(MOUSE_Move, ev->data1, ev->data2); } } else if (ev->subtype == EV_GUI_LButtonUp) @@ -129,8 +134,9 @@ bool DMenu::Responder (event_t *ev) if (mMouseCapture) { ReleaseCapture(); - MouseEventBack(MOUSE_Release, ev->data1, ev->data2); - return MouseEvent(MOUSE_Release, ev->data1, ev->data2); + res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2); + if (res) ev->data2 = -1; + res |= MouseEvent(MOUSE_Release, ev->data1, ev->data2); } } } @@ -211,7 +217,7 @@ bool DMenu::MouseEventBack(int type, int x, int y) if (m_use_mouse == 2) mBackbuttonSelected = false; MenuEvent(MKEY_Back, true); } - return true; + return mBackbuttonSelected; } } return false; From 4ca21e8e382992867970c33f62c446613cebab2d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 13:49:11 +0000 Subject: [PATCH 007/815] - added Edward-san's 'changemus' submission for printing the currently playing music track. SVN r2782 (trunk) --- src/s_sound.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 8d6424169..c8fbd2343 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2618,15 +2618,27 @@ CCMD (idmus) CCMD (changemus) { - if (argv.argc() > 1) - { - if (PlayList) - { - delete PlayList; - PlayList = NULL; - } - S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0); - } + if (argv.argc() > 1) + { + if (PlayList) + { + delete PlayList; + PlayList = NULL; + } + S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0); + } + else + { + const char *currentmus = mus_playing.name.GetChars(); + if(currentmus != NULL && *currentmus != 0) + { + Printf ("currently playing %s\n", currentmus); + } + else + { + Printf ("no music playing\n"); + } + } } //========================================================================== From a0d7693f33e8761a103ce6d33153dcf6537eb989 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 14:09:48 +0000 Subject: [PATCH 008/815] - added Spleen's fix for not running 2 frames of the weapon when the player spawns. SVN r2783 (trunk) --- src/g_game.cpp | 5 ++++- src/p_pspr.cpp | 6 ++++++ src/p_user.cpp | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index caf43cd6f..cd9cd6f4a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1345,7 +1345,10 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { actor->GiveDefaultInventory (); - p->ReadyWeapon = p->PendingWeapon; + + // [Spleen] ReadyWeapon will be set when P_MovePsprites calls P_BringUpWeapon + p->ReadyWeapon = NULL; + //p->ReadyWeapon = p->PendingWeapon; } //Added by MC: Init bot structure. diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index a30e2d736..0a9e3eb4e 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -794,9 +794,15 @@ void P_SetupPsprites(player_t *player) { player->psprites[i].state = NULL; } + + + // [Spleen] Let P_MovePsprites handle calling P_BringUpWeapon, + // instead of P_SetupPsprites, to avoid raising it twice in one tic. + /* // Spawn the ready weapon player->PendingWeapon = player->ReadyWeapon; P_BringUpWeapon (player); + */ } //------------------------------------------------------------------------ diff --git a/src/p_user.cpp b/src/p_user.cpp index eef4f5025..f94cf7088 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1103,7 +1103,10 @@ void APlayerPawn::GiveDefaultInventory () if (item != NULL && item->IsKindOf (RUNTIME_CLASS (AWeapon)) && static_cast(item)->CheckAmmo(AWeapon::EitherFire, false)) { - player->ReadyWeapon = player->PendingWeapon = static_cast (item); + // [Spleen] ReadyWeapon will be set when P_MovePsprites calls P_BringUpWeapon + player->PendingWeapon = static_cast (item); + player->ReadyWeapon = NULL; + //player->ReadyWeapon = player->PendingWeapon = static_cast (item); } } di = di->Next; From dca03ac76a92b396f54bb38ada016f9d74f91b19 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 14:47:44 +0000 Subject: [PATCH 009/815] - changed ACS's print n: cast to use an actor's tag for non-players instead of the class name. SVN r2784 (trunk) --- src/p_acs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 556592991..807827b95 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5040,7 +5040,7 @@ int DLevelScript::RunScript () } else if (activator) { - work += RUNTIME_TYPE(activator)->TypeName.GetChars(); + work += activator->GetTag(); } else { From d8fd72e19a5243c4052a049bb74c7b0ff8774189 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 16:50:08 +0000 Subject: [PATCH 010/815] - added Khamsin's fix for visible mouse cursors in Linux fullscreen mode with mouse disabled. SVN r2785 (trunk) --- src/sdl/i_input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index 3e94ff045..0fe02cfd2 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -256,7 +256,7 @@ static void I_CheckNativeMouse () == (SDL_APPINPUTFOCUS|SDL_APPACTIVE); bool fs = (SDL_GetVideoSurface ()->flags & SDL_FULLSCREEN) != 0; - bool wantNative = !focus || !use_mouse || (!fs && (GUICapture || paused || demoplayback || !inGame())); + bool wantNative = !focus || (!fs && (!use_mouse || GUICapture || paused || demoplayback || !inGame())); if (wantNative != NativeMouse) { From 5dfc57336e7ebf6bc6ec504a269e14d01eb0da91 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 20:11:32 +0000 Subject: [PATCH 011/815] - fixed: The class selection menu crashed when no item in the menu was selected. SVN r2786 (trunk) --- src/menu/playerdisplay.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/menu/playerdisplay.cpp b/src/menu/playerdisplay.cpp index f9871e1ca..a621562fd 100644 --- a/src/menu/playerdisplay.cpp +++ b/src/menu/playerdisplay.cpp @@ -424,14 +424,18 @@ void FListMenuItemPlayerDisplay::SetPlayerClass(int classnum, bool force) bool FListMenuItemPlayerDisplay::UpdatePlayerClass() { - int classnum; - FName seltype = mOwner->mItems[mOwner->mSelectedItem]->GetAction(&classnum); + if (mOwner->mSelectedItem >= 0) + { + int classnum; + FName seltype = mOwner->mItems[mOwner->mSelectedItem]->GetAction(&classnum); - if (seltype != NAME_Episodemenu) return false; - if (PlayerClasses.Size() == 0) return false; + if (seltype != NAME_Episodemenu) return false; + if (PlayerClasses.Size() == 0) return false; - SetPlayerClass(classnum); - return true; + SetPlayerClass(classnum); + return true; + } + return false; } //============================================================================= From 106cf82f6262e0d930bf07e079fd4c8243d05eb9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 21:41:49 +0000 Subject: [PATCH 012/815] - added a fixed version of Demolisher's GetActor** submission for returning the activator's info when the tid is 0. SVN r2787 (trunk) --- src/p_acs.cpp | 84 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 807827b95..ff7a95625 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5834,15 +5834,22 @@ int DLevelScript::RunScript () case PCD_GETACTORY: case PCD_GETACTORZ: { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) + if(STACK(1) == 0) { - STACK(1) = 0; + STACK(1) = (&activator->x)[pcd - PCD_GETACTORX]; } else { - STACK(1) = (&actor->x)[pcd - PCD_GETACTORX]; + AActor *actor = SingleActorFromTID (STACK(1), activator); + + if (actor == NULL) + { + STACK(1) = 0; + } + else + { + STACK(1) = (&actor->x)[pcd - PCD_GETACTORX]; + } } } break; @@ -5850,50 +5857,77 @@ int DLevelScript::RunScript () case PCD_GETACTORFLOORZ: case PCD_GETACTORCEILINGZ: { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) + if(STACK(1) == 0) { - STACK(1) = 0; - } - else if (pcd == PCD_GETACTORFLOORZ) - { - STACK(1) = actor->floorz; + if (pcd == PCD_GETACTORFLOORZ) + { + STACK(1) = activator->floorz; + } + else if(STACK(1) == 0) + { + STACK(1) = activator->ceilingz; + } } else { - STACK(1) = actor->ceilingz; - } + AActor *actor = SingleActorFromTID (STACK(1), activator); + if (actor == NULL) + { + STACK(1) = 0; + } + else if (pcd == PCD_GETACTORFLOORZ) + { + STACK(1) = actor->floorz; + } + else + { + STACK(1) = actor->ceilingz; + } + } } break; case PCD_GETACTORANGLE: { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) + if(STACK(1) == 0) { - STACK(1) = 0; + STACK(1) = activator->angle >> 16; } else { - STACK(1) = actor->angle >> 16; + AActor *actor = SingleActorFromTID (STACK(1), activator); + + if (actor == NULL) + { + STACK(1) = 0; + } + else + { + STACK(1) = actor->angle >> 16; + } } } break; case PCD_GETACTORPITCH: { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) + if(STACK(1) == 0) { - STACK(1) = 0; + STACK(1) = activator->pitch >> 16; } else { - STACK(1) = actor->pitch >> 16; + AActor *actor = SingleActorFromTID (STACK(1), activator); + + if (actor == NULL) + { + STACK(1) = 0; + } + else + { + STACK(1) = actor->pitch >> 16; + } } } break; From c948fd08f2bee82230b0d4bf507b247909ae1341 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Sep 2010 21:53:12 +0000 Subject: [PATCH 013/815] - fixed: GUI mouse events did not take letterboxing into account. SVN r2788 (trunk) --- src/menu/menu.cpp | 3 ++- src/v_video.h | 1 + src/win32/i_input.cpp | 1 + src/win32/win32iface.h | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index f74ac4794..6d60344b1 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -211,7 +211,8 @@ bool DMenu::MouseEventBack(int type, int x, int y) { 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); + mBackbuttonSelected = ( x >= 0 && x < tex->GetScaledWidth() * CleanXfac && + y >= 0 && y < tex->GetScaledHeight() * CleanYfac); if (mBackbuttonSelected && type == MOUSE_Release) { if (m_use_mouse == 2) mBackbuttonSelected = false; diff --git a/src/v_video.h b/src/v_video.h index 8d492c7b4..f4fb6f5bf 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -413,6 +413,7 @@ public: virtual bool WipeDo(int ticks); virtual void WipeCleanup(); virtual int GetPixelDoubling() const { return 0; } + virtual int GetTrueHeight() { return GetHeight(); } uint32 GetLastFPS() const { return LastCount; } diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index 49915bf29..c5370fb03 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -325,6 +325,7 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU int shift = screen? screen->GetPixelDoubling() : 0; ev.data1 = LOWORD(lParam) >> shift; ev.data2 = HIWORD(lParam) >> shift; + if (screen) ev.data2 -= (screen->GetTrueHeight() - screen->GetHeight())/2; } if (wParam & MK_SHIFT) ev.data3 |= GKM_SHIFT; diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index e3710b234..e6745b6cf 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -162,6 +162,7 @@ public: void SetVSync (bool vsync); void NewRefreshRate(); HRESULT GetHR (); + virtual int GetTrueHeight() { return TrueHeight; } void Blank (); bool PaintToWindow (); @@ -265,6 +266,7 @@ public: bool WipeDo(int ticks); void WipeCleanup(); HRESULT GetHR (); + virtual int GetTrueHeight() { return TrueHeight; } private: friend class D3DTex; From 3662bf8a15f1f2bed0dd34a14528a6dc06bc12e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 06:51:42 +0000 Subject: [PATCH 014/815] - reverted r2783 because it was causing problems. SVN r2792 (trunk) --- src/g_game.cpp | 5 +---- src/p_pspr.cpp | 6 ------ src/p_user.cpp | 5 +---- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index cd9cd6f4a..caf43cd6f 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1345,10 +1345,7 @@ void G_PlayerReborn (int player) if (gamestate != GS_TITLELEVEL) { actor->GiveDefaultInventory (); - - // [Spleen] ReadyWeapon will be set when P_MovePsprites calls P_BringUpWeapon - p->ReadyWeapon = NULL; - //p->ReadyWeapon = p->PendingWeapon; + p->ReadyWeapon = p->PendingWeapon; } //Added by MC: Init bot structure. diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 0a9e3eb4e..a30e2d736 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -794,15 +794,9 @@ void P_SetupPsprites(player_t *player) { player->psprites[i].state = NULL; } - - - // [Spleen] Let P_MovePsprites handle calling P_BringUpWeapon, - // instead of P_SetupPsprites, to avoid raising it twice in one tic. - /* // Spawn the ready weapon player->PendingWeapon = player->ReadyWeapon; P_BringUpWeapon (player); - */ } //------------------------------------------------------------------------ diff --git a/src/p_user.cpp b/src/p_user.cpp index f94cf7088..eef4f5025 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1103,10 +1103,7 @@ void APlayerPawn::GiveDefaultInventory () if (item != NULL && item->IsKindOf (RUNTIME_CLASS (AWeapon)) && static_cast(item)->CheckAmmo(AWeapon::EitherFire, false)) { - // [Spleen] ReadyWeapon will be set when P_MovePsprites calls P_BringUpWeapon - player->PendingWeapon = static_cast (item); - player->ReadyWeapon = NULL; - //player->ReadyWeapon = player->PendingWeapon = static_cast (item); + player->ReadyWeapon = player->PendingWeapon = static_cast (item); } } di = di->Next; From 916d4809a97b98c88c30b00390191bf4b523f9b7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 07:00:27 +0000 Subject: [PATCH 015/815] - added Demolisher's APROP_Target/TracerTID submission. SVN r2793 (trunk) --- src/p_acs.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index ff7a95625..930d1d554 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2547,6 +2547,8 @@ enum APROP_Notrigger = 23, APROP_DamageFactor = 24, APROP_MasterTID = 25, + APROP_TargetTID = 26, + APROP_TracerTID = 27 }; // These are needed for ACS's APROP_RenderStyle @@ -2786,6 +2788,8 @@ int DLevelScript::GetActorProperty (int tid, int property) } case APROP_Score: return actor->Score; case APROP_MasterTID: return DoGetMasterTID (actor); + case APROP_TargetTID: return (actor->target != NULL) actor->target->tid : 0; + case APROP_TracerTID: return (actor->tracer != NULL) actor->tracer->tid : 0; default: return 0; } } @@ -2817,6 +2821,8 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_JumpZ: case APROP_Score: case APROP_MasterTID: + case APROP_TargetTID: + case APROP_TracerTID: return (GetActorProperty(tid, property) == value); // Boolean values need to compare to a binary version of value From deef0e0a882badb24ec343e3a4efe8173b17c001 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 08:36:14 +0000 Subject: [PATCH 016/815] - oops SVN r2794 (trunk) --- src/p_acs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 930d1d554..f7a52987b 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2788,8 +2788,8 @@ int DLevelScript::GetActorProperty (int tid, int property) } case APROP_Score: return actor->Score; case APROP_MasterTID: return DoGetMasterTID (actor); - case APROP_TargetTID: return (actor->target != NULL) actor->target->tid : 0; - case APROP_TracerTID: return (actor->tracer != NULL) actor->tracer->tid : 0; + case APROP_TargetTID: return (actor->target != NULL)? actor->target->tid : 0; + case APROP_TracerTID: return (actor->tracer != NULL)? actor->tracer->tid : 0; default: return 0; } } From ee87fdc58ef219eacde1ea7cf2bc153297acc53f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 10:59:40 +0000 Subject: [PATCH 017/815] - some layout tweaks for the option menu system, in particular to shorten the sliders if the menu is too wide. - allow specifying the fractional precision for the numbers behind the sliders. - took all HUD related options out of the display options menu and created a seaparate one for them. - added several more display and HUD options to the menu. - created a new 'Miscellaneous options' menu for a few items that should be accessible but don't fit anywhere else. SVN r2795 (trunk) --- src/menu/menudef.cpp | 6 +- src/menu/optionmenu.cpp | 41 ----------- src/menu/optionmenuitems.h | 53 +++++++++++++- wadsrc/static/menudef.txt | 145 ++++++++++++++++++++++++++++++------- 4 files changed, 172 insertions(+), 73 deletions(-) diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 033ad599b..8b865cbc6 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -749,13 +749,13 @@ static void ParseOptionMenuBody(FScanner &sc, FOptionMenuDescriptor *desc) sc.MustGetStringName(","); sc.MustGetFloat(); double step = sc.Float; - bool showvalue = true; + int showvalue = 1; if (sc.CheckString(",")) { sc.MustGetNumber(); - showvalue = !!sc.Number; + showvalue = sc.Number; } - FOptionMenuItem *it = new FOptionMenuSliderCVar(text, action, min, max, step, showvalue? 1:-1); + FOptionMenuItem *it = new FOptionMenuSliderCVar(text, action, min, max, step, showvalue); desc->mItems.Push(it); } else if (sc.Compare("screenresolution")) diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index a64f77431..5661eec8e 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -66,47 +66,6 @@ void M_DrawConText (int color, int x, int y, const char *str) 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 || CleanXfac_1 * 320 < screen->GetWidth()) - { - 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) diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index 07a72fb53..e365f6039 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -34,7 +34,6 @@ 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(); @@ -551,6 +550,8 @@ class FOptionMenuSliderBase : public FOptionMenuItem double mMin, mMax, mStep; int mShowValue; int mDrawX; + int mSliderShort; + public: FOptionMenuSliderBase(const char *label, double min, double max, double step, int showval) : FOptionMenuItem(label, NAME_None) @@ -560,17 +561,63 @@ public: mStep = step; mShowValue = showval; mDrawX = 0; + mSliderShort = 0; } virtual double GetValue() = 0; virtual void SetValue(double val) = 0; + //============================================================================= + // + // Draw a slider. Set fracdigits negative to not display the current value numerically. + // + //============================================================================= + + void DrawSlider (int x, int y, double min, double max, double cur,int fracdigits, int indent) + { + char textbuf[16]; + double range; + int maxlen = 0; + int right = x + (12*8 + 4) * CleanXfac_1; + + range = max - min; + double ccur = clamp(cur, min, max) - min; + + if (fracdigits >= 0) + { + mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, max); + maxlen = SmallFont->StringWidth(textbuf) * CleanXfac_1; + } + + mSliderShort = right + maxlen > screen->GetWidth(); + + if (!mSliderShort) + { + M_DrawConText(CR_WHITE, x, 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"); + } + 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"); + right -= 5*8*CleanXfac_1; + } + + if (fracdigits >= 0 && right + maxlen <= screen->GetWidth()) + { + mysnprintf(textbuf, countof(textbuf), "%.*f", fracdigits, cur); + screen->DrawText(SmallFont, CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true, TAG_DONE); + } + } + + //============================================================================= int Draw(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); + DrawSlider (mDrawX, y + OptionSettings.mLabelOffset, mMin, mMax, GetValue(), mShowValue, indent); return indent; } @@ -609,7 +656,7 @@ public: } int slide_left = mDrawX+8*CleanXfac_1; - int slide_right = slide_left + 10*8*CleanXfac_1; // 12 char cells with 8 pixels each. + int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each. if (type == DMenu::MOUSE_Click) { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index ee624ed75..875525ec0 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -331,6 +331,8 @@ OptionMenu "OptionsMenu" Submenu "Gameplay Options", "GameplayOptions" Submenu "Compatibility Options", "CompatibilityOptions" Submenu "Automap Options", "AutomapOptions" + Submenu "HUD Options", "HUDOptions" + Submenu "Miscellaneous Options", "MiscOptions" Submenu "Sound Options", "SoundOptions" Submenu "Display Options", "VideoOptions" Submenu "Set video mode", "VideoModeMenu" @@ -615,6 +617,48 @@ OptionValue Contrast 2.0, "Smooth" } +OptionMenu "VideoOptions" +{ + Title "DISPLAY OPTIONS" + + Submenu "Message Options", "MessageOptions" + Submenu "Scoreboard Options", "ScoreboardOptions" + StaticText " " + Slider "Screen size", "screenblocks", 3.0, 12.0, 1.0, 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 "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" + } + + Option "Stretch short skies", "r_stretchsky", "OnOff" + Option "Use fuzz effect", "r_drawfuzz", "YesNo" + Slider "Lost Soul translucency", "transsouls", 0.25, 1.0, 0.05, 2 + 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" + Slider "Number of particles", "r_maxparticles", 100, 10000, 100, 0 + Option "Show player sprites", "r_drawplayersprites", "OnOff" + Option "Death camera", "r_deathcamera", "OnOff" + Option "Teleporter zoom", "telezoom", "OnOff" + Option "Interpolate monster movement", "nomonsterinterpolation", "NoYes" +} + +//------------------------------------------------------------------------------------------- +// +// HUD menu +// +//------------------------------------------------------------------------------------------- + OptionValue DisplayTagsTypes { 0.0, "None" @@ -654,40 +698,89 @@ OptionValue Crosshairs // will be filled in from the XHAIRS lump } -OptionMenu "VideoOptions" +OptionMenu "HUDOptions" { - Title "DISPLAY OPTIONS" - Submenu "Message Options", "MessageOptions" - Submenu "Scoreboard Options", "ScoreboardOptions" + Title "HUD Options" + Submenu "Alternative HUD", "AltHudOptions" 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" - + Option "Default Crosshair", "crosshair", "Crosshairs" + Option "Force default crosshair", "crosshairforce", "OnOff" + Option "Grow crosshair when picking up items", "crosshairgrow", "OnOff" + ColorPicker "Crosshair color", "crosshaircolor" + Option "Crosshair shows health", "crosshairhealth", "OnOff" + Option "Scale crosshair", "crosshairscale", "OnOff" 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" +} + +//------------------------------------------------------------------------------------------- +// +// Alternative HUD +// +//------------------------------------------------------------------------------------------- +OptionValue "AMCoordinates" +{ + 0, "Player" + 1, "Map" +} + +OptionMenu "AltHUDOptions" +{ + Title "Alternative HUD" + //Indent 220 + Option "Enable alternative HUD", "hud_althud", "OnOff" + Option "Show secret count", "hud_showsecrets", "OnOff" + Option "Show monster count", "hud_showmonsters", "OnOff" + Option "Show item count", "hud_showitems", "OnOff" + Option "Show stamina and accuracy", "hud_showstats", "OnOff" + Slider "Red ammo display below %", "hud_ammo_red", 0, 100, 1, 0 + Slider "Yellow ammo display below %", "hud_ammo_yellow", 0, 100, 1, 0 + Slider "Red health display below", "hud_health_red", 0, 100, 1, 0 + Slider "Yellow health display below", "hud_health_yellow", 0, 100, 1, 0 + Slider "Green health display below", "hud_health_green", 0, 100, 1, 0 + Slider "Red armor display below", "hud_armor_red", 0, 100, 1, 0 + Slider "Yellow armor display below", "hud_armor_yellow", 0, 100, 1, 0 + Slider "Green armor display below", "hud_armor_green", 0, 100, 1, 0 + StaticText " " + StaticText "Alternative Automap HUD", 1 + option "Map title color", "hudcolor_titl", "TextColors" + option "Hub time color", "hudcolor_time", "TextColors" + option "Map time color", "hudcolor_ltim", "TextColors" + option "Total title color", "hudcolor_ttim", "TextColors" + option "Coordinate color", "hudcolor_xyco", "TextColors" + option "Coordinate mode", "map_point_coordinates", "AMCoordinates" + option "Map title color", "hudcolor_titl", "TextColors" + option "Statistics name color", "hudcolor_statnames", "TextColors" + option "Statistics color", "hudcolor_stats", "TextColors" +} + +//------------------------------------------------------------------------------------------- +// +// Misc menu +// +//------------------------------------------------------------------------------------------- + +OptionMenu "MiscOptions" +{ + Title "Miscellaneous Options" + //Indent 220 IfOption(Windows) { - Option "Show ENDOOM screen", "showendoom", "Endoom" - //Option "DirectDraw palette hack", "vid_palettehack", "OnOff" - //Option "Use attached surfaces", "vid_attachedsurfaces", "OnOff" + Option "Merge left+right Alt/Ctrl/Shift", "k_mergekeys", "OnOff" + Option "Alt-Enter toggles fullscreen", "k_allowfullscreentoggle", "OnOff" + Option "Show IWAD selection dialog", "queryiwad", "OnOff" + StaticText " " } - + Option "Enable cheats from all games", "allcheats", "OnOff" + Option "Enable autosaves", "disableautosave", "OffOn" + Slider "Number of autosaves", "autosavecount", 1, 32, 1, 0 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" + Option "Cache nodes", "gl_cachenodes", "OnOff" + Slider "Time threshold for node caching", "gl_cachetime", 0.0, 2.0, 0.1 + SafeCommand "Clear node cache", "clearnodecache" } //------------------------------------------------------------------------------------------- @@ -941,7 +1034,7 @@ OptionValue JumpCrouch OptionMenu GameplayOptions { Title "GAMEPLAY OPTIONS" - Indent 222 + //Indent 222 Option "Teamplay", "teamplay", "OnOff" Slider "Team damage scalar", "teamdamage", 0, 1, 0.05 StaticText " " @@ -1185,9 +1278,9 @@ OptionMenu SoundOptions Option "MIDI device", "snd_mididevice", "MidiDevices" StaticText " " Option "Underwater reverb", "snd_waterreverb", "OnOff" - Slider "Underwater cutoff", "snd_waterlp", 0, 2000, 50 + Slider "Underwater cutoff", "snd_waterlp", 0, 2000, 50, 0 Option "Randomize pitches", "snd_pitched", "OnOff" - Slider "Sound channels", "snd_channels", 8, 256, 8 + Slider "Sound channels", "snd_channels", 8, 256, 8, 0 StaticText " " Command "Restart sound", "snd_reset" StaticText " " From a5be18c1c1e26eaa312121e3113fac97e3ce7608 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 12:48:58 +0000 Subject: [PATCH 018/815] - try not to scale the option menu to more than 3/4 of the screen's width if doable. SVN r2796 (trunk) --- src/v_video.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/v_video.cpp b/src/v_video.cpp index c51f13cfc..6beb89292 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1485,6 +1485,12 @@ bool V_DoModeSetup (int width, int height, int bits) { CleanXfac_1 = MAX(CleanXfac - 1, 1); CleanYfac_1 = MAX(CleanYfac - 1, 1); + // On larger screens this is not enough so make sure it's at most 3/4 of the screen's width + while (CleanXfac_1 * 320 > screen->GetWidth()*3/4 && CleanXfac_1 > 2) + { + CleanXfac_1--; + CleanYfac_1--; + } } CleanWidth_1 = width / CleanXfac_1; CleanHeight_1 = height / CleanYfac_1; From 09da3876897296079002d6d392c682b2e84b6b2b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 14:26:56 +0000 Subject: [PATCH 019/815] - added: If a map contains both extended normal and GL nodes the GL nodes loader will now load the prebuilt GL nodes. SVN r2797 (trunk) --- src/p_glnodes.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 382f026b2..8a491f595 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -851,6 +851,48 @@ static int FindGLNodesInFile(FileReader * f, const char * label) bool P_LoadGLNodes(MapData * map) { + + if (map->MapLumps[ML_GLZNODES].Size != 0) + { + const int idcheck = MAKE_ID('Z','G','L','N'); + const int idcheck2 = MAKE_ID('Z','G','L','2'); + const int idcheck3 = MAKE_ID('X','G','L','N'); + const int idcheck4 = MAKE_ID('X','G','L','2'); + int id; + + map->Seek(ML_GLZNODES); + map->file->Read (&id, 4); + if (id == idcheck || id == idcheck2 || id == idcheck3 || id == idcheck4) + { + try + { + subsectors = NULL; + segs = NULL; + nodes = NULL; + P_LoadZNodes (*map->file, id); + return true; + } + catch (CRecoverableError &) + { + if (subsectors != NULL) + { + delete[] subsectors; + subsectors = NULL; + } + if (segs != NULL) + { + delete[] segs; + segs = NULL; + } + if (nodes != NULL) + { + delete[] nodes; + nodes = NULL; + } + } + } + } + if (!CheckCachedNodes(map)) { wadlump_t gwalumps[4]; From 4da5ce74ca432682a7d66ba846e43593da1ca783 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 16:20:39 +0000 Subject: [PATCH 020/815] - fixed: The menu should not override the user's player class if there is no player class menu. SVN r2798 (trunk) --- src/g_level.cpp | 2 +- src/menu/menu.cpp | 1 + src/menu/menudef.cpp | 13 ++++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 10ed1f83e..1c125324a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -233,7 +233,7 @@ void G_DeferedInitNew (const char *mapname, int newskill) void G_DeferedInitNew (FGameStartup *gs) { - playerclass = gs->PlayerClass; + if (gs->PlayerClass != NULL) playerclass = gs->PlayerClass; d_mapname = AllEpisodes[gs->Episode].mEpisodeMap; d_skill = gs->Skill; CheckWarpTransMap (d_mapname, true); diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 6d60344b1..d54b27c1c 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -350,6 +350,7 @@ void M_SetMenu(FName menu, int param) GameStartupInfo.Skill = -1; GameStartupInfo.Episode = -1; GameStartupInfo.PlayerClass = + param == -1000? NULL : param == -1? "Random" : PlayerClasses[param].Type->Meta.GetMetaString (APMETA_DisplayName); break; diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 8b865cbc6..8d37f570c 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -1006,7 +1006,14 @@ static void BuildPlayerclassMenu() // 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) + if (numclassitems <= 1) + { + // create a dummy item that auto-chooses the default class. + FListMenuItemText *it = new FListMenuItemText(0, 0, 0, 'p', "player", + ld->mFont,ld->mFontColor, NAME_Episodemenu, -1000); + ld->mAutoselect = 0; + } + else if (totalheight <= 190) { int newtop = (200 - totalheight + topy) / 2; int topdelta = newtop - topy; @@ -1051,10 +1058,6 @@ static void BuildPlayerclassMenu() ld->mItems.Push(it); } } - if (n < 2) - { - ld->mAutoselect = ld->mSelectedItem; - } success = true; } } From 491da14d9b1e772d9245c1e50c3ed8602d75f221 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 16:29:23 +0000 Subject: [PATCH 021/815] - Changed the ambient sound timer to use level.maptime instead of gametic so that it gets paused along with the rest of the game automatically. SVN r2799 (trunk) --- src/s_advsound.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 46d4bc813..39ef8f75c 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2013,7 +2013,7 @@ void AAmbientSound::Serialize (FArchive &arc) NextCheck += gametic; if (NextCheck < 0) NextCheck = INT_MAX; } - else + else if (SaveVersion < 2798) { if (arc.IsStoring()) { @@ -2036,6 +2036,10 @@ void AAmbientSound::Serialize (FArchive &arc) } } } + else + { + arc << NextCheck; + } } //========================================================================== @@ -2048,7 +2052,7 @@ void AAmbientSound::Tick () { Super::Tick (); - if (!bActive || gametic < NextCheck) + if (!bActive || level.maptime < NextCheck) return; FAmbientSound *ambient; @@ -2176,7 +2180,7 @@ void AAmbientSound::Activate (AActor *activator) amb->periodmin = Scale(S_GetMSLength(sndnum), TICRATE, 1000); } - NextCheck = gametic; + NextCheck = level.maptime; if (amb->type & (RANDOM|PERIODIC)) SetTicker (amb); From 6db772b4e58998f5d08e08cbdb99b0e38607e224 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 17:01:42 +0000 Subject: [PATCH 022/815] - fixed the player class selection fix from r2798. SVN r2800 (trunk) --- src/menu/menudef.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 8d37f570c..25fce4e7b 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -1011,7 +1011,8 @@ static void BuildPlayerclassMenu() // create a dummy item that auto-chooses the default class. FListMenuItemText *it = new FListMenuItemText(0, 0, 0, 'p', "player", ld->mFont,ld->mFontColor, NAME_Episodemenu, -1000); - ld->mAutoselect = 0; + ld->mAutoselect = ld->mItems.Push(it); + success = true; } else if (totalheight <= 190) { From 6789b8936c4ccd02457a74a4912d6fe1d2b105f2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 20:15:44 +0000 Subject: [PATCH 023/815] - fixed: When moving a sector plane P_CheckPosition must do the same anti-overlap checks for monsters like P_Move to avoid monster pile-ups. SVN r2801 (trunk) --- src/p_map.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index f578ec7fe..424591be5 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -844,16 +844,23 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) if (tm.FromPMove) { - fixed_t newdist = P_AproxDistance(thing->x - tm.x, thing->y - tm.y); - fixed_t olddist = P_AproxDistance(thing->x - tm.thing->x, thing->y - tm.thing->y); - // Both actors already overlap. To prevent them from remaining stuck allow the move if it - // takes them further apart. - if (newdist > olddist) + // takes them further apart or the move does not change the position (when called from P_ChangeSector.) + if (tm.x == tm.thing->x && tm.y == tm.thing->y) { - // ... but not if they did not overlap in z-direction before but would after the move. - unblocking = !((tm.thing->x >= thing->x + thing->height && tm.x < thing->x + thing->height) || - (tm.thing->x + tm.thing->height <= thing->x && tm.x + tm.thing->height > thing->x)); + unblocking = true; + } + else + { + fixed_t newdist = P_AproxDistance(thing->x - tm.x, thing->y - tm.y); + fixed_t olddist = P_AproxDistance(thing->x - tm.thing->x, thing->y - tm.thing->y); + + if (newdist > olddist) + { + // ... but not if they did not overlap in z-direction before but would after the move. + unblocking = !((tm.thing->z >= thing->z + thing->height && tm.z < thing->z + thing->height) || + (tm.thing->z + tm.thing->height <= thing->z && tm.z + tm.thing->height > thing->z)); + } } } @@ -4512,6 +4519,11 @@ bool P_AdjustFloorCeil (AActor *thing, FChangePosition *cpos) int flags2 = thing->flags2 & MF2_PASSMOBJ; FCheckPosition tm; + if ((thing->flags2 & MF2_PASSMOBJ) && (thing->flags3 & MF3_ISMONSTER)) + { + tm.FromPMove = true; + } + if (cpos->movemidtex) { // From Eternity: From 198c714dad7625abbbebf7fe92c014f35347b27e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 20:47:41 +0000 Subject: [PATCH 024/815] - fixed the compatibility handler of AAmbientSound::Serialize. SVN r2802 (trunk) --- src/s_advsound.cpp | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 39ef8f75c..a2fa8ccd0 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2005,40 +2005,10 @@ IMPLEMENT_CLASS (AAmbientSound) void AAmbientSound::Serialize (FArchive &arc) { Super::Serialize (arc); - arc << bActive; - - if (SaveVersion < 1902) + arc << bActive << NextCheck; + if (SaveVersion < 2798) { - arc << NextCheck; - NextCheck += gametic; - if (NextCheck < 0) NextCheck = INT_MAX; - } - else if (SaveVersion < 2798) - { - if (arc.IsStoring()) - { - if (NextCheck != INT_MAX) - { - int checktime = NextCheck - gametic; - arc << checktime; - } - else - { - arc << NextCheck; - } - } - else - { - arc << NextCheck; - if (NextCheck != INT_MAX) - { - NextCheck += gametic; - } - } - } - else - { - arc << NextCheck; + NextCheck += level.maptime; } } From 3424548bec794220ec2097c72777f553a7ed5896 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 22:45:12 +0000 Subject: [PATCH 025/815] - skip all mouse move events that come right after switching to native mouse. Windows sends some when the mouseis ungrabbed even when it does not move. This caused the currently selected menu item to get unselected. SVN r2803 (trunk) --- src/win32/i_input.cpp | 3 +++ src/win32/i_mouse.cpp | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index c5370fb03..678c03b77 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -158,6 +158,7 @@ LPDIRECTINPUT g_pdi3; BOOL AppActive = TRUE; int SessionState = 0; +int BlockMouseMove; CVAR (Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -319,6 +320,7 @@ bool GUIWndProcHook(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESU else if (message == WM_MOUSEMOVE) { ev.subtype = EV_GUI_MouseMove; + if (BlockMouseMove > 0) return true; } { @@ -807,6 +809,7 @@ void I_GetEvent () // void I_StartTic () { + BlockMouseMove--; ResetButtonTriggers (); I_CheckGUICapture (); I_CheckNativeMouse (false); diff --git a/src/win32/i_mouse.cpp b/src/win32/i_mouse.cpp index 0fa46c1db..873b9271a 100644 --- a/src/win32/i_mouse.cpp +++ b/src/win32/i_mouse.cpp @@ -114,6 +114,7 @@ extern HWND Window; extern LPDIRECTINPUT8 g_pdi; extern LPDIRECTINPUT g_pdi3; extern bool GUICapture; +extern int BlockMouseMove; // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -283,6 +284,7 @@ void I_CheckNativeMouse(bool preferNative) NativeMouse = want_native; if (want_native) { + BlockMouseMove = 3; Mouse->Ungrab(); } else From 35aa6e78474840446f6795383472e209c45e5b33 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Sep 2010 23:16:06 +0000 Subject: [PATCH 026/815] - fixed: The player name input box did not 0-terminate the string when temporarily appending an underscore for the cursor display. SVN r2804 (trunk) --- src/menu/playermenu.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index b25b86f1a..b23a4d422 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -162,6 +162,7 @@ void FPlayerNameBox::Drawer(bool selected) { size_t l = strlen(mEditName); mEditName[l] = (gameinfo.gametype & (GAME_DoomStrifeChex)) ? '_' : '['; + mEditName[l+1] = 0; screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, mEditName, DTA_Clean, true, TAG_DONE); From ae3c60454f3adc37b794d5317353164dbf3f0614 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 06:20:08 +0000 Subject: [PATCH 027/815] - fixed: M_Ticker should not run any code when the menu is off. SVN r2805 (trunk) --- src/menu/menu.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index d54b27c1c..8141e870e 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -673,29 +673,30 @@ 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) + for (int i = 0; i < NUM_MKEYS; ++i) { - if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0) + if (MenuButtons[i].bDown) { - MenuButtonTickers[i] = KEY_REPEAT_RATE; - DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]); + 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; + if (BackbuttonTime > 0) + { + if (BackbuttonAlpha < FRACUNIT) BackbuttonAlpha += FRACUNIT/10; + BackbuttonTime--; + } + else + { + if (BackbuttonAlpha > 0) BackbuttonAlpha -= FRACUNIT/10; + if (BackbuttonAlpha < 0) BackbuttonAlpha = 0; + } } } From fa062aa1411c041ecfd6a0bb90be45efdf838d8f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 08:19:02 +0000 Subject: [PATCH 028/815] - fixed: DPlayerMenu::ClassChanged needs to call PickPlayerClass before setting the playerclass CVAR. SVN r2806 (trunk) --- src/menu/playermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index b23a4d422..dab8f29fb 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -899,11 +899,11 @@ void DPlayerMenu::ClassChanged (FListMenuItem *li) if (li->GetValue(0, &sel)) { players[consoleplayer].userinfo.PlayerClass = sel-1; + PickPlayerClass(); cvar_set ("playerclass", sel == 0 ? "Random" : PlayerClass->Type->Meta.GetMetaString (APMETA_DisplayName)); - PickPlayerClass(); UpdateSkins(); UpdateColorsets(); UpdateTranslation(); From e385de010d6e41997218146de5478dbde35f73b6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 10:11:39 +0000 Subject: [PATCH 029/815] - fixed: FValueTextItem::MenuEvent must eat all MKEY_Enter events so that the menu doesn't try to activate the item which results in an attempt to create an invalid submenu. SVN r2807 (trunk) --- src/menu/playermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index dab8f29fb..6965d391b 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -298,7 +298,7 @@ bool FValueTextItem::MenuEvent (int mkey, bool fromcontroller) return true; } } - return false; + return (mkey == MKEY_Enter); // needs to eat enter keys so that Activate won't get called } //============================================================================= From 46b3bb8e4191268c1250b8933e2aecf8ecf56e79 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 13:22:39 +0000 Subject: [PATCH 030/815] - moved BFGSplash damage type to BFGExtra actor so that it can be replaced. SVN r2808 (trunk) --- src/g_doom/a_doomweaps.cpp | 2 +- wadsrc/static/actors/doom/doomweapons.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 515a3015e..f5bf1a800 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -586,7 +586,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) damage += (pr_bfgspray() & 7) + 1; thingToHit = linetarget; - P_DamageMobj (thingToHit, self->target, self->target, damage, NAME_BFGSplash); + P_DamageMobj (thingToHit, self->target, self->target, damage, spray->DamageType); P_TraceBleed (damage, thingToHit, self->target); } } diff --git a/wadsrc/static/actors/doom/doomweapons.txt b/wadsrc/static/actors/doom/doomweapons.txt index b748f8611..4b0386524 100644 --- a/wadsrc/static/actors/doom/doomweapons.txt +++ b/wadsrc/static/actors/doom/doomweapons.txt @@ -587,6 +587,7 @@ ACTOR BFGExtra +NOGRAVITY RenderStyle Add Alpha 0.75 + DamageType "BFGSplash" States { Spawn: From eeab5ba0e4071b8fb45ebc9b924db64a38c59ddb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 22:48:55 +0000 Subject: [PATCH 031/815] - reintegrate savegame menu changes back into trunk SVN r2816 (trunk) --- src/menu/loadsavemenu.cpp | 578 ++++++++++++++++---------------------- src/menu/menu.h | 2 +- 2 files changed, 246 insertions(+), 334 deletions(-) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 130dd0bd5..f4a33522b 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -56,46 +56,66 @@ class DLoadSaveMenu : public DListMenu DECLARE_CLASS(DLoadSaveMenu, DListMenu) protected: - static List SaveGames; - static FSaveGameNode *TopSaveGame; - static FSaveGameNode *lastSaveSlot; - static FSaveGameNode *SelSaveGame; + static TDeletingArray SaveGames; + static int LastSaved; + + int Selected; + int TopItem; - friend void M_NotifyNewSave (const char *file, const char *title, bool okForQuicksave); + int savepicLeft; + int savepicTop; + int savepicWidth; + int savepicHeight; - static FSaveGameNode *RemoveSaveSlot (FSaveGameNode *file); - static void UnloadSaveStrings(); - static void InsertSaveNode (FSaveGameNode *node); + int rowHeight; + int listboxLeft; + int listboxTop; + int listboxWidth; + + int listboxRows; + int listboxHeight; + int listboxRight; + int listboxBottom; + + int commentLeft; + int commentTop; + int commentWidth; + int commentHeight; + int commentRight; + int commentBottom; + + + static int 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(); + int RemoveSaveSlot (int index); void UnloadSaveData (); void ClearSaveStuff (); - void ExtractSaveData (const FSaveGameNode *node); + void ExtractSaveData (int index); void Drawer (); bool MenuEvent (int mkey, bool fromcontroller); bool MouseEvent(int type, int x, int y); bool Responder(event_t *ev); +public: + static void NotifyNewSave (const char *file, const char *title, bool okForQuicksave); + }; IMPLEMENT_CLASS(DLoadSaveMenu) -List DLoadSaveMenu::SaveGames; -FSaveGameNode *DLoadSaveMenu::TopSaveGame; -FSaveGameNode *DLoadSaveMenu::lastSaveSlot; -FSaveGameNode *DLoadSaveMenu::SelSaveGame; +TDeletingArray DLoadSaveMenu::SaveGames; +int DLoadSaveMenu::LastSaved = -1; FSaveGameNode *quickSaveSlot; @@ -105,78 +125,54 @@ FSaveGameNode *quickSaveSlot; // //============================================================================= -FSaveGameNode *DLoadSaveMenu::RemoveSaveSlot (FSaveGameNode *file) +int DLoadSaveMenu::RemoveSaveSlot (int index) { - FSaveGameNode *next = static_cast(file->Succ); + FSaveGameNode *file = SaveGames[index]; - if (file == TopSaveGame) - { - TopSaveGame = next; - } - if (quickSaveSlot == file) + if (quickSaveSlot == SaveGames[index]) { quickSaveSlot = NULL; } - if (lastSaveSlot == file) + if (Selected == index) { - lastSaveSlot = NULL; + Selected = -1; } - file->Remove (); if (!file->bNoDelete) delete file; - return next; + SaveGames.Delete(index); + if ((unsigned)index >= SaveGames.Size()) index--; + return index; } - //============================================================================= // // // //============================================================================= -void DLoadSaveMenu::UnloadSaveStrings() +int DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) { - while (!SaveGames.IsEmpty()) + if (SaveGames.Size() == 0) { - RemoveSaveSlot (static_cast(SaveGames.Head)); - } -} - - -//============================================================================= -// -// -// -//============================================================================= - -void DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) -{ - FSaveGameNode *probe; - - if (SaveGames.IsEmpty ()) - { - SaveGames.AddHead (node); - return; + return SaveGames.Push(node); } 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); + return SaveGames.Push(node); } else - { // Add node at top of list - probe = static_cast(SaveGames.Head); - while (probe->Succ != NULL && !probe->bOldVersion && - stricmp (node->Title, probe->Title) > 0) + { // Add node at top of list + unsigned int i; + for(i = 0; i < SaveGames.Size(); i++) { - probe = static_cast(probe->Succ); + if (SaveGames[i]->bOldVersion || + stricmp (node->Title, SaveGames[i]->Title) <= 0) + { + break; + } } - node->InsertBefore (probe); + SaveGames.Insert(i, node); + return i; } } @@ -191,14 +187,12 @@ void DLoadSaveMenu::InsertSaveNode (FSaveGameNode *node) void DLoadSaveMenu::ReadSaveStrings () { - if (SaveGames.IsEmpty ()) + if (SaveGames.Size() == 0) { 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))) @@ -309,10 +303,6 @@ void DLoadSaveMenu::ReadSaveStrings () I_FindClose (filefirst); } } - if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) - { - SelSaveGame = static_cast(SaveGames.Head); - } } @@ -332,10 +322,9 @@ void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okF ReadSaveStrings (); // See if the file is already in our list - for (node = static_cast(SaveGames.Head); - node->Succ != NULL; - node = static_cast(node->Succ)) + for (unsigned i=0; iFilename.Compare (file) == 0) #else @@ -345,25 +334,26 @@ void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okF strcpy (node->Title, title); node->bOldVersion = false; node->bMissingWads = false; - break; + if (okForQuicksave) + { + if (quickSaveSlot == NULL) quickSaveSlot = node; + LastSaved = i; + } + return; } } - if (node->Succ == NULL) - { - node = new FSaveGameNode; - strcpy (node->Title, title); - node->Filename = file; - node->bOldVersion = false; - node->bMissingWads = false; - InsertSaveNode (node); - SelSaveGame = node; - } + node = new FSaveGameNode; + strcpy (node->Title, title); + node->Filename = file; + node->bOldVersion = false; + node->bMissingWads = false; + int index = InsertSaveNode (node); if (okForQuicksave) { if (quickSaveSlot == NULL) quickSaveSlot = node; - lastSaveSlot = node; + LastSaved = index; } } @@ -382,7 +372,28 @@ DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, FListMenuDescriptor *desc) : DListMenu(parent, desc) { ReadSaveStrings(); - mWheelScrolled = false; + + savepicLeft = 10; + savepicTop = 54*CleanYfac; + savepicWidth = 216*screen->GetWidth()/640; + savepicHeight = 135*screen->GetHeight()/400; + + rowHeight = (SmallFont->GetHeight() + 1) * CleanYfac; + listboxLeft = savepicLeft + savepicWidth + 14; + listboxTop = savepicTop; + listboxWidth = screen->GetWidth() - listboxLeft - 10; + int listboxHeight1 = screen->GetHeight() - listboxTop - 10; + listboxRows = (listboxHeight1 - 1) / rowHeight; + listboxHeight = listboxRows * rowHeight + 1; + listboxRight = listboxLeft + listboxWidth; + listboxBottom = listboxTop + listboxHeight; + + commentLeft = savepicLeft; + commentTop = savepicTop + savepicHeight + 16; + commentWidth = savepicWidth; + commentHeight = (51+(screen->GetHeight()>200?10:0))*CleanYfac; + commentRight = commentLeft + commentWidth; + commentBottom = commentTop + commentHeight; } void DLoadSaveMenu::Destroy() @@ -420,7 +431,7 @@ void DLoadSaveMenu::UnloadSaveData () void DLoadSaveMenu::ClearSaveStuff () { UnloadSaveData(); - if (quickSaveSlot == (FSaveGameNode *)1) + if (quickSaveSlot == (FSaveGameNode*)1) { quickSaveSlot = NULL; } @@ -432,15 +443,16 @@ void DLoadSaveMenu::ClearSaveStuff () // //============================================================================= -void DLoadSaveMenu::ExtractSaveData (const FSaveGameNode *node) +void DLoadSaveMenu::ExtractSaveData (int index) { FILE *file; PNGHandle *png; + FSaveGameNode *node; UnloadSaveData (); - if (node != NULL && - node->Succ != NULL && + if ((unsigned)index < SaveGames.Size() && + (node = SaveGames[index]) && !node->Filename.IsEmpty() && !node->bOldVersion && (file = fopen (node->Filename.GetChars(), "rb")) != NULL) @@ -512,30 +524,9 @@ 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; + unsigned j; bool didSeeSelected = false; // Draw picture area @@ -558,10 +549,10 @@ void DLoadSaveMenu::Drawer () screen->Clear (savepicLeft, savepicTop, savepicLeft+savepicWidth, savepicTop+savepicHeight, 0, 0); - if (!SaveGames.IsEmpty ()) + if (SaveGames.Size() > 0) { const char *text = - (SelSaveGame == NULL || !SelSaveGame->bOldVersion) + (Selected == -1 || SaveGames[Selected]->bOldVersion) ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); const int textlen = SmallFont->StringWidth (text)*CleanXfac; @@ -588,110 +579,73 @@ void DLoadSaveMenu::Drawer () } // Draw file area - do + V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); + screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + + if (SaveGames.Size() == 0) { - V_DrawFrame (listboxLeft, listboxTop, listboxWidth, listboxHeight); - screen->Clear (listboxLeft, listboxTop, listboxRight, listboxBottom, 0, 0); + const char * text = GStrings("MNU_NOFILES"); + const int textlen = SmallFont->StringWidth (text)*CleanXfac; - if (SaveGames.IsEmpty ()) + screen->DrawText (SmallFont, CR_GOLD, listboxLeft+(listboxWidth-textlen)/2, + listboxTop+(listboxHeight-rowHeight)/2, text, + DTA_CleanNoMove, true, TAG_DONE); + return; + } + + for (i = 0, j = TopItem; i < listboxRows && j < SaveGames.Size(); i++,j++) + { + int color; + node = SaveGames[j]; + if (node->bOldVersion) { - 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; + color = CR_BLUE; } - - for (i = 0, node = TopSaveGame; - i < listboxRows && node->Succ != NULL; - ++i, node = static_cast(node->Succ)) + else if (node->bMissingWads) { - 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 + color = CR_ORANGE; + } + else if (j == Selected) + { + color = CR_WHITE; + } + else + { + color = CR_TAN; + } + if (j == Selected) + { + screen->Clear (listboxLeft, listboxTop+rowHeight*i, + listboxRight, listboxTop+rowHeight*(i+1), -1, + mEntering ? MAKEARGB(255,255,0,0) : MAKEARGB(255,0,0,255)); + didSeeSelected = true; + if (!mEntering) { screen->DrawText (SmallFont, color, listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, DTA_CleanNoMove, true, TAG_DONE); } - } - - // 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); + 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); } } - } while (!didSeeSelected); -} + else + { + screen->DrawText (SmallFont, color, + listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, node->Title, + DTA_CleanNoMove, true, TAG_DONE); + } + } +} //============================================================================= // @@ -704,47 +658,68 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) switch (mkey) { case MKEY_Up: - if (SelSaveGame == NULL) + if (SaveGames.Size() > 1) { - SelSaveGame = TopSaveGame; - } - else if (SelSaveGame->Succ != NULL) - { - if (SelSaveGame != SaveGames.Head) - { - if (SelSaveGame == TopSaveGame) - { - TopSaveGame = static_cast(TopSaveGame->Pred); - } - SelSaveGame = static_cast(SelSaveGame->Pred); - } + if (Selected == -1) Selected = TopItem; else { - SelSaveGame = static_cast(SaveGames.TailPred); + if (--Selected < 0) Selected = SaveGames.Size()-1; + if (Selected < TopItem) TopItem = Selected; + else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); } UnloadSaveData (); - ExtractSaveData (SelSaveGame); + ExtractSaveData (Selected); } return true; case MKEY_Down: - if (SelSaveGame == NULL) + if (SaveGames.Size() > 1) { - SelSaveGame = TopSaveGame; - } - else if (SelSaveGame->Succ != NULL) - { - if (SelSaveGame != SaveGames.TailPred) + if (Selected == -1) Selected = TopItem; + else { - SelSaveGame = static_cast(SelSaveGame->Succ); + if (unsigned(++Selected) >= SaveGames.Size()) Selected = 0; + if (Selected < TopItem) TopItem = Selected; + else if (Selected >= TopItem + listboxRows) TopItem = MAX(0, Selected - listboxRows + 1); + } + UnloadSaveData (); + ExtractSaveData (Selected); + } + return true; + + case MKEY_PageDown: + if (SaveGames.Size() > 1) + { + if (TopItem >= (int)SaveGames.Size() - listboxRows) + { + TopItem = 0; + if (Selected != -1) Selected = 0; } else { - SelSaveGame = TopSaveGame = - static_cast(SaveGames.Head); + TopItem = MIN(TopItem + listboxRows, SaveGames.Size() - listboxRows); + if (TopItem > Selected && Selected != -1) Selected = TopItem; } UnloadSaveData (); - ExtractSaveData (SelSaveGame); + ExtractSaveData (Selected); + } + return true; + + case MKEY_PageUp: + if (SaveGames.Size() > 1) + { + if (TopItem == 0) + { + TopItem = SaveGames.Size() - listboxRows; + if (Selected != -1) Selected = TopItem; + } + else + { + TopItem = MAX(TopItem - listboxRows, 0); + if (Selected >= TopItem + listboxRows) Selected = TopItem; + } + UnloadSaveData (); + ExtractSaveData (Selected); } return true; @@ -753,22 +728,12 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) case MKEY_MBYes: { - if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + if (Selected != -1) { - 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()); + remove (SaveGames[Selected]->Filename.GetChars()); UnloadSaveData (); - SelSaveGame = RemoveSaveSlot (SelSaveGame); - ExtractSaveData (SelSaveGame); + Selected = RemoveSaveSlot (Selected); + ExtractSaveData (Selected); } return true; } @@ -786,39 +751,16 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool 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) + + if (TopItem + lineno < (int)SaveGames.Size()) { - lineno--; - top = (FSaveGameNode *)top->Succ; - } - if (lineno == 0) - { - if (SelSaveGame != top) - { - SelSaveGame = top; - UnloadSaveData (); - ExtractSaveData (SelSaveGame); - // Sound? - } + Selected = TopItem + lineno; + UnloadSaveData (); + ExtractSaveData (Selected); if (type == MOUSE_Release) { if (MenuEvent(MKEY_Enter, true)) @@ -827,12 +769,10 @@ bool DLoadSaveMenu::MouseEvent(int type, int x, int y) } } } - else SelSaveGame = NULL; - } - else - { - SelSaveGame = NULL; + else Selected = -1; } + else Selected = -1; + return Super::MouseEvent(type, x, y); } @@ -848,16 +788,16 @@ bool DLoadSaveMenu::Responder (event_t *ev) { if (ev->subtype == EV_GUI_KeyDown) { - if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + if (Selected != -1) { switch (ev->data1) { case GK_F1: - if (!SelSaveGame->Filename.IsEmpty()) + if (!SaveGames[Selected]->Filename.IsEmpty()) { char workbuf[512]; - mysnprintf (workbuf, countof(workbuf), "File on disk:\n%s", SelSaveGame->Filename.GetChars()); + mysnprintf (workbuf, countof(workbuf), "File on disk:\n%s", SaveGames[Selected]->Filename.GetChars()); if (SaveComment != NULL) { V_FreeBrokenLines (SaveComment); @@ -871,7 +811,7 @@ bool DLoadSaveMenu::Responder (event_t *ev) { FString EndString; EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", - GStrings("MNU_DELETESG"), SelSaveGame->Title, GStrings("PRESSYN")); + GStrings("MNU_DELETESG"), SaveGames[Selected]->Title, GStrings("PRESSYN")); M_StartMessage (EndString, 0); } return true; @@ -880,36 +820,13 @@ bool DLoadSaveMenu::Responder (event_t *ev) } else if (ev->subtype == EV_GUI_WheelUp) { - if (TopSaveGame != SaveGames.Head && TopSaveGame != NULL) - { - TopSaveGame = static_cast(TopSaveGame->Pred); - mWheelScrolled = true; - } + if (TopItem > 0) TopItem--; 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; - } - } + if (TopItem < (int)SaveGames.Size() - listboxRows) TopItem++; + return true; } } return Super::Responder(ev); @@ -952,17 +869,17 @@ DSaveMenu::DSaveMenu(DMenu *parent, FListMenuDescriptor *desc) { strcpy (NewSaveNode.Title, ""); NewSaveNode.bNoDelete = true; - SaveGames.AddHead (&NewSaveNode); - TopSaveGame = static_cast(SaveGames.Head); - if (lastSaveSlot == NULL) + SaveGames.Insert(0, &NewSaveNode); + TopItem = 0; + if (LastSaved == -1) { - SelSaveGame = &NewSaveNode; + Selected = 0; } else { - SelSaveGame = lastSaveSlot; + Selected = LastSaved + 1; } - ExtractSaveData (SelSaveGame); + ExtractSaveData (Selected); } //============================================================================= @@ -973,17 +890,11 @@ DSaveMenu::DSaveMenu(DMenu *parent, FListMenuDescriptor *desc) void DSaveMenu::Destroy() { - if (SaveGames.Head == &NewSaveNode) + if (SaveGames[0] == &NewSaveNode) { - SaveGames.RemHead (); - if (SelSaveGame == &NewSaveNode) - { - SelSaveGame = static_cast(SaveGames.Head); - } - if (TopSaveGame == &NewSaveNode) - { - TopSaveGame = static_cast(SaveGames.Head); - } + SaveGames.Delete(0); + if (Selected == 0) Selected = -1; + else Selected--; } } @@ -1034,16 +945,16 @@ bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) { return true; } - if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) + if (Selected == -1) { return false; } if (mkey == MKEY_Enter) { - if (SelSaveGame != &NewSaveNode) + if (Selected != 0) { - strcpy (savegamestring, SelSaveGame->Title); + strcpy (savegamestring, SaveGames[Selected]->Title); } else { @@ -1056,7 +967,7 @@ bool DSaveMenu::MenuEvent (int mkey, bool fromcontroller) else if (mkey == MKEY_Input) { mEntering = false; - DoSave(SelSaveGame); + DoSave(SaveGames[Selected]); } else if (mkey == MKEY_Abort) { @@ -1075,18 +986,18 @@ bool DSaveMenu::Responder (event_t *ev) { if (ev->subtype == EV_GUI_KeyDown) { - if (SelSaveGame != NULL && SelSaveGame->Succ != NULL) + if (Selected != -1) { switch (ev->data1) { case GK_DEL: case '\b': // cannot delete 'new save game' item - if (SelSaveGame == &NewSaveNode) return true; + if (Selected == 0) return true; break; case 'N': - SelSaveGame = TopSaveGame = &NewSaveNode; + Selected = TopItem = 0; UnloadSaveData (); return true; } @@ -1124,8 +1035,9 @@ IMPLEMENT_CLASS(DLoadMenu) DLoadMenu::DLoadMenu(DMenu *parent, FListMenuDescriptor *desc) : DLoadSaveMenu(parent, desc) { - TopSaveGame = static_cast(SaveGames.Head); - ExtractSaveData (SelSaveGame); + TopItem = 0; + ExtractSaveData (Selected); + } //============================================================================= @@ -1140,21 +1052,21 @@ bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) { return true; } - if (SelSaveGame == NULL || SelSaveGame->Succ == NULL) + if (Selected == -1) { return false; } if (mkey == MKEY_Enter) { - G_LoadGame (SelSaveGame->Filename.GetChars()); + G_LoadGame (SaveGames[Selected]->Filename.GetChars()); if (gamestate == GS_FULLCONSOLE) { gamestate = GS_HIDECONSOLE; } - if (quickSaveSlot == (FSaveGameNode *)1) + if (quickSaveSlot == (FSaveGameNode*)1) { - quickSaveSlot = SelSaveGame; + quickSaveSlot = SaveGames[Selected]; } M_ClearMenus(); BorderNeedRefresh = screen->GetPageCount (); diff --git a/src/menu/menu.h b/src/menu/menu.h index b1073a041..c4dc8b9e5 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -56,7 +56,7 @@ struct FGameStartup extern FGameStartup GameStartupInfo; -struct FSaveGameNode : public Node +struct FSaveGameNode { char Title[SAVESTRINGSIZE]; FString Filename; From 3090ad7be65eab1f31087f4a8777b5901248e2b7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Sep 2010 22:57:04 +0000 Subject: [PATCH 032/815] - we don't need to include lists.h any longer. SVN r2817 (trunk) --- src/menu/loadsavemenu.cpp | 1 - src/menu/menu.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index f4a33522b..cb73c2586 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -35,7 +35,6 @@ #include "menu/menu.h" #include "i_system.h" -#include "lists.h" #include "version.h" #include "g_game.h" #include "m_png.h" diff --git a/src/menu/menu.h b/src/menu/menu.h index c4dc8b9e5..07acd7849 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -5,7 +5,6 @@ #include "dobject.h" -#include "lists.h" #include "d_player.h" #include "r_translate.h" #include "c_cvars.h" From 5871712fd408858ddf82cd71a6466651e4754c3a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 18 Sep 2010 02:12:49 +0000 Subject: [PATCH 033/815] - Fixed incorrect definition of FLUID_FAILED when building with DYN_FLUIDSYNTH enabled. SVN r2818 (trunk) --- src/sound/i_musicinterns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 8c6507167..ccf014555 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -287,7 +287,7 @@ protected: fluid_synth_t *FluidSynth; #ifdef DYN_FLUIDSYNTH - enum { FLUID_FAILED = 1, FLUID_OK = 0 }; + enum { FLUID_FAILED = -1, FLUID_OK = 0 }; fluid_settings_t *(STACK_ARGS *new_fluid_settings)(); fluid_synth_t *(STACK_ARGS *new_fluid_synth)(fluid_settings_t *); int (STACK_ARGS *delete_fluid_synth)(fluid_synth_t *); From eb031d73e74b7f535111e01f53e9f10000761210 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 18 Sep 2010 02:55:08 +0000 Subject: [PATCH 034/815] - Change the time when walking monsters step down steps to just after the step rather than just before. This allows for thrusting walking monsters vertically without them suddenly warping to the ground. SVN r2819 (trunk) --- src/p_enemy.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 1cf28aa5d..1a3de085a 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -427,20 +427,14 @@ bool P_Move (AActor *actor) return false; } - // [RH] Instead of yanking non-floating monsters to the ground, - // let gravity drop them down, unless they're moving down a step. + // [RH] Walking actors that are not on the ground cannot walk. We don't + // want to yank them to the ground here as Doom did, since that makes + // it difficult ot thrust them vertically in a reasonable manner. // [GZ] Let jumping actors jump. if (!((actor->flags & MF_NOGRAVITY) || (actor->flags6 & MF6_CANJUMP)) && actor->z > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) { - if (actor->z > actor->floorz + actor->MaxStepHeight) - { - return false; - } - else - { - actor->z = actor->floorz; - } + return false; } if ((unsigned)actor->movedir >= 8) @@ -537,6 +531,26 @@ bool P_Move (AActor *actor) actor->vely += FixedMul (deltay, movefactor); } + // [RH] If a walking monster is no longer on the floor, move it down + // to the floor if it is within MaxStepHeight, presuming that it is + // actually walking down a step. + if (try_ok && + !((actor->flags & MF_NOGRAVITY) || (actor->flags6 & MF6_CANJUMP)) + && actor->z > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) + { + if (actor->z <= actor->floorz + actor->MaxStepHeight) + { + fixed_t savedz = actor->z; + actor->z = actor->floorz; + // Make sure that there isn't some other actor between us and + // the floor we could get stuck in. The old code did not do this. + if (!P_TestMobjZ(actor)) + { + actor->z = savedz; + } + } + } + if (!try_ok) { if (((actor->flags6 & MF6_CANJUMP)||(actor->flags & MF_FLOAT)) && tm.floatok) From 051ae3224f0c46532fa378f94f0c62f52980afd9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 18 Sep 2010 12:37:22 +0000 Subject: [PATCH 035/815] - fixed: The subsector serializing code accessed the subsector array before validating the index. - added episode names to episode definitions of Doom 1 and Chex Quest. SVN r2820 (trunk) --- src/p_saveg.cpp | 2 +- wadsrc/static/mapinfo/chex.txt | 1 + wadsrc/static/mapinfo/doom1.txt | 7 +++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 52ebf88d2..7381aaf31 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -590,7 +590,7 @@ void P_SerializeSubsectors(FArchive &arc) by = 0; for(int j=0;j<8;j++) { - if ((subsectors[i+j].flags & SSECF_DRAWN) && i+j Date: Sat, 18 Sep 2010 16:08:10 +0000 Subject: [PATCH 036/815] - resurrected some old statistics code I had and made some minor enhancements to be of more use. SVN r2821 (trunk) --- src/CMakeLists.txt | 2 + src/d_main.cpp | 2 + src/g_game.cpp | 5 + src/g_level.cpp | 19 +- src/g_level.h | 3 + src/g_mapinfo.cpp | 2 + src/statistics.cpp | 606 +++++++++++++++++++++++++++++++++++++++++++++ src/strnatcmp.c | 178 +++++++++++++ src/strnatcmp.h | 39 +++ zdoom.vcproj | 12 + 10 files changed, 853 insertions(+), 15 deletions(-) create mode 100644 src/statistics.cpp create mode 100644 src/strnatcmp.c create mode 100644 src/strnatcmp.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e34d8ea3d..672267e54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -728,8 +728,10 @@ add_executable( zdoom WIN32 s_sound.cpp sc_man.cpp st_stuff.cpp + statistics.cpp stats.cpp stringtable.cpp + strnatcmp.c tables.cpp teaminfo.cpp tempfiles.cpp diff --git a/src/d_main.cpp b/src/d_main.cpp index 9dc18f473..cbed77f80 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -116,6 +116,7 @@ void DrawHUD(); // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +extern void ReadStatistics(); extern void M_RestoreMode (); extern void M_SetDefaultMode (); extern void R_ExecuteSetViewSize (); @@ -2073,6 +2074,7 @@ void D_DoomMain (void) // [RH] Parse through all loaded mapinfo lumps Printf ("G_ParseMapInfo: Load map definitions.\n"); G_ParseMapInfo (iwad_info->MapInfo); + ReadStatistics(); // [RH] Parse any SNDINFO lumps Printf ("S_InitData: Load sound definitions.\n"); diff --git a/src/g_game.cpp b/src/g_game.cpp index caf43cd6f..35e740f40 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -104,6 +104,9 @@ void G_DoWorldDone (void); void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description); void G_DoAutoSave (); +void STAT_Write(FILE *file); +void STAT_Read(PNGHandle *png); + FIntCVar gameskill ("skill", 2, CVAR_SERVERINFO|CVAR_LATCH); CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH); CVAR (Bool, chasedemo, false, 0); @@ -1791,6 +1794,7 @@ void G_DoLoadGame () } G_ReadSnapshots (png); + STAT_Read(png); FRandom::StaticReadRNGState (png); P_ReadACSDefereds (png); @@ -2052,6 +2056,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } G_WriteSnapshots (stdfile); + STAT_Write(stdfile); FRandom::StaticWriteRNGState (stdfile); P_WriteACSDefereds (stdfile); diff --git a/src/g_level.cpp b/src/g_level.cpp index 1c125324a..610a1108a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -83,18 +83,9 @@ #include "g_hub.h" +void STAT_StartNewGame(const char *lev); +void STAT_ChangeLevel(const char *newl); -#ifndef STAT -#define STAT_NEW(map) -#define STAT_END(newl) -#define STAT_READ(png) -#define STAT_WRITE(f) -#else -void STAT_NEW(const char *lev); -void STAT_END(const char *newl); -void STAT_READ(PNGHandle *png); -void STAT_WRITE(FILE *f); -#endif EXTERN_CVAR (Float, sv_gravity) EXTERN_CVAR (Float, sv_aircontrol) @@ -502,7 +493,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) for (i = 0; i < MAXPLAYERS; i++) players[i].playerstate = PST_ENTER; // [BC] - STAT_NEW(mapname); + STAT_StartNewGame(mapname); } usergame = !bTitleLevel; // will be set false if a demo @@ -614,7 +605,7 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true); unloading = false; - STAT_END(nextlevel); + STAT_ChangeLevel(nextlevel); if (thiscluster && (thiscluster->flags & CLUSTER_HUB)) { @@ -1652,7 +1643,6 @@ void G_WriteSnapshots (FILE *file) { unsigned int i; - STAT_WRITE(file); for (i = 0; i < wadlevelinfos.Size(); i++) { if (wadlevelinfos[i].snapshot) @@ -1803,7 +1793,6 @@ void G_ReadSnapshots (PNGHandle *png) arc << pnum; } } - STAT_READ(png); png->File->ResetFilePtr(); } diff --git a/src/g_level.h b/src/g_level.h index cf7fd2d48..f04e222d5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -202,6 +202,9 @@ enum ELevelFlags LEVEL2_POLYGRIND = 0x02000000, // Polyobjects grind corpses to gibs. LEVEL2_RESETINVENTORY = 0x04000000, // Resets player inventory when starting this level (unless in a hub) LEVEL2_RESETHEALTH = 0x08000000, // Resets player health when starting this level (unless in a hub) + + LEVEL2_NOSTATISTICS = 0x10000000, // This level should not have statistics collected + LEVEL2_ENDGAME = 0x20000000, // This is an epilogue level that cannot be quit. }; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 6b3faa78a..42beba873 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1377,6 +1377,8 @@ MapFlagHandlers[] = { "no_grinding_polyobj", MITYPE_CLRFLAG2, LEVEL2_POLYGRIND, 0 }, { "resetinventory", MITYPE_SETFLAG2, LEVEL2_RESETINVENTORY, 0 }, { "resethealth", MITYPE_SETFLAG2, LEVEL2_RESETHEALTH, 0 }, + { "endofgame", MITYPE_SETFLAG2, LEVEL2_ENDGAME, 0 }, + { "nostatistics", MITYPE_SETFLAG2, LEVEL2_NOSTATISTICS, 0 }, { "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 }, { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes { "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX}, diff --git a/src/statistics.cpp b/src/statistics.cpp new file mode 100644 index 000000000..a5c85bcd5 --- /dev/null +++ b/src/statistics.cpp @@ -0,0 +1,606 @@ +/* +** +** statistics.cpp +** Save game statistics to a file +** +**--------------------------------------------------------------------------- +** 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 + +#include "strnatcmp.h" +#include "gi.h" +#include "g_level.h" +#include "gstrings.h" +#include "doomstat.h" +#include "configfile.h" +#include "c_dispatch.h" +#include "c_console.h" +#include "d_gui.h" +#include "d_dehacked.h" +#include "g_game.h" +#include "m_png.h" +#include "m_misc.h" +#include "doomerrors.h" +#include "w_wad.h" +#include "hu_stuff.h" +#include "p_local.h" +#include "m_png.h" +#include "p_setup.h" +#include "s_Sound.h" +#include "wi_stuff.h" +#include "sc_man.h" +#include "cmdlib.h" +#include "p_terrain.h" +#include "decallib.h" +#include "a_doomglobal.h" +#include "autosegs.h" +#include "i_cd.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "v_text.h" +#include "r_sky.h" +#include "p_lnspec.h" +#include "m_crc32.h" + +CVAR(Int, savestatistics, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(String, statfile, "zdoomstat.txt", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +//========================================================================== +// +// Global statistics data +// +//========================================================================== + +// This struct is used to track statistics data in game +struct OneLevel +{ + int totalkills, killcount; + int totalsecrets, secretcount; + int leveltime; + char levelname[9]; +}; + +// Current game's statistics +static TArray LevelData; +static FEpisode *StartEpisode; + +// The statistics for one level +struct FLevelStatistics +{ + char info[30]; + short skill; + short playerclass; + char name[12]; + int timeneeded; +}; + +// Statistics for one episode playthrough +struct FSessionStatistics : public FLevelStatistics +{ + TArray levelstats; +}; + +// Collected statistics for one episode +struct FStatistics +{ + TArray stats; + FString epi_name; + FString epi_header; +}; + +// All statistics ever collected +static TArray EpisodeStatistics; + +extern TArray wadlevelinfos; + +//========================================================================== +// +// Initializes statistics data from external file +// +//========================================================================== + +static void ParseStatistics(const char *fn, TArray &statlist) +{ + try + { + FScanner sc; + sc.OpenFile(fn); + + while (sc.GetString()) + { + FStatistics &ep_entry = statlist[statlist.Reserve(1)]; + + ep_entry.epi_header = sc.String; + sc.MustGetString(); + ep_entry.epi_name = sc.String; + + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + FSessionStatistics &session = ep_entry.stats[ep_entry.stats.Reserve(1)]; + + sc.MustGetString(); + sc.MustGetString(); + strncpy(session.name, sc.String, 12); + sc.MustGetString(); + strncpy(session.info, sc.String, 30); + + int h,m,s; + sc.MustGetString(); + sscanf(sc.String, "%d:%d:%d", &h, &m, &s); + session.timeneeded= ((((h*60)+m)*60)+s)*TICRATE; + + sc.MustGetNumber(); + session.skill=sc.Number; + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + FLevelStatistics &lstats = session.levelstats[session.levelstats.Reserve(1)]; + + sc.MustGetString(); + strncpy(lstats.name, sc.String, 12); + sc.MustGetString(); + strncpy(lstats.info, sc.String, 30); + + int h,m,s; + sc.MustGetString(); + sscanf(sc.String, "%d:%d:%d", &h, &m, &s); + lstats.timeneeded= ((((h*60)+m)*60)+s)*TICRATE; + + lstats.skill = 0; + } + } + } + } + } + catch(CRecoverableError &) + { + } +} + + +// ==================================================================== +// +// Reads the statistics file +// +// ==================================================================== + +void ReadStatistics() +{ + ParseStatistics(statfile, EpisodeStatistics); +} + +// ==================================================================== +// +// Saves the statistics file +// Sorting helpers. +// +// ==================================================================== + +int STACK_ARGS compare_episode_names(const void *a, const void *b) +{ + FStatistics *A = (FStatistics*)a; + FStatistics *B = (FStatistics*)b; + + return strnatcasecmp(A->epi_header, B->epi_header); +} + +int STACK_ARGS compare_level_names(const void *a, const void *b) +{ + FLevelStatistics *A = (FLevelStatistics*)a; + FLevelStatistics *B = (FLevelStatistics*)b; + + return strnatcasecmp(A->name, B->name); +} + +int STACK_ARGS compare_dates(const void *a, const void *b) +{ + FLevelStatistics *A = (FLevelStatistics*)a; + FLevelStatistics *B = (FLevelStatistics*)b; + char *p; + + int aday = strtol(A->name, &p, 10); + int amonth = strtol(p+1, &p, 10); + int ayear = strtol(p+1, &p, 10); + int av = aday + 100 * amonth + 2000*ayear; + + int bday = strtol(B->name, &p, 10); + int bmonth = strtol(p+1, &p, 10); + int byear = strtol(p+1, &p, 10); + int bv = bday + 100 * bmonth + 2000*byear; + + return av-bv; +} + + +// ==================================================================== +// +// Main save routine +// +// ==================================================================== + +inline int hours(int v) { return v / (60*60*TICRATE); } +inline int minutes(int v) { return (v % (60*60*TICRATE)) / (60*TICRATE); } +inline int seconds(int v) { return (v % (60*TICRATE))/TICRATE; } + +static void SaveStatistics(const char *fn, TArray &statlist) +{ + unsigned int j; + + FILE * f = fopen(fn, "wt"); + if (f==NULL) return; + + qsort(&statlist[0], statlist.Size(), sizeof(statlist[0]), compare_episode_names); + for(unsigned i=0;iinfo[0]>0) + { + fprintf(f,"\t%2i. %10s \"%-22s\" %02d:%02d:%02d %i\n", j+1, sst->name, sst->info, + hours(sst->timeneeded), minutes(sst->timeneeded), seconds(sst->timeneeded), sst->skill); + + TArray &ls = sst->levelstats; + if (ls.Size() > 0) + { + fprintf(f,"\t{\n"); + + qsort(&ls[0], ls.Size(), sizeof(ls[0]), compare_level_names); + + for(unsigned k=0;k &statlist, const char *section, const char *fullname) +{ + for(unsigned int i=0;iepi_header = section; + stats->epi_name = fullname; + return stats; +} + +// ==================================================================== +// +// Adds a statistics entry +// +// ==================================================================== +static FSessionStatistics *StatisticsEntry(FStatistics *stats, const char *text, int playtime) +{ + FSessionStatistics s; + time_t clock; + struct tm *lt; + + time (&clock); + lt = localtime (&clock); + + if (lt != NULL) + mysnprintf(s.name, countof(s.name), "%02d.%02d.%04d",lt->tm_mday, lt->tm_mon+1, lt->tm_year+1900); + else + strcpy(s.name,"00.00.0000"); + + s.skill=G_SkillProperty(SKILLP_ACSReturn); + strcpy(s.info, text); + s.timeneeded=playtime; + + stats->stats.Push(s); + return &stats->stats[stats->stats.Size()-1]; +} + +// ==================================================================== +// +// Adds a statistics entry +// +// ==================================================================== +static void LevelStatEntry(FSessionStatistics *es, const char *level, const char *text, int playtime) +{ + FLevelStatistics s; + time_t clock; + struct tm *lt; + + time (&clock); + lt = localtime (&clock); + + strcpy(s.name, level); + strcpy(s.info, text); + s.timeneeded=playtime; + es->levelstats.Push(s); +} + + + +//========================================================================== +// +// STAT_StartNewGame: called when a new game starts. Sets the current episode +// +//========================================================================== + +void STAT_StartNewGame(const char *mapname) +{ + LevelData.Clear(); + if (!deathmatch && !multiplayer) + { + for(unsigned int j=0;j it; + AActor *ac; + int mc = 0; + + while ((ac = it.Next())) + { + if ((ac->flags & MF_COUNTKILL) && ac->health > 0) mc++; + } + if (mc == 0) LevelData[i].killcount = LevelData[i].totalkills; + } +} + +//========================================================================== +// +// STAT_ChangeLevel: called when the level changes or the current statistics are +// requested +// +//========================================================================== + +void STAT_ChangeLevel(const char *newl) +{ + // record the current level's stats. + StoreLevelStats(); + + level_info_t *thisinfo = level.info; + level_info_t *nextinfo = NULL; + + if (strncmp(newl, "enDSeQ", 6)) + { + level_info_t *l = FindLevelInfo (newl); + nextinfo = l->CheckLevelRedirect (); + if (nextinfo == NULL) nextinfo = l; + + } + + if (savestatistics == 1) + { + if ((nextinfo == NULL || (nextinfo->flags2 & LEVEL2_ENDGAME)) && StartEpisode != NULL) + { + // we reached the end of this episode + int wad = 0; + MapData * map = P_OpenMapData(StartEpisode->mEpisodeMap); + if (map != NULL) + { + wad = Wads.GetLumpFile(map->lumpnum); + delete map; + } + const char * name = Wads.GetWadName(wad); + FString section = ExtractFileBase(name) + "." + StartEpisode->mEpisodeMap; + section.ToUpper(); + + const char *ep_name = StartEpisode->mEpisodeName; + if (*ep_name == '$') ep_name = GStrings[ep_name+1]; + FStatistics *sl = GetStatisticsList(EpisodeStatistics, section, ep_name); + + int statvals[4] = {0,0,0,0}; + FString infostring; + int validlevels = LevelData.Size(); + for(unsigned i = 0; i < LevelData.Size(); i++) + { + statvals[0] += LevelData[i].killcount; + statvals[1] += LevelData[i].totalkills; + statvals[2] += LevelData[i].secretcount; + statvals[3] += LevelData[i].totalsecrets; + } + + infostring.Format("%4d/%4d, %3d/%3d, %2d", statvals[0], statvals[1], statvals[2], statvals[3], validlevels); + FSessionStatistics *es = StatisticsEntry(sl, infostring, level.totaltime); + + for(unsigned i = 0; i < LevelData.Size(); i++) + { + FString lsection = LevelData[i].levelname; + lsection.ToUpper(); + infostring.Format("%4d/%4d, %3d/%3d", + LevelData[i].killcount, LevelData[i].totalkills, LevelData[i].secretcount, LevelData[i].totalsecrets); + + LevelStatEntry(es, lsection, infostring, LevelData[i].leveltime); + } + SaveStatistics(statfile, EpisodeStatistics); + } + } + else if (savestatistics == 2) // todo: handle single level statistics. + { + } +} + + +//========================================================================== +// +// saves statistics info to savegames +// +//========================================================================== + +static void SerializeStatistics(FArchive &arc) +{ + FString startlevel; + int i = LevelData.Size(); + + arc << i; + + if (arc.IsLoading()) + { + arc << startlevel; + StartEpisode = NULL; + for(unsigned int j=0;jmEpisodeMap; + arc << startlevel; + } + for(int j = 0; j < i; j++) + { + OneLevel &l = LevelData[j]; + + arc << l.totalkills + << l.killcount + << l.totalsecrets + << l.secretcount + << l.leveltime; + + if (arc.IsStoring()) arc.WriteName(l.levelname); + else strcpy(l.levelname, arc.ReadName()); + } +} + +#define STAT_ID MAKE_ID('s','T','a','t') + +void STAT_Write(FILE *file) +{ + FPNGChunkArchive arc (file, STAT_ID); + SerializeStatistics(arc); +} + +void STAT_Read(PNGHandle *png) +{ + DWORD chunkLen = (DWORD)M_FindPNGChunk (png, STAT_ID); + if (chunkLen != 0) + { + FPNGChunkArchive arc (png->File->GetFile(), STAT_ID, chunkLen); + SerializeStatistics(arc); + } +} + +//========================================================================== +// +// show statistics +// +//========================================================================== + +FString GetStatString() +{ + FString compose; + for(unsigned i = 0; i < LevelData.Size(); i++) + { + OneLevel *l = &LevelData[i]; + compose.AppendFormat("Level %s - Kills: %d/%d - Secrets: %d/%d - Time: %d:%02d\n", + l->levelname, l->killcount, l->totalkills, l->secretcount, l->totalsecrets, + l->leveltime/(60*TICRATE), (l->leveltime/TICRATE)%60); + } + return compose; +} + +CCMD(printstats) +{ + StoreLevelStats(); // Get the current level's current results. + Printf("%s", GetStatString()); +} + + +CCMD(finishgame) +{ + // This CCMD simulates an end-of-game action and exists to end mods that never exit their last level. + G_SetForEndGame (level.nextmap); + G_ExitLevel (0, false); +} + diff --git a/src/strnatcmp.c b/src/strnatcmp.c new file mode 100644 index 000000000..fb8636d87 --- /dev/null +++ b/src/strnatcmp.c @@ -0,0 +1,178 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +/* partial change history: + * + * 2004-10-10 mbp: Lift out character type dependencies into macros. + * + * Eric Sosman pointed out that ctype functions take a parameter whose + * value must be that of an unsigned int, even on platforms that have + * negative chars in their default char type. + */ + +#include +#include +#include +#include + +#include "strnatcmp.h" + + +/* These are defined as macros to make it easier to adapt this code to + * different characters types or comparison functions. */ +static __inline int +nat_isdigit(nat_char a) +{ + return isdigit((unsigned char) a); +} + + +static __inline int +nat_isspace(nat_char a) +{ + return isspace((unsigned char) a); +} + + +static __inline nat_char +nat_toupper(nat_char a) +{ + return toupper((unsigned char) a); +} + + + +static int +compare_right(nat_char const *a, nat_char const *b) +{ + int bias = 0; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return bias; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) { + if (!bias) + bias = -1; + } else if (*a > *b) { + if (!bias) + bias = +1; + } else if (!*a && !*b) + return bias; + } + + return 0; +} + + +static int +compare_left(nat_char const *a, nat_char const *b) +{ + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return 0; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) + return -1; + else if (*a > *b) + return +1; + } + + return 0; +} + + +static int strnatcmp0(nat_char const *a, nat_char const *b, int fold_case) +{ + int ai, bi; + nat_char ca, cb; + int fractional, result; + + assert(a && b); + ai = bi = 0; + while (1) { + ca = a[ai]; cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (nat_isspace(ca)) + ca = a[++ai]; + + while (nat_isspace(cb)) + cb = b[++bi]; + + /* process run of digits */ + if (nat_isdigit(ca) && nat_isdigit(cb)) { + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + if ((result = compare_left(a+ai, b+bi)) != 0) + return result; + } else { + if ((result = compare_right(a+ai, b+bi)) != 0) + return result; + } + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + return 0; + } + + if (fold_case) { + ca = nat_toupper(ca); + cb = nat_toupper(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + + ++ai; ++bi; + } +} + + + +int strnatcmp(nat_char const *a, nat_char const *b) { + return strnatcmp0(a, b, 0); +} + + +/* Compare, recognizing numeric string and ignoring case. */ +int strnatcasecmp(nat_char const *a, nat_char const *b) { + return strnatcmp0(a, b, 1); +} diff --git a/src/strnatcmp.h b/src/strnatcmp.h new file mode 100644 index 000000000..7d7462349 --- /dev/null +++ b/src/strnatcmp.h @@ -0,0 +1,39 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* CUSTOMIZATION SECTION + * + * You can change this typedef, but must then also change the inline + * functions in strnatcmp.c */ +typedef char nat_char; + +int strnatcmp(nat_char const *a, nat_char const *b); +int strnatcasecmp(nat_char const *a, nat_char const *b); + +#ifdef __cplusplus +} +#endif diff --git a/zdoom.vcproj b/zdoom.vcproj index 94a7a92dd..11e243a83 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1000,6 +1000,10 @@ RelativePath=".\src\st_stuff.cpp" > + + @@ -1008,6 +1012,10 @@ RelativePath=".\src\stringtable.cpp" > + + @@ -1533,6 +1541,10 @@ RelativePath=".\src\stringtable.h" > + + From 20513cff41ed2b670603df50f2e2ca01612c7bdc Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 18 Sep 2010 17:45:19 +0000 Subject: [PATCH 037/815] - Fixed Linux compilation issue with statistics.cpp SVN r2823 (trunk) --- src/statistics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statistics.cpp b/src/statistics.cpp index a5c85bcd5..03e9d7405 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -55,7 +55,7 @@ #include "p_local.h" #include "m_png.h" #include "p_setup.h" -#include "s_Sound.h" +#include "s_sound.h" #include "wi_stuff.h" #include "sc_man.h" #include "cmdlib.h" From b50007ecf64135e82ba93e3d6dcf99922940f8bd Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 18 Sep 2010 20:13:56 +0000 Subject: [PATCH 038/815] - Fixed: Compilation errors on Mac OS X. SVN r2824 (trunk) --- src/cmdlib.cpp | 5 ++++- src/p_glnodes.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index 36a900709..96aca06a1 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -1004,7 +1004,8 @@ void ScanDirectory(TArray &list, const char *dirpath) void ScanDirectory(TArray &list, const char *dirpath) { - const char **argv[] = {dirpath, NULL }; + char * const argv[] = {new char[strlen(dirpath)+1], NULL }; + memcpy(argv[0], dirpath, strlen(dirpath)+1); FTS *fts; FTSENT *ent; @@ -1012,6 +1013,7 @@ void ScanDirectory(TArray &list, const char *dirpath) if (fts == NULL) { I_Error("Failed to start directory traversal: %s\n", strerror(errno)); + delete[] argv[0]; return; } while ((ent = fts_read(fts)) != NULL) @@ -1037,5 +1039,6 @@ void ScanDirectory(TArray &list, const char *dirpath) } } fts_close(fts); + delete[] argv[0]; } #endif diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index 8a491f595..c48d72e8f 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -1077,7 +1077,7 @@ static FString GetCachePath() FSRef folder; if (noErr == FSFindFolder(kLocalDomain, kApplicationSupportFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + noErr == FSRefMakePath(&folder, (UInt8*)path.GetChars(), PATH_MAX)) { path = pathstr; } From 7b01f7b296202f38a8df72e10aef5b98c4a435ac Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 18 Sep 2010 22:39:27 +0000 Subject: [PATCH 039/815] - merged Thing_Destroy extension from Doom64 branch into trunk and extended it by a tid=0, tag!=0 case which will kill all shootable actors in sectors with the specified tag. SVN r2825 (trunk) --- src/actionspecials.h | 2 +- src/p_lnspec.cpp | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/actionspecials.h b/src/actionspecials.h index c735c7a9d..0f31243e0 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -123,7 +123,7 @@ DEFINE_SPECIAL(UsePuzzleItem, 129, 2, 5, 5) DEFINE_SPECIAL(Thing_Activate, 130, 1, 1, 1) DEFINE_SPECIAL(Thing_Deactivate, 131, 1, 1, 1) DEFINE_SPECIAL(Thing_Remove, 132, 1, 1, 1) -DEFINE_SPECIAL(Thing_Destroy, 133, 1, 2, 2) +DEFINE_SPECIAL(Thing_Destroy, 133, 1, 3, 3) DEFINE_SPECIAL(Thing_Projectile, 134, 5, 5, 5) DEFINE_SPECIAL(Thing_Spawn, 135, 3, 4, 4) DEFINE_SPECIAL(Thing_ProjectileGravity, 136, 5, 5, 5) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index a53356a8e..b0697904a 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1210,22 +1210,36 @@ FUNC(LS_Thing_Remove) } FUNC(LS_Thing_Destroy) -// Thing_Destroy (tid, extreme) +// Thing_Destroy (tid, extreme, tag) { - if (arg0 == 0) + AActor *actor; + + if (arg0 == 0 && arg2 == 0) { P_Massacre (); } + else if (arg0 == 0) + { + TThinkerIterator iterator; + + actor = iterator.Next (); + while (actor) + { + AActor *temp = iterator.Next (); + if (actor->flags & MF_SHOOTABLE && actor->Sector->tag == arg2) + P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None); + actor = temp; + } + } else { FActorIterator iterator (arg0); - AActor *actor; actor = iterator.Next (); while (actor) { AActor *temp = iterator.Next (); - if (actor->flags & MF_SHOOTABLE) + if (actor->flags & MF_SHOOTABLE && (arg2 == 0 || actor->Sector->tag == arg2)) P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None); actor = temp; } From df138fe4f90725894fceee2b54b27a0f296406fb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 00:06:45 +0000 Subject: [PATCH 040/815] - added a ClearCounters function to AActor that handles everything necessary to un-count an item that is not supposed to be counted but has some of the COUNT* flags set. - merged all places where secrets are credited into one common function. - added the Doom64 COUNTSECRET actor flag. - fixed: AInventory::CreateCopy did not clear the COUNTITEM flag. - fixed: Dropping an item did not increase the item count but the dropped item could still have the COUNTITEM flag. Now this flag gets cleared when the item gets picked up so that dropped items don't count a second time. SVN r2826 (trunk) --- specs/udmf_zdoom.txt | 6 +++- src/actor.h | 7 ++-- src/d_dehacked.cpp | 6 +--- src/doomdata.h | 2 ++ src/g_heretic/a_dsparil.cpp | 2 +- src/g_hexen/a_heresiarch.cpp | 2 +- src/g_shared/a_pickups.cpp | 5 +++ src/g_shared/a_secrettrigger.cpp | 19 ++--------- src/gameconfigfile.cpp | 2 +- src/m_cheat.cpp | 6 +--- src/namedef.h | 1 + src/p_acs.cpp | 16 ++------- src/p_conversation.cpp | 6 +--- src/p_mobj.cpp | 56 ++++++++++++++++++++++++++----- src/p_spec.cpp | 39 +++++++++++++++++---- src/p_spec.h | 10 ++---- src/p_things.cpp | 23 ++----------- src/p_udmf.cpp | 5 +++ src/thingdef/thingdef_codeptr.cpp | 23 +++++++++---- src/thingdef/thingdef_data.cpp | 1 + 20 files changed, 137 insertions(+), 100 deletions(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index fe6897d9f..fef3fa996 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -183,6 +183,7 @@ Note: All fields default to false unless mentioned otherwise. // menu can display. conversation = // Assigns a conversation dialogue to this thing. // Parameter is the conversation ID, 0 meaning none. + countsecret = ; // Picking up this actor counts as a secret. } @@ -269,7 +270,7 @@ Changed node specifications to deprecate compression of node lump. Added 'playeruseback' line trigger flag. 1.11 07.08.2010 -Added 'soundsequnce' sector property. +Added 'soundsequence' sector property. 1.12 22.08.2010 Added 'conversation' thing property. @@ -277,6 +278,9 @@ Added 'conversation' thing property. 1.13 29.08.2010 Added 'hidden' sector property. +1.14 19.09.2010 +Added 'countsecret' actor property. + =============================================================================== EOF =============================================================================== diff --git a/src/actor.h b/src/actor.h index 2a8921b56..7de8a725e 100644 --- a/src/actor.h +++ b/src/actor.h @@ -269,7 +269,7 @@ enum MF5_FASTMELEE = 0x00000002, // has a faster melee attack when DF_FAST_MONSTERS or nightmare is on. MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. /* = 0x00000008, */ - /* = 0x00000010, */ + MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret MF5_AVOIDINGDROPOFF = 0x00000020, // Used to move monsters away from dropoffs MF5_NODAMAGE = 0x00000040, // Actor can be shot and reacts to being shot but takes no damage MF5_CHASEGOAL = 0x00000080, // Walks to goal instead of target if a valid goal is set. @@ -289,7 +289,7 @@ enum MF5_NOINTERACTION = 0x00200000, // Thing is completely excluded from any gameplay related checks MF5_NOTIMEFREEZE = 0x00400000, // Actor is not affected by time freezer MF5_PUFFGETSOWNER = 0x00800000, // [BB] Sets the owner of the puff to the player who fired it - MF5_SPECIALFIREDAMAGE=0x01000000, // Special treatment of PhoenixFX1 turned into a flag to removr + MF5_SPECIALFIREDAMAGE=0x01000000, // Special treatment of PhoenixFX1 turned into a flag to remove // dependence of main engine code of specific actor types. MF5_SUMMONEDMONSTER = 0x02000000, // To mark the friendly Minotaur. Hopefully to be generalized later. MF5_NOVERTICALMELEERANGE=0x04000000,// Does not check vertical distance for melee range @@ -832,7 +832,7 @@ public: BYTE MinMissileChance;// [RH] If a random # is > than this, then missile attack. SBYTE LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0) WORD BounceFlags; // which bouncing type? - WORD SpawnFlags; + DWORD SpawnFlags; // Increased to DWORD because of Doom 64 fixed_t meleerange; // specifies how far a melee attack reaches. fixed_t meleethreshold; // Distance below which a monster doesn't try to shoot missiles anynore // but instead tries to come closer for a melee attack. @@ -937,6 +937,7 @@ public: virtual bool UpdateWaterLevel (fixed_t oldz, bool splash=true); bool isFast(); void SetIdle(); + void ClearCounters(); FState *FindState (FName label) const { diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 97c8296a4..225f24cab 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2895,11 +2895,7 @@ bool ADehackedPickup::TryPickup (AActor *&toucher) if (RealPickup != NULL) { // The internally spawned item should never count towards statistics. - if (RealPickup->flags & MF_COUNTITEM) - { - RealPickup->flags &= ~MF_COUNTITEM; - level.total_items--; - } + RealPickup->ClearCounters(); if (!(flags & MF_DROPPED)) { RealPickup->flags &= ~MF_DROPPED; diff --git a/src/doomdata.h b/src/doomdata.h index 9c22b22a6..83345a671 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -380,6 +380,8 @@ enum EMapThingFlags MTF_STANDSTILL = 0x4000, MTF_STRIFESOMETHING = 0x8000, + MTF_SECRET = 0x080000, // Secret pickup + MTF_NOINFIGHTING = 0x100000, // BOOM and DOOM compatible versions of some of the above BTF_NOTSINGLE = 0x0010, // (TF_COOPERATIVE|TF_DEATHMATCH) diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp index aa0bd46ed..0b25a94de 100644 --- a/src/g_heretic/a_dsparil.cpp +++ b/src/g_heretic/a_dsparil.cpp @@ -258,8 +258,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_GenWizard) mo->z -= mo->GetDefault()->height/2; if (!P_TestMobjLocation (mo)) { // Didn't fit + mo->ClearCounters(); mo->Destroy (); - level.total_monsters--; } else { // [RH] Make the new wizards inherit D'Sparil's target diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index 2107f2722..ecce5d2a8 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -859,8 +859,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_SpawnBishop) { if (!P_TestMobjLocation(mo)) { + mo->ClearCounters(); mo->Destroy (); - level.total_monsters--; } else if (self->target != NULL) { // [RH] Make the new bishops inherit the Heriarch's target diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 30dcd482c..524daee77 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -924,6 +924,11 @@ void AInventory::Touch (AActor *toucher) level.found_items++; } + if (flags5 & MF5_COUNTSECRET) + { + P_GiveSecret(toucher, true, true); + } + //Added by MC: Check if item taken was the roam destination of any bot for (int i = 0; i < MAXPLAYERS; i++) { diff --git a/src/g_shared/a_secrettrigger.cpp b/src/g_shared/a_secrettrigger.cpp index d0996aab8..79f3bcd30 100644 --- a/src/g_shared/a_secrettrigger.cpp +++ b/src/g_shared/a_secrettrigger.cpp @@ -40,6 +40,8 @@ #include "d_player.h" #include "doomstat.h" #include "v_font.h" +#include "r_data.h" +#include "p_spec.h" EXTERN_CVAR(String, secretmessage) @@ -61,22 +63,7 @@ void ASecretTrigger::PostBeginPlay () void ASecretTrigger::Activate (AActor *activator) { - if (activator != NULL) - { - if (activator->CheckLocalView (consoleplayer)) - { - if (args[0] <= 1) - { - C_MidPrint (SmallFont, secretmessage); - } - if (args[0] == 0 || args[0] == 2) - { - S_Sound (activator, CHAN_AUTO, "misc/secret", 1, ATTN_NORM); - } - } - if (activator->player) activator->player->secretcount++; - } - level.found_secrets++; + P_GiveSecret(activator, args[0] <= 1, (args[0] == 0 || args[0] == 2)); Destroy (); } diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index acf2bbad1..64ddaa4b8 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -380,7 +380,7 @@ void FGameConfigFile::DoGameSetup (const char *gamename) ReadCVars (0); } - if (game != Doom && game != Strife && game != Chex) + if (game == Heretic || game == Hexen) { SetRavenDefaults (game == Hexen); } diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 9da1b1ff5..95ddf7725 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -590,11 +590,7 @@ void GiveSpawner (player_t *player, const PClass *type, int amount) item->Amount = MIN (amount, item->MaxAmount); } } - if(item->flags & MF_COUNTITEM) // Given items shouldn't count against the map's total, - { // since they aren't added to the player's total. - level.total_items--; - item->flags &= ~MF_COUNTITEM; - } + item->ClearCounters(); if (!item->CallTryPickup (player->mo)) { item->Destroy (); diff --git a/src/namedef.h b/src/namedef.h index 9f7e5a87d..012d85afa 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -359,6 +359,7 @@ xx(Invisible) xx(Friend) xx(Strifeally) xx(Standing) +xx(Countsecret) xx(Blocking) xx(Blockmonsters) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index f7a52987b..e147648e5 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -434,11 +434,7 @@ static void DoGiveInv (AActor *actor, const PClass *info, int amount) AInventory *item = static_cast(Spawn (info, 0,0,0, NO_REPLACE)); // This shouldn't count for the item statistics! - if (item->flags & MF_COUNTITEM) - { - level.total_items--; - item->flags &= ~MF_COUNTITEM; - } + item->ClearCounters(); if (info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorPickup))) { if (static_cast(item)->SaveAmount != 0) @@ -2286,15 +2282,7 @@ int DLevelScript::DoSpawn (int type, fixed_t x, fixed_t y, fixed_t z, int tid, i { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. - if (actor->CountsAsKill()) - { - level.total_monsters--; - } - // Same, for items - if (actor->flags & MF_COUNTITEM) - { - level.total_items--; - } + actor->ClearCounters(); actor->Destroy (); actor = NULL; } diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index a2c287a42..7283c4abd 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1264,11 +1264,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply { AInventory *item = static_cast(Spawn(reply->GiveType, 0, 0, 0, NO_REPLACE)); // Items given here should not count as items! - if (item->flags & MF_COUNTITEM) - { - level.total_items--; - item->flags &= ~MF_COUNTITEM; - } + item->ClearCounters(); if (item->GetClass()->TypeName == NAME_FlameThrower) { // The flame thrower gives less ammo when given in a dialog diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 4d6cdc765..f12127a75 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -258,9 +258,18 @@ void AActor::Serialize (FArchive &arc) << args[0] << args[1] << args[2] << args[3] << args[4] << goal << waterlevel - << MinMissileChance - << SpawnFlags - << Inventory + << MinMissileChance; + if (SaveVersion >= 2826) + { + arc << SpawnFlags; + } + else + { + WORD w; + arc << w; + SpawnFlags = w; + } + arc << Inventory << InventoryID << id << FloatBobPhase @@ -728,6 +737,7 @@ AInventory *AActor::DropInventory (AInventory *item) drop->vely = vely + 5 * finesine[an]; drop->velz = velz + FRACUNIT; drop->flags &= ~MF_NOGRAVITY; // Don't float + drop->ClearCounters(); // do not count for statistics again return drop; } @@ -2458,7 +2468,7 @@ void P_NightmareRespawn (AActor *mobj) if (!P_TestMobjLocation (mo)) { //[GrafZahl] MF_COUNTKILL still needs to be checked here. - if (mo->CountsAsKill()) level.total_monsters--; + mo->ClearCounters(); mo->Destroy (); return; // no respawn } @@ -3626,6 +3636,11 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t { level.total_items++; } + // And for secrets + if (actor->flags5 & MF5_COUNTSECRET) + { + level.total_secrets++; + } if (screen != NULL) { screen->StateChanged(actor); @@ -3695,6 +3710,11 @@ void AActor::HandleSpawnFlags () { RenderStyle = STYLE_None; } + if (SpawnFlags & MTF_SECRET) + { + //Printf("Secret %s in sector %i!\n", GetTag(), Sector->sectornum); + flags5 |= MF5_COUNTSECRET; + } } void AActor::BeginPlay () @@ -4969,11 +4989,7 @@ bool P_CheckMissileSpawn (AActor* th) if (th->BlockingMobj == NULL || !(th->flags2 & MF2_RIP) || (th->BlockingMobj->flags5 & MF5_DONTRIP)) { // If this is a monster spawned by A_CustomMissile subtract it from the counter. - if (th->CountsAsKill()) - { - th->flags&=~MF_COUNTKILL; - level.total_monsters--; - } + th->ClearCounters(); // [RH] Don't explode missiles that spawn on top of horizon lines if (th->BlockingLine != NULL && th->BlockingLine->special == Line_Horizon) { @@ -5602,6 +5618,28 @@ const char *AActor::GetTag(const char *def) const } +void AActor::ClearCounters() +{ + if (CountsAsKill()) + { + level.total_monsters--; + flags &= ~MF_COUNTKILL; + } + // Same, for items + if (flags & MF_COUNTITEM) + { + level.total_items--; + flags &= ~MF_COUNTITEM; + } + // And finally for secrets + if (flags5 & MF5_COUNTSECRET) + { + level.total_secrets--; + flags5 &= ~MF5_COUNTSECRET; + } +} + + //---------------------------------------------------------------------------- // // DropItem handling diff --git a/src/p_spec.cpp b/src/p_spec.cpp index e37df68b1..2e3f1b6e2 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -256,7 +256,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) // end of changed code if (developer && buttonSuccess) { - Printf ("Line special %d activated\n", special); + Printf ("Line special %d activated on line %i\n", special, line - lines); } return true; } @@ -561,15 +561,33 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) if (sector->special & SECRET_MASK) { - player->secretcount++; - level.found_secrets++; sector->special &= ~SECRET_MASK; - if (player->mo->CheckLocalView (consoleplayer)) + P_GiveSecret(player->mo, true, true); + } +} + + +//============================================================================ +// +// P_GiveSecret +// +//============================================================================ + +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound) +{ + if (actor != NULL) + { + if (actor->player != NULL) { - C_MidPrint (SmallFont, secretmessage); - S_Sound (CHAN_AUTO, "misc/secret", 1, ATTN_NORM); + actor->player->secretcount++; + } + if (actor->CheckLocalView (consoleplayer)) + { + if (printmessage) C_MidPrint (SmallFont, secretmessage); + if (playsound) S_Sound (CHAN_AUTO, "misc/secret", 1, ATTN_NORM); } } + level.found_secrets++; } //============================================================================ @@ -1923,6 +1941,15 @@ DPusher::DPusher (DPusher::EPusher type, line_t *l, int magnitude, int angle, m_Affectee = affectee; } +int DPusher::CheckForSectorMatch (EPusher type, int tag) +{ + if (m_Type == type && sectors[m_Affectee].tag == tag) + return m_Affectee; + else + return -1; +} + + ///////////////////////////// // // T_Pusher looks for all objects that are inside the radius of diff --git a/src/p_spec.h b/src/p_spec.h index ca3694c09..0171cf796 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -122,13 +122,7 @@ public: DPusher (); DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); void Serialize (FArchive &arc); - int CheckForSectorMatch (EPusher type, int tag) - { - if (m_Type == type && sectors[m_Affectee].tag == tag) - return m_Affectee; - else - return -1; - } + int CheckForSectorMatch (EPusher type, int tag); void ChangeValues (int magnitude, int angle) { angle_t ang = ((angle_t)(angle<<24)) >> ANGLETOFINESHIFT; @@ -187,6 +181,8 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType); void P_SetSectorFriction (int tag, int amount, bool alterFlag); +void P_GiveSecret(AActor *actor, bool printmessage, bool playsound); + // // getSide() // Will return a side_t* diff --git a/src/p_things.cpp b/src/p_things.cpp index 6622ac0a7..aa48fe18b 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -105,15 +105,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. - if (mobj->CountsAsKill()) - { - level.total_monsters--; - } - // Same, for items - if (mobj->flags & MF_COUNTITEM) - { - level.total_items--; - } + mobj->ClearCounters(); mobj->Destroy (); } } @@ -352,15 +344,7 @@ nolead: mobj->angle = R_PointToAngle2 (mobj->x, mobj->y, targ->x, targ->y); { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. - if (mobj->CountsAsKill()) - { - level.total_monsters--; - } - // Same, for items - if (mobj->flags & MF_COUNTITEM) - { - level.total_items--; - } + mobj->ClearCounters(); mobj->Destroy (); } else @@ -418,8 +402,7 @@ void P_RemoveThing(AActor * actor) if (actor->player == NULL || actor != actor->player->mo) { // be friendly to the level statistics. ;) - if (actor->CountsAsKill() && actor->health > 0) level.total_monsters--; - if (actor->flags&MF_COUNTITEM) level.total_items--; + actor->ClearCounters(); actor->Destroy (); } } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 5f2fca941..2dd3c770e 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -599,6 +599,11 @@ public: Flag(th->flags, MTF_STANDSTILL, key); break; + case NAME_Countsecret: + CHECK_N(Zd | Zdt | Va) + Flag(th->flags, MTF_SECRET, key); + break; + default: if (!strnicmp("user_", key.GetChars(), 5)) { diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index b304e638f..1ac84d38a 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1402,11 +1402,7 @@ static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO) item->Amount = amount; } item->flags |= MF_DROPPED; - if (item->flags & MF_COUNTITEM) - { - item->flags&=~MF_COUNTITEM; - level.total_items--; - } + item->ClearCounters(); if (!item->CallTryPickup (receiver)) { item->Destroy (); @@ -1541,7 +1537,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) if (!(flags&SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo)) { // The monster is blocked so don't spawn it at all! - if (mo->CountsAsKill()) level.total_monsters--; + mo->ClearCounters(); mo->Destroy(); return false; } @@ -2925,9 +2921,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) { bool kill_before, kill_after; INTBOOL item_before, item_after; + INTBOOL secret_before, secret_after; kill_before = self->CountsAsKill(); item_before = self->flags & MF_COUNTITEM; + secret_before = self->flags5 & MF5_COUNTSECRET; if (fd->structoffset == -1) { @@ -2953,6 +2951,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) } kill_after = self->CountsAsKill(); item_after = self->flags & MF_COUNTITEM; + secret_after = self->flags5 & MF5_COUNTSECRET; // Was this monster previously worth a kill but no longer is? // Or vice versa? if (kill_before != kill_after) @@ -2978,6 +2977,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) level.total_items--; } } + // and secretd + if (secret_before != secret_after) + { + if (secret_after) + { // It counts as an secret now. + level.total_secrets++; + } + else + { // It no longer counts as an secret + level.total_secrets--; + } + } } else { diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index b08e48719..05e5bc3a7 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -185,6 +185,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF5, FASTER, AActor, flags5), DEFINE_FLAG(MF5, FASTMELEE, AActor, flags5), DEFINE_FLAG(MF5, NODROPOFF, AActor, flags5), + DEFINE_FLAG(MF5, COUNTSECRET, AActor, flags5), DEFINE_FLAG(MF5, NODAMAGE, AActor, flags5), DEFINE_FLAG(MF5, BLOODSPLATTER, AActor, flags5), DEFINE_FLAG(MF5, OLDRADIUSDMG, AActor, flags5), From 94ce4d5d696b5b9f5a5ffb74c61d56e3a66fbdb6 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 19 Sep 2010 03:30:31 +0000 Subject: [PATCH 041/815] - Remove extra *usefail definition for the pig player. - Locks can now define more than one LockedSound by separating them with commas. The default setting for this property is now "*keytry", "misc/keytry". The first sound that is defined is the one that will be played for the lock. Thus, for standard locks, if the player class defines *keytry, that will be played. Otherwise, misc/keytry will be played as before. SVN r2827 (trunk) --- src/g_shared/a_keys.cpp | 46 +++++++++++++++++++++++++++++++-------- wadsrc/static/sndinfo.txt | 17 +++++++-------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index c9139f76b..b0f76e4b2 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -45,9 +45,9 @@ struct Keygroup struct Lock { TArray keylist; + TArray locksound; FString Message; FString RemoteMsg; - FSoundID locksound; int rgb; Lock() @@ -230,7 +230,8 @@ static void ParseLock(FScanner &sc) delete locks[keynum]; } locks[keynum] = lock; - locks[keynum]->locksound = "misc/keytry"; + locks[keynum]->locksound.Push("*keytry"); + locks[keynum]->locksound.Push("misc/keytry"); ignorekey=false; } else if (keynum != -1) @@ -273,8 +274,21 @@ static void ParseLock(FScanner &sc) break; case 4: // locksound - sc.MustGetString(); - lock->locksound = sc.String; + lock->locksound.Clear(); + for (;;) + { + sc.MustGetString(); + lock->locksound.Push(sc.String); + if (!sc.GetString()) + { + break; + } + if (!sc.Compare(",")) + { + sc.UnGet(); + break; + } + } break; default: @@ -398,27 +412,32 @@ void P_DeinitKeyMessages() bool P_CheckKeys (AActor *owner, int keynum, bool remote) { const char *failtext = NULL; - FSoundID failsound; + FSoundID *failsound; + int numfailsounds; if (owner == NULL) return false; if (keynum<=0 || keynum>255) return true; // Just a safety precaution. The messages should have been initialized upon game start. if (!keysdone) P_InitKeyMessages(); + FSoundID failage[2] = { "*keytry", "misc/keytry" }; + if (!locks[keynum]) { - if (keynum==103 && gameinfo.gametype == GAME_Strife) + if (keynum == 103 && gameinfo.gametype == GAME_Strife) failtext = "THIS AREA IS ONLY AVAILABLE IN THE RETAIL VERSION OF STRIFE"; else failtext = "That doesn't seem to work"; - failsound = "misc/keytry"; + failsound = failage; + numfailsounds = countof(failage); } else { if (locks[keynum]->check(owner)) return true; failtext = remote? locks[keynum]->RemoteMsg : locks[keynum]->Message; - failsound = locks[keynum]->locksound; + failsound = &locks[keynum]->locksound[0]; + numfailsounds = locks[keynum]->locksound.Size(); } // If we get here, that means the actor isn't holding an appropriate key. @@ -426,7 +445,16 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote) if (owner == players[consoleplayer].camera) { PrintMessage(failtext); - S_Sound (owner, CHAN_VOICE, failsound, 1, ATTN_NORM); + + // Play the first defined key sound. + for (int i = 0; i < numfailsounds; ++i) + { + if (failsound[i] != 0) + { + S_Sound (owner, CHAN_VOICE, failsound[i], 1, ATTN_NORM); + break; + } + } } return false; diff --git a/wadsrc/static/sndinfo.txt b/wadsrc/static/sndinfo.txt index 100485e28..513832a59 100644 --- a/wadsrc/static/sndinfo.txt +++ b/wadsrc/static/sndinfo.txt @@ -890,15 +890,14 @@ $playeralias mage male *usefail PlayerMageFailedUse $playeralias mage male *puzzfail PuzzleFailMage $playersound mage male *jump mgjump -$playeralias pig male *usefail PigActive1 -$PlayerAlias Pig Male *UseFail PigActive1 -$playeralias Pig Male *PuzzFail PigActive2 -$PlayerAlias Pig Male *Grunt PigActive1 -$PlayerAlias Pig Male *Land PigActive2 -$PlayerAlias Pig Male *Jump PigActive1 -$PlayerAlias Pig Male *Poison PigActive2 -$PlayerAlias Pig Male *Falling PigPain -$PlayerAlias Pig Male *Splat PigDeath +$playeralias pig male *usefail PigActive1 +$playeralias pig male *puzzfail PigActive2 +$playeralias pig male *grunt PigActive1 +$playeralias pig male *land PigActive2 +$playeralias pig male *jump PigActive1 +$playeralias pig male *poison PigActive2 +$playeralias pig male *falling PigPain +$playeralias pig male *splat PigDeath $alias world/drip Ambient10 $alias world/watersplash WaterSplash From 503ae5a9eea232329ceb739132da20135bc7def8 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 19 Sep 2010 03:52:18 +0000 Subject: [PATCH 042/815] - Scroll two rows at a time with the mouse wheel in the options menu. SVN r2828 (trunk) --- src/menu/optionmenu.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 5661eec8e..cebaf9c83 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -138,17 +138,16 @@ bool DOptionMenu::Responder (event_t *ev) { if (ev->subtype == EV_GUI_WheelUp) { - if (mDesc->mScrollPos > 0) - { - mDesc->mScrollPos--; - } + int scrollamt = MIN(2, mDesc->mScrollPos); + mDesc->mScrollPos -= scrollamt; return true; } else if (ev->subtype == EV_GUI_WheelDown) { if (CanScrollDown) { - mDesc->mScrollPos++; + mDesc->mScrollPos += 2; + VisBottom += 2; VisBottom++; } return true; From c4c69df6dcf0492c2a4088301a35ab73138b54e3 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 19 Sep 2010 04:04:34 +0000 Subject: [PATCH 043/815] - Fixed: FOptionMenuItemJoyMap::SetSelection() did not convert from menu selection number to joyaxis number. SVN r2829 (trunk) --- src/menu/joystickmenu.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index a29df9596..27bd150f8 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -168,7 +168,8 @@ public: int GetSelection() { - float f = (float)(int)SELECTED_JOYSTICK->GetAxisMap(mAxis); + double f = SELECTED_JOYSTICK->GetAxisMap(mAxis); + // Map from joystick axis to menu selection. for(unsigned i=0;imValues.Size(); i++) { if (fabs(f - mValues->mValues[i].Value) < FLT_EPSILON) @@ -179,9 +180,18 @@ public: return -1; } - void SetSelection(int Selection) + void SetSelection(int selection) { - SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)Selection); + // Map from menu selection to joystick axis. + if ((unsigned)selection >= mValues->mValues.Size()) + { + selection = JOYAXIS_None; + } + else + { + selection = (int)mValues->mValues[selection].Value; + } + SELECTED_JOYSTICK->SetAxisMap(mAxis, (EJoyAxis)selection); } }; From e1de9f0633fffc7cdb326f25bdff83a890c09223 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 06:34:15 +0000 Subject: [PATCH 044/815] - fixed: The newly accelerated mousewheel scrolling code did not check for the end of the list and could scroll one item too far. It also incremented VisBottom by 3 instead of 2. - changed lock failsound lookup so that for each sound it tries to resolve it as a player sound before deciding if it is valid. SVN r2830 (trunk) --- src/g_shared/a_keys.cpp | 8 ++++++-- src/menu/optionmenu.cpp | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index b0f76e4b2..1169acb88 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -451,8 +451,12 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote) { if (failsound[i] != 0) { - S_Sound (owner, CHAN_VOICE, failsound[i], 1, ATTN_NORM); - break; + int snd = S_FindSkinnedSound(owner, failsound[i]); + if (snd != 0) + { + S_Sound (owner, CHAN_VOICE, snd, 1, ATTN_NORM); + break; + } } } } diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index cebaf9c83..ff13d4b24 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -146,9 +146,16 @@ bool DOptionMenu::Responder (event_t *ev) { if (CanScrollDown) { - mDesc->mScrollPos += 2; - VisBottom += 2; - VisBottom++; + if (VisBottom < (int)(mDesc->mItems.Size()-2)) + { + mDesc->mScrollPos += 2; + VisBottom += 2; + } + else + { + mDesc->mScrollPos++; + VisBottom++; + } } return true; } From a3d38be79399b9c978ac7e448554f4f4d35d5c4b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 06:37:38 +0000 Subject: [PATCH 045/815] - fixed: The DUMB specific options should be grayed out when using FMod for playing back modules. SVN r2831 (trunk) --- wadsrc/static/menudef.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 875525ec0..7f42c0e56 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1359,11 +1359,11 @@ 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" + Option "Sample rate", "mod_samplerate", "SampleRates", "mod_dumb" + Option "Interpolation", "mod_interp", "ModInterpolations", "mod_dumb" + Option "Volume ramping", "mod_volramp", "ModVolumeRamps", "mod_dumb" StaticText " " - Option "Chip-o-matic", "mod_autochip", "OnOff" + Option "Chip-o-matic", "mod_autochip", "OnOff", "mod_dumb" // 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. From 9a8a446840b1a028446c757079b812ed077e2a6b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 08:27:20 +0000 Subject: [PATCH 046/815] - added player class definition through the GAMEINFO section in MAPINFO. Also added a NOMENU flag that can be set for a player class. This means that the use of KEYCONF is now deprecated except for its original purpose: To define mod specific key binding options. SVN r2832 (trunk) --- src/actor.h | 1 + src/gi.cpp | 1 + src/gi.h | 1 + src/p_user.cpp | 78 +++++++++++++--------------- src/thingdef/thingdef_data.cpp | 1 + wadsrc/static/mapinfo/chex.txt | 1 + wadsrc/static/mapinfo/doomcommon.txt | 1 + wadsrc/static/mapinfo/heretic.txt | 1 + wadsrc/static/mapinfo/hexen.txt | 1 + wadsrc/static/mapinfo/strife.txt | 1 + 10 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/actor.h b/src/actor.h index 7de8a725e..e089de96a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -323,6 +323,7 @@ enum MF6_BLOCKEDBYSOLIDACTORS = 0x00080000, // Blocked by solid actors, even if not solid itself MF6_ADDITIVEPOISONDAMAGE = 0x00100000, MF6_ADDITIVEPOISONDURATION = 0x00200000, + MF6_NOMENU = 0x00400000, // Player class should not appear in the class selection menu. // --- mobj.renderflags --- diff --git a/src/gi.cpp b/src/gi.cpp index 190d2b031..0a3f51dbb 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -250,6 +250,7 @@ void FMapInfoParser::ParseGameInfo() // Insert valid keys here. GAMEINFOKEY_CSTRING(titlePage, "titlePage", 8) GAMEINFOKEY_STRINGARRAY(creditPages, "creditPage", 8) + GAMEINFOKEY_STRINGARRAY(PlayerClasses, "playerclasses", 0) GAMEINFOKEY_STRING(titleMusic, "titleMusic") GAMEINFOKEY_FLOAT(titleTime, "titleTime") GAMEINFOKEY_FLOAT(advisoryTime, "advisoryTime") diff --git a/src/gi.h b/src/gi.h index ef9777553..df50060a9 100644 --- a/src/gi.h +++ b/src/gi.h @@ -79,6 +79,7 @@ struct gameinfo_t TArray finalePages; TArray infoPages; TArray DefaultWeaponSlots[10]; + TArray PlayerClasses; FString titleMusic; float titleTime; diff --git a/src/p_user.cpp b/src/p_user.cpp index eef4f5025..3fecc380f 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -95,40 +95,43 @@ bool FPlayerClass::CheckSkin (int skin) return false; } +bool ValidatePlayerClass(const PClass *ti, const char *name) +{ + if (!ti) + { + Printf ("Unknown player class '%s'\n", name); + return false; + } + else if (!ti->IsDescendantOf (RUNTIME_CLASS (APlayerPawn))) + { + Printf ("Invalid player class '%s'\n", name); + return false; + } + else if (ti->Meta.GetMetaString (APMETA_DisplayName) == NULL) + { + Printf ("Missing displayname for player class '%s'\n", name); + return false; + } + return true; +} + void SetupPlayerClasses () { FPlayerClass newclass; - newclass.Flags = 0; + for (unsigned i=0; iflags6 & MF6_NOMENU)) + { + newclass.Flags |= PCF_NOMENU; + } + PlayerClasses.Push (newclass); + } } } @@ -146,19 +149,7 @@ CCMD (addplayerclass) { const PClass *ti = PClass::FindClass (argv[1]); - if (!ti) - { - Printf ("Unknown player class '%s'\n", argv[1]); - } - else if (!ti->IsDescendantOf (RUNTIME_CLASS (APlayerPawn))) - { - Printf ("Invalid player class '%s'\n", argv[1]); - } - else if (ti->Meta.GetMetaString (APMETA_DisplayName) == NULL) - { - Printf ("Missing displayname for player class '%s'\n", argv[1]); - } - else + if (ValidatePlayerClass(ti, argv[1])) { FPlayerClass newclass; @@ -189,7 +180,8 @@ CCMD (playerclasses) { for (unsigned int i = 0; i < PlayerClasses.Size (); i++) { - Printf ("% 3d %s\n", i, + Printf ("%3d: Class = %s, Name = %s\n", i, + PlayerClasses[i].Type->TypeName.GetChars(), PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName)); } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 05e5bc3a7..54de97aa8 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -228,6 +228,7 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF6, ADDITIVEPOISONDAMAGE, AActor, flags6), DEFINE_FLAG(MF6, ADDITIVEPOISONDURATION, AActor, flags6), DEFINE_FLAG(MF6, BLOCKEDBYSOLIDACTORS, AActor, flags6), + DEFINE_FLAG(MF6, NOMENU, AActor, flags6), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 469277681..0dac3e7c9 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -52,6 +52,7 @@ gameinfo menufontcolor_highlight = "BLUE" menufontcolor_selection = "GOLD" menubackbutton = "M_BACK_H" + playerclasses = "ChexPlayer" } skill baby diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 9fd4b9637..2a103d6a9 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -52,6 +52,7 @@ gameinfo menufontcolor_highlight = "YELLOW" menufontcolor_selection = "BRICK" menubackbutton = "M_BACK_D" + playerclasses = "DoomPlayer" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index b9f88e2b9..e6876ab2c 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -51,6 +51,7 @@ gameinfo menufontcolor_highlight = "YELLOW" menufontcolor_selection = "DARKGREEN" menubackbutton = "M_BACK_H" + playerclasses = "HereticPlayer" } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 8f0962a50..46682b56f 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -49,6 +49,7 @@ gameinfo menufontcolor_highlight = "YELLOW" menufontcolor_selection = "BRICK" menubackbutton = "M_BACK_X" + PlayerClasses = "FighterPlayer", "ClericPlayer", "MagePlayer" } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index ffb70e696..edfe798e6 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -52,6 +52,7 @@ gameinfo menufontcolor_highlight = "GREEN" menufontcolor_selection = "GOLD" menubackbutton = "M_BACK_S" + PlayerClasses = "StrifePlayer" } skill baby From 7dcf9b4738ce050ede19ea198b6eae537701644a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 09:02:44 +0000 Subject: [PATCH 047/815] - fixed: The current level's statistics should not be updated when not actually playing. - added a STAT display for the game statistics info. SVN r2833 (trunk) --- src/statistics.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/statistics.cpp b/src/statistics.cpp index 03e9d7405..f12a458dc 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -400,6 +400,8 @@ static void StoreLevelStats() { unsigned int i; + if (gamestate != GS_LEVEL) return; + if (!(level.flags2&LEVEL2_NOSTATISTICS)) // don't consider maps that were excluded from statistics { for(i=0;i Date: Sun, 19 Sep 2010 10:39:34 +0000 Subject: [PATCH 048/815] - moved some info into the GAMEINFO section: * the sprite used for 'pause'. * the factor with which a monster's health is multiplied to decide if it's supposed to be gibbed, * the decision to make monsters run faster in nightmare mode. - moved the hard coded lock messages for lock types 102 and 103 into the language lump. - fixed: Raven's fast monsters could become slower in Nightmare if they had very short walking states. SVN r2834 (trunk) --- src/actor.h | 1 + src/d_main.cpp | 2 +- src/g_shared/a_keys.cpp | 6 +++--- src/gi.cpp | 3 +++ src/gi.h | 3 +++ src/p_enemy.cpp | 15 +++++++++------ src/p_interaction.cpp | 16 +++------------- src/p_mobj.cpp | 10 ++++++---- wadsrc/static/language.enu | 2 ++ wadsrc/static/mapinfo/chex.txt | 2 ++ wadsrc/static/mapinfo/doomcommon.txt | 2 ++ wadsrc/static/mapinfo/heretic.txt | 3 +++ wadsrc/static/mapinfo/hexen.txt | 3 +++ wadsrc/static/mapinfo/strife.txt | 2 ++ 14 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/actor.h b/src/actor.h index e089de96a..79358148f 100644 --- a/src/actor.h +++ b/src/actor.h @@ -704,6 +704,7 @@ public: // Return starting health adjusted by skill level int SpawnHealth(); + int GibHealth(); // Check for monsters that count as kill but excludes all friendlies. bool CountsAsKill() const diff --git a/src/d_main.cpp b/src/d_main.cpp index cbed77f80..d24b8c57d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -785,7 +785,7 @@ void D_Display () FTexture *tex; int x; - tex = TexMan[gameinfo.gametype & (GAME_DoomStrifeChex) ? "M_PAUSE" : "PAUSED"]; + tex = TexMan[gameinfo.PauseSign]; x = (SCREENWIDTH - tex->GetScaledWidth() * CleanXfac)/2 + tex->GetScaledLeftOffset() * CleanXfac; screen->DrawTexture (tex, x, 4, DTA_CleanNoMove, true, TAG_DONE); diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index 1169acb88..aaddb9084 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -424,10 +424,10 @@ bool P_CheckKeys (AActor *owner, int keynum, bool remote) if (!locks[keynum]) { - if (keynum == 103 && gameinfo.gametype == GAME_Strife) - failtext = "THIS AREA IS ONLY AVAILABLE IN THE RETAIL VERSION OF STRIFE"; + if (keynum == 103 && (gameinfo.flags & GI_SHAREWARE)) + failtext = "$TXT_RETAIL_ONLY"; else - failtext = "That doesn't seem to work"; + failtext = "$TXT_DOES_NOT_WORK"; failsound = failage; numfailsounds = countof(failage); diff --git a/src/gi.cpp b/src/gi.cpp index 0a3f51dbb..4cd1284c9 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -260,9 +260,11 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_CSTRING(finaleFlat, "finaleFlat", 8) GAMEINFOKEY_STRINGARRAY(finalePages, "finalePage", 8) GAMEINFOKEY_STRINGARRAY(infoPages, "infoPage", 8) + GAMEINFOKEY_CSTRING(PauseSign, "pausesign", 8) GAMEINFOKEY_STRING(quitSound, "quitSound") GAMEINFOKEY_CSTRING(borderFlat, "borderFlat", 8) GAMEINFOKEY_FIXED(telefogheight, "telefogheight") + GAMEINFOKEY_FIXED(gibfactor, "gibfactor") GAMEINFOKEY_INT(defKickback, "defKickback") GAMEINFOKEY_CSTRING(SkyFlatName, "SkyFlatName", 8) GAMEINFOKEY_STRING(translator, "translator") @@ -275,6 +277,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_BOOL(noloopfinalemusic, "noloopfinalemusic") GAMEINFOKEY_BOOL(drawreadthis, "drawreadthis") GAMEINFOKEY_BOOL(intermissioncounter, "intermissioncounter") + GAMEINFOKEY_BOOL(nightmarefast, "nightmarefast") GAMEINFOKEY_COLOR(dimcolor, "dimcolor") GAMEINFOKEY_FLOAT(dimamount, "dimamount") GAMEINFOKEY_INT(definventorymaxamount, "definventorymaxamount") diff --git a/src/gi.h b/src/gi.h index df50060a9..483b4a1b0 100644 --- a/src/gi.h +++ b/src/gi.h @@ -75,6 +75,7 @@ struct gameinfo_t bool drawreadthis; bool noloopfinalemusic; bool intermissioncounter; + bool nightmarefast; TArray creditPages; TArray finalePages; TArray infoPages; @@ -92,6 +93,7 @@ struct gameinfo_t char SkyFlatName[9]; char ArmorIcon1[9]; char ArmorIcon2[9]; + char PauseSign[9]; char Endoom[9]; fixed_t Armor2Percent; FString quitSound; @@ -120,6 +122,7 @@ struct gameinfo_t FName mFontColorHighlight; FName mFontColorSelection; char mBackButton[9]; + fixed_t gibfactor; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 1a3de085a..378aaae9d 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2166,10 +2166,13 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi if (nightmarefast && G_SkillProperty(SKILLP_FastMonsters)) { // Monsters move faster in nightmare mode - actor->tics -= actor->tics / 2; - if (actor->tics < 3) + if (actor->tics > 3) { - actor->tics = 3; + actor->tics -= actor->tics / 2; + if (actor->tics < 3) + { + actor->tics = 3; + } } } @@ -2648,7 +2651,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) } else // this is the old default A_Chase { - A_DoChase (self, false, self->MeleeState, self->MissileState, true, !!(gameinfo.gametype & GAME_Raven), false); + A_DoChase (self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false); } } @@ -2660,7 +2663,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FastChase) DEFINE_ACTION_FUNCTION(AActor, A_VileChase) { if (!P_CheckForResurrection(self, true)) - A_DoChase (self, false, self->MeleeState, self->MissileState, true, !!(gameinfo.gametype & GAME_Raven), false); + A_DoChase (self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false); } DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ExtChase) @@ -2680,7 +2683,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ExtChase) // for internal use void A_Chase(AActor *self) { - A_DoChase (self, false, self->MeleeState, self->MissileState, true, !!(gameinfo.gametype & GAME_Raven), false); + A_DoChase (self, false, self->MeleeState, self->MissileState, true, gameinfo.nightmarefast, false); } //============================================================================= diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index d4db86436..93bb0c441 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -318,20 +318,10 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker) // EXTERN_CVAR (Int, fraglimit) -static int GibHealth(AActor *actor) -{ - return -abs( - actor->GetClass()->Meta.GetMetaInt ( - AMETA_GibHealth, - gameinfo.gametype & GAME_DoomChex ? - -actor->SpawnHealth() : - -actor->SpawnHealth()/2)); -} - void AActor::Die (AActor *source, AActor *inflictor) { // Handle possible unmorph on death - bool wasgibbed = (health < GibHealth(this)); + bool wasgibbed = (health < GibHealth()); AActor *realthis = NULL; int realstyle = 0; @@ -342,7 +332,7 @@ void AActor::Die (AActor *source, AActor *inflictor) { if (wasgibbed) { - int realgibhealth = GibHealth(realthis); + int realgibhealth = realthis->GibHealth(); if (realthis->health >= realgibhealth) { realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed) @@ -670,7 +660,7 @@ void AActor::Die (AActor *source, AActor *inflictor) { int flags4 = inflictor == NULL ? 0 : inflictor->flags4; - int gibhealth = GibHealth(this); + int gibhealth = GibHealth(); // Don't pass on a damage type this actor cannot handle. // (most importantly, prevent barrels from passing on ice damage.) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f12127a75..d1f0345d8 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5509,6 +5509,11 @@ int AActor::TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FN return (death == NULL) ? -1 : damage; } +int AActor::GibHealth() +{ + return -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth, FixedMul(SpawnHealth(), gameinfo.gibfactor))); +} + void AActor::Crash() { if (((flags & MF_CORPSE) || (flags6 & MF6_KILLED)) && @@ -5523,10 +5528,7 @@ void AActor::Crash() } if (crashstate == NULL) { - int gibhealth = -abs(GetClass()->Meta.GetMetaInt (AMETA_GibHealth, - gameinfo.gametype & GAME_DoomChex ? -SpawnHealth() : -SpawnHealth()/2)); - - if (health < gibhealth) + if (health < GibHealth()) { // Extreme death crashstate = FindState (NAME_Crash, NAME_Extreme); } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 5f0eff88e..dd097672f 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1328,6 +1328,8 @@ TXT_NEED_SILVERKEY = "You need the Silver Key"; TXT_NEED_BRASSKEY = "You need the Brass Key"; TXT_NEED_REDCRYSTAL = "You need the Red Crystal"; TXT_NEED_BLUECRYSTAL = "You need the Blue Crystal"; +TXT_RETAIL_ONLY = "THIS AREA IS ONLY AVAILABLE IN THE RETAIL VERSION OF STRIFE"; +TXT_DOES_NOT_WORK = "That doesn't seem to work"; // Strife Quest messages diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 0dac3e7c9..2f2d77bf7 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -53,6 +53,8 @@ gameinfo menufontcolor_selection = "GOLD" menubackbutton = "M_BACK_H" playerclasses = "ChexPlayer" + pausesign = "M_PAUSE" + gibfactor = 1 } skill baby diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 2a103d6a9..a4d12b7c7 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -53,6 +53,8 @@ gameinfo menufontcolor_selection = "BRICK" menubackbutton = "M_BACK_D" playerclasses = "DoomPlayer" + pausesign = "M_PAUSE" + gibfactor = 1 } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index e6876ab2c..1596f45d8 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -52,6 +52,9 @@ gameinfo menufontcolor_selection = "DARKGREEN" menubackbutton = "M_BACK_H" playerclasses = "HereticPlayer" + nightmarefast = true + pausesign = "PAUSED" + gibfactor = 0.5 } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 46682b56f..af6528699 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -50,6 +50,9 @@ gameinfo menufontcolor_selection = "BRICK" menubackbutton = "M_BACK_X" PlayerClasses = "FighterPlayer", "ClericPlayer", "MagePlayer" + nightmarefast = true + pausesign = "PAUSED" + gibfactor = 0.5 } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index edfe798e6..8595c56c9 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -53,6 +53,8 @@ gameinfo menufontcolor_selection = "GOLD" menubackbutton = "M_BACK_S" PlayerClasses = "StrifePlayer" + pausesign = "PAUSED" + gibfactor = 0.5 } skill baby From ebcf08686a624fdd8ef73dcfd515824e09933fd1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 16:49:43 +0000 Subject: [PATCH 049/815] - Fixed typo in plasma gun's nametag. SVN r2835 (trunk) --- wadsrc/static/language.enu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index dd097672f..da6de9524 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -799,7 +799,7 @@ TAG_SHOTGUN = "Shotgun"; TAG_SUPERSHOTGUN = "Super Shotgun"; TAG_CHAINGUN = "Chaingun"; TAG_ROCKETLAUNCHER = "Rocket Launcher"; -TAG_PLASMARIFLE = "Plama Rifle"; +TAG_PLASMARIFLE = "Plasma Rifle"; TAG_BFG9000 = "BFG 9000"; // Item tags: Heretic weapons From a2673c2cc4af635b37cd693539e2a663085d40a0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 22:23:57 +0000 Subject: [PATCH 050/815] - fixed a misplaced ')' in addkeysection. SVN r2836 (trunk) --- src/keysections.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keysections.cpp b/src/keysections.cpp index 3da17fb38..94c551002 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -105,7 +105,7 @@ CCMD (addkeysection) for (unsigned i = 0; i < KeySections.Size(); i++) { - if (KeySections[i].mTitle.CompareNoCase(argv[2] == 0)) + if (KeySections[i].mTitle.CompareNoCase(argv[2]) == 0) { CurrentKeySection = i; return; From 59b19d36fb8589ae86873f4851b27f8b4b146987 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 19 Sep 2010 22:33:21 +0000 Subject: [PATCH 051/815] - Fixed: AActor::Slam mzst check the charging monster's health before setting it back to its See state. Damaging the target may cause action that might have killed it. SVN r2837 (trunk) --- src/p_mobj.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d1f0345d8..0257fc23c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2626,8 +2626,12 @@ bool AActor::Slam (AActor *thing) int dam = GetMissileDamage (7, 1); P_DamageMobj (thing, this, this, dam, NAME_Melee); P_TraceBleed (dam, thing, this); - if (SeeState != NULL) SetState (SeeState); - else SetIdle(); + // The charging monster may have died by the target's actions here. + if (health > 0) + { + if (SeeState != NULL) SetState (SeeState); + else SetIdle(); + } } else { From 0619a2677e44f04d8639774324bf58a99595e11f Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 20 Sep 2010 23:00:27 +0000 Subject: [PATCH 052/815] - Fixed: SBarInfo couldn't print extended ASCII characters. SVN r2838 (trunk) --- src/g_shared/sbarinfo.cpp | 12 ++++++------ src/g_shared/sbarinfo_commands.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index eed9d86c7..8b97c7cf3 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1352,16 +1352,16 @@ public: if(script->spacingCharacter == '\0') ax += font->GetSpaceWidth(); else - ax += font->GetCharWidth((int) script->spacingCharacter); + ax += font->GetCharWidth((unsigned char) script->spacingCharacter); str++; continue; } int width; if(script->spacingCharacter == '\0') //No monospace? - width = font->GetCharWidth((int) *str); + width = font->GetCharWidth((unsigned char) *str); else - width = font->GetCharWidth((int) script->spacingCharacter); - FTexture* character = font->GetChar((int) *str, &width); + width = font->GetCharWidth((unsigned char) script->spacingCharacter); + FTexture* character = font->GetChar((unsigned char) *str, &width); if(character == NULL) //missing character. { str++; @@ -1378,7 +1378,7 @@ public: if(script->spacingCharacter != '\0') { - double spacingSize = font->GetCharWidth((int) script->spacingCharacter); + double spacingSize = font->GetCharWidth((unsigned char) script->spacingCharacter); switch(script->spacingAlignment) { default: @@ -1444,7 +1444,7 @@ public: if(script->spacingCharacter == '\0') ax += width + spacing - (character->LeftOffset+1); else //width gets changed at the call to GetChar() - ax += font->GetCharWidth((int) script->spacingCharacter) + spacing; + ax += font->GetCharWidth((unsigned char) script->spacingCharacter) + spacing; str++; } } diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index e66d23f79..3536bfa5e 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -745,13 +745,13 @@ class CommandDrawString : public SBarInfoCommand if(script->spacingCharacter == '\0') x -= static_cast (font->StringWidth(str)+(spacing * str.Len())); else //monospaced, so just multiplay the character size - x -= static_cast ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len()); + x -= static_cast ((font->GetCharWidth((unsigned char) script->spacingCharacter) + spacing) * str.Len()); break; case ALIGN_CENTER: if(script->spacingCharacter == '\0') x -= static_cast (font->StringWidth(str)+(spacing * str.Len()) / 2); else - x -= static_cast ((font->GetCharWidth((int) script->spacingCharacter) + spacing) * str.Len() / 2); + x -= static_cast ((font->GetCharWidth((unsigned char) script->spacingCharacter) + spacing) * str.Len() / 2); break; } } From 583cbd49a6fdc27718bea8a92929dfea7f0a06da Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 21 Sep 2010 09:43:54 +0000 Subject: [PATCH 053/815] - try to encapsulate access to the player class's display name for printing purposes. SVN r2839 (trunk) --- src/d_player.h | 1 + src/g_shared/sbarinfo_commands.cpp | 2 +- src/menu/menu.cpp | 2 +- src/menu/menudef.cpp | 8 ++++---- src/menu/playermenu.cpp | 7 +++---- src/p_user.cpp | 14 ++++++++++++++ 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 2d58e00ee..6f414e1ff 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -74,6 +74,7 @@ enum FPlayerColorSet *P_GetPlayerColorSet(FName classname, int setnum); void P_EnumPlayerColorSets(FName classname, TArray *out); +const char *GetPrintableDisplayName(const PClass *cls); class player_t; diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 3536bfa5e..792115b5e 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -684,7 +684,7 @@ class CommandDrawString : public SBarInfoCommand if(statusBar->CPlayer->userinfo.PlayerClass != cache) { cache = statusBar->CPlayer->userinfo.PlayerClass; - str = statusBar->CPlayer->cls->Meta.GetMetaString(APMETA_DisplayName); + str = GetPrintableDisplayName(statusBar->CPlayer->cls); RealignString(); } break; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 8141e870e..2732b7cfb 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -351,7 +351,7 @@ void M_SetMenu(FName menu, int param) GameStartupInfo.Episode = -1; GameStartupInfo.PlayerClass = param == -1000? NULL : - param == -1? "Random" : PlayerClasses[param].Type->Meta.GetMetaString (APMETA_DisplayName); + param == -1? "Random" : GetPrintableDisplayName(PlayerClasses[param].Type); break; case NAME_Skillmenu: diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 25fce4e7b..6028d8fd7 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -995,7 +995,7 @@ static void BuildPlayerclassMenu() { if (!(PlayerClasses[i].Flags & PCF_NOMENU)) { - const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); if (pname != NULL) { numclassitems++; @@ -1032,7 +1032,7 @@ static void BuildPlayerclassMenu() { if (!(PlayerClasses[i].Flags & PCF_NOMENU)) { - const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); if (pname != NULL) { FListMenuItemText *it = new FListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, @@ -1051,7 +1051,7 @@ static void BuildPlayerclassMenu() } if (n == 0) { - const char *pname = PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName); + const char *pname = GetPrintableDisplayName(PlayerClasses[0].Type); if (pname != NULL) { FListMenuItemText *it = new FListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, @@ -1086,7 +1086,7 @@ static void BuildPlayerclassMenu() { if (!(PlayerClasses[i].Flags & PCF_NOMENU)) { - const char *pname = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); if (pname != NULL) { FOptionMenuItemSubmenu *it = new FOptionMenuItemSubmenu(pname, "Episodemenu", i); diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 6965d391b..8a5d08507 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -592,7 +592,7 @@ void DPlayerMenu::Init(DMenu *parent, FListMenuDescriptor *desc) { if (PlayerClasses.Size() == 1) { - li->SetString(0, PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName)); + li->SetString(0, GetPrintableDisplayName(PlayerClasses[0].Type)); li->SetValue(0, 0); } else @@ -600,7 +600,7 @@ void DPlayerMenu::Init(DMenu *parent, FListMenuDescriptor *desc) li->SetString(0, "Random"); for(unsigned i=0; i< PlayerClasses.Size(); i++) { - const char *cls = PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName); + const char *cls = GetPrintableDisplayName(PlayerClasses[i].Type); li->SetString(i+1, cls); } li->SetValue(0, players[consoleplayer].userinfo.PlayerClass + 1); @@ -901,8 +901,7 @@ void DPlayerMenu::ClassChanged (FListMenuItem *li) players[consoleplayer].userinfo.PlayerClass = sel-1; PickPlayerClass(); - cvar_set ("playerclass", - sel == 0 ? "Random" : PlayerClass->Type->Meta.GetMetaString (APMETA_DisplayName)); + cvar_set ("playerclass", sel == 0 ? "Random" : PlayerClass->Type->TypeName); UpdateSkins(); UpdateColorsets(); diff --git a/src/p_user.cpp b/src/p_user.cpp index 3fecc380f..37cb230b5 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -52,6 +52,7 @@ #include "thingdef/thingdef.h" #include "g_level.h" #include "d_net.h" +#include "gstrings.h" static FRandom pr_skullpop ("SkullPop"); @@ -95,6 +96,19 @@ bool FPlayerClass::CheckSkin (int skin) return false; } +//=========================================================================== +// +// GetDisplayName +// +//=========================================================================== + +const char *GetPrintableDisplayName(const PClass *cls) +{ + // Fixme; This needs a decent way to access the string table without creating a mess. + const char *name = cls->Meta.GetMetaString(APMETA_DisplayName); + return name; +} + bool ValidatePlayerClass(const PClass *ti, const char *name) { if (!ti) From a3e98eb4ab5fe03a0fde647784219b5c1389f49f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 21 Sep 2010 12:58:59 +0000 Subject: [PATCH 054/815] - added automatic centering of Hexen's skill menu so that it can adjust automatically to the different player classes. SVN r2840 (trunk) --- src/menu/listmenu.cpp | 52 +++++++++++++++++++++++++++++++++++++-- src/menu/menu.h | 6 +++++ src/menu/menudef.cpp | 15 ++++------- wadsrc/static/menudef.txt | 3 ++- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 42164c16b..882f97489 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -53,8 +53,8 @@ IMPLEMENT_CLASS(DListMenu) DListMenu::DListMenu(DMenu *parent, FListMenuDescriptor *desc) : DMenu(parent) { - mDesc = desc; - mFocusControl = NULL; + mDesc = NULL; + if (desc != NULL) Init(parent, desc); } //============================================================================= @@ -68,6 +68,31 @@ void DListMenu::Init(DMenu *parent, FListMenuDescriptor *desc) mParentMenu = parent; GC::WriteBarrier(this, parent); mDesc = desc; + if (desc->mCenter) + { + int center = 160; + for(unsigned i=0;imItems.Size(); i++) + { + int xpos = mDesc->mItems[i]->GetX(); + int width = mDesc->mItems[i]->GetWidth(); + int curx = mDesc->mSelectOfsX; + + if (width > 0 && mDesc->mItems[i]->Selectable()) + { + int left = 160 - (width - curx) / 2 - curx; + if (left < center) center = left; + } + } + for(unsigned i=0;imItems.Size(); i++) + { + int width = mDesc->mItems[i]->GetWidth(); + + if (width > 0) + { + mDesc->mItems[i]->SetX(center); + } + } + } } //============================================================================= @@ -334,6 +359,11 @@ bool FListMenuItem::CheckHotkey(int c) return false; } +int FListMenuItem::GetWidth() +{ + return 0; +} + //============================================================================= // @@ -492,6 +522,18 @@ void FListMenuItemText::Drawer(bool selected) } } +int FListMenuItemText::GetWidth() +{ + const char *text = mText; + if (text != NULL) + { + if (*text == '$') text = GStrings(text+1); + return mFont->StringWidth(text); + } + return 1; +} + + //============================================================================= // // patch item @@ -509,3 +551,9 @@ void FListMenuItemPatch::Drawer(bool selected) { screen->DrawTexture (TexMan(mTexture), mXpos, mYpos, DTA_Clean, true, TAG_DONE); } + +int FListMenuItemPatch::GetWidth() +{ + return TexMan[mTexture]->GetScaledWidth(); +} + diff --git a/src/menu/menu.h b/src/menu/menu.h index 07acd7849..692542198 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -110,6 +110,7 @@ struct FListMenuDescriptor : public FMenuDescriptor EColorRange mFontColor2; const PClass *mClass; FMenuDescriptor *mRedirect; // used to redirect overlong skill and episode menus to option menu based alternatives + bool mCenter; }; struct FOptionMenuSettings @@ -262,9 +263,12 @@ public: virtual bool MenuEvent (int mkey, bool fromcontroller); virtual bool MouseEvent(int type, int x, int y); virtual bool CheckHotkey(int c); + virtual int GetWidth(); void DrawSelector(int xofs, int yofs, FTextureID tex); void OffsetPositionY(int ydelta) { mYpos += ydelta; } int GetY() { return mYpos; } + int GetX() { return mXpos; } + void SetX(int x) { mXpos = x; } }; class FListMenuItemStaticPatch : public FListMenuItem @@ -370,6 +374,7 @@ 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); + int GetWidth(); }; class FListMenuItemPatch : public FListMenuItemSelectable @@ -378,6 +383,7 @@ class FListMenuItemPatch : public FListMenuItemSelectable public: FListMenuItemPatch(int x, int y, int height, int hotkey, FTextureID patch, FName child, int param = 0); void Drawer(bool selected); + int GetWidth(); }; //============================================================================= diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 6028d8fd7..66145bff6 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -238,6 +238,10 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) sc.MustGetNumber(); desc->mYpos = sc.Number; } + else if (sc.Compare("Centermenu")) + { + desc->mCenter = true; + } else if (sc.Compare("MouseWindow")) { sc.MustGetNumber(); @@ -463,6 +467,7 @@ static void ParseListMenu(FScanner &sc) desc->mRedirect = NULL; desc->mWLeft = 0; desc->mWRight = 0; + desc->mCenter = false; FMenuDescriptor **pOld = MenuDescriptors.CheckKey(desc->mMenuName); if (pOld != NULL && *pOld != NULL) delete *pOld; @@ -1228,16 +1233,6 @@ void M_StartupSkillMenu(FGameStartup *gs) 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++) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 7f42c0e56..edbeecb6a 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -204,7 +204,8 @@ ListMenu "SkillMenu" IfGame (Hexen) { StaticText 74, 16, "$MNU_CHOOSESKILL" - Position 38, 44 + Position 160, 44 + centermenu } } From 385cfc56985dc1db1c4eb8f44444d08ee3998352 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 21 Sep 2010 16:50:09 +0000 Subject: [PATCH 055/815] - fixed some texture priorititing problems with Hexen's player displays for the menu. The patches for these should not be placed in the TEX_WallPatch namespace because it's not what modders might expect from them. SVN r2841 (trunk) --- src/textures/multipatchtexture.cpp | 20 ++++++++---- wadsrc/static/textures.txt | 49 +++++++++++++++--------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 4debfa24a..0c0a54499 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, bool silent); + void ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype); }; //========================================================================== @@ -970,12 +970,12 @@ void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) // //========================================================================== -void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent) +void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent, int usetype) { FString patchname; sc.MustGetString(); - FTextureID texno = TexMan.CheckForTexture(sc.String, TEX_WallPatch); + FTextureID texno = TexMan.CheckForTexture(sc.String, usetype); int Mirror = 0; if (!texno.isValid()) @@ -996,10 +996,10 @@ void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, bool silent) } else if (strlen(sc.String) <= 8 && !strpbrk(sc.String, "./")) { - int lumpnum = Wads.CheckNumForName(sc.String, ns_patches); + int lumpnum = Wads.CheckNumForName(sc.String, usetype == TEX_MiscPatch? ns_graphics : ns_patches); if (lumpnum >= 0) { - part.Texture = FTexture::CreateTexture(lumpnum, TEX_WallPatch); + part.Texture = FTexture::CreateTexture(lumpnum, usetype); TexMan.AddTexture(part.Texture); } } @@ -1252,7 +1252,15 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) else if (sc.Compare("Patch")) { TexPart part; - ParsePatch(sc, part, bSilent); + ParsePatch(sc, part, bSilent, TEX_WallPatch); + if (part.Texture != NULL) parts.Push(part); + part.Texture = NULL; + part.Translation = NULL; + } + else if (sc.Compare("Graphic")) + { + TexPart part; + ParsePatch(sc, part, bSilent, TEX_MiscPatch); if (part.Texture != NULL) parts.Push(part); part.Texture = NULL; part.Translation = NULL; diff --git a/wadsrc/static/textures.txt b/wadsrc/static/textures.txt index 19f27e84e..7ae26ce2f 100644 --- a/wadsrc/static/textures.txt +++ b/wadsrc/static/textures.txt @@ -1,72 +1,73 @@ +// Note: These textures use 'graphic' instead of patch because the patches must not be placed in the TEX_WallPatch namespace. Graphic optional P_FWALK1, 112, 136 { - Patch "M_FBOX", 0, 0 - Patch "M_FWALK1", 24, 12 { useoffsets } + Graphic "M_FBOX", 0, 0 + Graphic "M_FWALK1", 24, 12 { useoffsets } } Graphic optional P_FWALK2, 112, 136 { - Patch "M_FBOX", 0, 0 - Patch "M_FWALK2", 24, 12 { useoffsets } + Graphic "M_FBOX", 0, 0 + Graphic "M_FWALK2", 24, 12 { useoffsets } } Graphic optional P_FWALK3, 112, 136 { - Patch "M_FBOX", 0, 0 - Patch "M_FWALK3", 24, 12 { useoffsets } + Graphic "M_FBOX", 0, 0 + Graphic "M_FWALK3", 24, 12 { useoffsets } } Graphic optional P_FWALK4, 112, 136 { - Patch "M_FBOX", 0, 0 - Patch "M_FWALK4", 24, 12 { useoffsets } + Graphic "M_FBOX", 0, 0 + Graphic "M_FWALK4", 24, 12 { useoffsets } } Graphic optional P_CWALK1, 112, 136 { - Patch "M_CBOX", 0, 0 - Patch "M_CWALK1", 24, 12 { useoffsets } + Graphic "M_CBOX", 0, 0 + Graphic "M_CWALK1", 24, 12 { useoffsets } } Graphic optional P_CWALK2, 112, 136 { - Patch "M_CBOX", 0, 0 - Patch "M_CWALK2", 24, 12 { useoffsets } + Graphic "M_CBOX", 0, 0 + Graphic "M_CWALK2", 24, 12 { useoffsets } } Graphic optional P_CWALK3, 112, 136 { - Patch "M_CBOX", 0, 0 - Patch "M_CWALK3", 24, 12 { useoffsets } + Graphic "M_CBOX", 0, 0 + Graphic "M_CWALK3", 24, 12 { useoffsets } } Graphic optional P_CWALK4, 112, 136 { - Patch "M_CBOX", 0, 0 - Patch "M_CWALK4", 24, 12 { useoffsets } + Graphic "M_CBOX", 0, 0 + Graphic "M_CWALK4", 24, 12 { useoffsets } } Graphic optional P_MWALK1, 112, 136 { - Patch "M_MBOX", 0, 0 - Patch "M_MWALK1", 24, 12 { useoffsets } + Graphic "M_MBOX", 0, 0 + Graphic "M_MWALK1", 24, 12 { useoffsets } } Graphic optional P_MWALK2, 112, 136 { - Patch "M_MBOX", 0, 0 - Patch "M_MWALK2", 24, 12 { useoffsets } + Graphic "M_MBOX", 0, 0 + Graphic "M_MWALK2", 24, 12 { useoffsets } } Graphic optional P_MWALK3, 112, 136 { - Patch "M_MBOX", 0, 0 - Patch "M_MWALK3", 24, 12 { useoffsets } + Graphic "M_MBOX", 0, 0 + Graphic "M_MWALK3", 24, 12 { useoffsets } } Graphic optional P_MWALK4, 112, 136 { - Patch "M_MBOX", 0, 0 - Patch "M_MWALK4", 24, 12 { useoffsets } + Graphic "M_MBOX", 0, 0 + Graphic "M_MWALK4", 24, 12 { useoffsets } } From 1e83634089d5e1aed4f1ef211528c51e501a7bfd Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Tue, 21 Sep 2010 20:07:03 +0000 Subject: [PATCH 056/815] - Fixed: The aspectratio SBarInfo command never ticked its contents. SVN r2842 (trunk) --- src/g_shared/sbarinfo_commands.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 792115b5e..a34983a3e 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1565,6 +1565,8 @@ class CommandAspectRatio : public SBarInfoCommandFlowControl } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { + SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + SetTruth(CheckRatio(screen->GetWidth(), screen->GetHeight()) == ratio, block, statusBar); } protected: From 15d74dab563613aac8d30c84d9bfadc47b931d20 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Tue, 21 Sep 2010 20:35:40 +0000 Subject: [PATCH 057/815] - Fixed: GCC warnings. SVN r2843 (trunk) --- src/keysections.cpp | 4 ++-- src/menu/listmenu.cpp | 4 ++-- src/menu/loadsavemenu.cpp | 4 ++-- src/menu/optionmenu.cpp | 6 +++--- src/menu/playermenu.cpp | 2 +- src/p_conversation.cpp | 2 +- src/p_spec.cpp | 2 +- src/statistics.cpp | 2 +- src/textures/multipatchtexture.cpp | 5 +++-- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/keysections.cpp b/src/keysections.cpp index 94c551002..fc0e668ba 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -80,9 +80,9 @@ void M_SaveCustomKeys (FConfigFile *config, char *section, char *subsection, siz { for (unsigned i=0; imItems[i]->CheckCoordinate(x, y)) { - if (i != mDesc->mSelectedItem) + if (i != static_cast (mDesc->mSelectedItem)) { //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); } @@ -258,7 +258,7 @@ void DListMenu::Drawer () { for(unsigned i=0;imItems.Size(); i++) { - if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == i); + if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(static_cast (mDesc->mSelectedItem) == i); } if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size()) mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector); diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index cb73c2586..8a07976bf 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -604,7 +604,7 @@ void DLoadSaveMenu::Drawer () { color = CR_ORANGE; } - else if (j == Selected) + else if (j == static_cast (Selected)) { color = CR_WHITE; } @@ -612,7 +612,7 @@ void DLoadSaveMenu::Drawer () { color = CR_TAN; } - if (j == Selected) + if (j == static_cast (Selected)) { screen->Clear (listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), -1, diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index ff13d4b24..988e87e37 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -392,13 +392,13 @@ void DOptionMenu::Drawer () for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight) { // Don't scroll the uppermost items - if (i == mDesc->mScrollTop) + if (i == static_cast (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()) + int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, static_cast (mDesc->mSelectedItem) == i); + if (cur_indent >= 0 && static_cast (mDesc->mSelectedItem) == i && mDesc->mItems[i]->Selectable()) { if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this) { diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 8a5d08507..2bf6ae97f 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -802,7 +802,7 @@ void DPlayerMenu::UpdateSkins() { int j = PlayerSkins.Push(i); li->SetString(j, skins[i].name); - if (players[consoleplayer].userinfo.skin == i) + if (static_cast (players[consoleplayer].userinfo.skin) == i) { sel = j; } diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 7283c4abd..772cdc3a7 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1031,7 +1031,7 @@ public: screen->DrawText (SmallFont, CR_GREEN, x, y, mResponseLines[i], DTA_Clean, true, TAG_DONE); - if (i == mResponses[response]) + if (static_cast (i) == mResponses[response]) { char tbuf[16]; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 2e3f1b6e2..be2c4163d 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -256,7 +256,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) // end of changed code if (developer && buttonSuccess) { - Printf ("Line special %d activated on line %i\n", special, line - lines); + Printf ("Line special %d activated on line %i\n", special, static_cast (line - lines)); } return true; } diff --git a/src/statistics.cpp b/src/statistics.cpp index f12a458dc..f5778af44 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -595,7 +595,7 @@ FString GetStatString() CCMD(printstats) { StoreLevelStats(); // Refresh the current level's results. - Printf("%s", GetStatString()); + Printf("%s", GetStatString().GetChars()); } diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index 0c0a54499..76a2dd612 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -1200,6 +1200,7 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) bMultiPatch = true; sc.SetCMode(true); sc.MustGetString(); + const char* textureName = NULL; if (sc.Compare("optional")) { bSilent = true; @@ -1208,11 +1209,11 @@ FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, int usetype) { // this is not right. Apparently a texture named 'optional' is being defined right now... sc.UnGet(); - sc.String = "optional"; + textureName = "optional"; bSilent = false; } } - uppercopy(Name, sc.String); + uppercopy(Name, !textureName ? sc.String : textureName); Name[8] = 0; sc.MustGetStringName(","); sc.MustGetNumber(); From 16fa8cc249cd56193d6cf604588754e30e91efda Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 22 Sep 2010 06:52:48 +0000 Subject: [PATCH 058/815] - Am I the only one who thinks that static_casts can create really clunky code? (Thanks, GCC, for your overly paranoid warning settings that necessitate such ugliness. :( ) SVN r2844 (trunk) --- src/g_shared/sbarinfo_commands.cpp | 2 +- src/menu/listmenu.cpp | 4 ++-- src/menu/loadsavemenu.cpp | 5 +++-- src/menu/optionmenu.cpp | 7 ++++--- src/menu/playermenu.cpp | 4 ++-- src/p_conversation.cpp | 9 +++++---- src/p_spec.cpp | 2 +- 7 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index a34983a3e..8c55206c3 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -361,7 +361,7 @@ class CommandDrawSwitchableImage : public CommandDrawImage } } // [BL] I have word that MSVC++ wants this static_cast ;) Shut up MSVC! - for(unsigned int i = 0;i < static_cast (conditionAnd ? 3 : 1);i++) + for(unsigned int i = 0;i < (conditionAnd ? 3u : 1u);i++) { sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 89d1fd879..2f14ee5e1 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -218,7 +218,7 @@ bool DListMenu::MouseEvent(int type, int x, int y) { if (mDesc->mItems[i]->CheckCoordinate(x, y)) { - if (i != static_cast (mDesc->mSelectedItem)) + if ((int)i != mDesc->mSelectedItem) { //S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); } @@ -258,7 +258,7 @@ void DListMenu::Drawer () { for(unsigned i=0;imItems.Size(); i++) { - if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(static_cast (mDesc->mSelectedItem) == i); + if (mDesc->mItems[i]->mEnabled) mDesc->mItems[i]->Drawer(mDesc->mSelectedItem == (int)i); } if (mDesc->mSelectedItem >= 0 && mDesc->mSelectedItem < (int)mDesc->mItems.Size()) mDesc->mItems[mDesc->mSelectedItem]->DrawSelector(mDesc->mSelectOfsX, mDesc->mSelectOfsY, mDesc->mSelector); diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 8a07976bf..d0ac38894 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -604,7 +604,7 @@ void DLoadSaveMenu::Drawer () { color = CR_ORANGE; } - else if (j == static_cast (Selected)) + else if ((int)j == Selected) { color = CR_WHITE; } @@ -612,7 +612,8 @@ void DLoadSaveMenu::Drawer () { color = CR_TAN; } - if (j == static_cast (Selected)) + + if ((int)j == Selected) { screen->Clear (listboxLeft, listboxTop+rowHeight*i, listboxRight, listboxTop+rowHeight*(i+1), -1, diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 988e87e37..9d3213716 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -392,13 +392,14 @@ void DOptionMenu::Drawer () for (i = 0; i < mDesc->mItems.Size() && y <= lastrow; i++, y += fontheight) { // Don't scroll the uppermost items - if (i == static_cast (mDesc->mScrollTop)) + if ((int)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, static_cast (mDesc->mSelectedItem) == i); - if (cur_indent >= 0 && static_cast (mDesc->mSelectedItem) == i && mDesc->mItems[i]->Selectable()) + bool isSelected = mDesc->mSelectedItem == (int)i; + int cur_indent = mDesc->mItems[i]->Draw(mDesc, y, indent, isSelected); + if (cur_indent >= 0 && isSelected && mDesc->mItems[i]->Selectable()) { if (((DMenu::MenuTime%8) < 6) || DMenu::CurrentMenu != this) { diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 2bf6ae97f..e41d6094d 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -796,13 +796,13 @@ void DPlayerMenu::UpdateSkins() else { PlayerSkins.Clear(); - for(unsigned i=0;i<(unsigned)numskins; i++) + for(int i=0;i<(int)numskins; i++) { if (PlayerClass->CheckSkin(i)) { int j = PlayerSkins.Push(i); li->SetString(j, skins[i].name); - if (static_cast (players[consoleplayer].userinfo.skin) == i) + if (players[consoleplayer].userinfo.skin == i) { sel = j; } diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 772cdc3a7..3c631c4e9 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -929,7 +929,7 @@ public: void Drawer() { const char *speakerName; - int i, x, y, linesize; + int x, y, linesize; int width, fontheight; int labelofs; @@ -973,6 +973,7 @@ public: // Dim the screen behind the dialogue (but only if there is no backdrop). if (!CurNode->Backdrop.isValid()) { + int i; for (i = 0; mDialogueLines[i].Width >= 0; ++i) { } screen->Dim (0, 0.45f, 14 * screen->GetWidth() / 320, 13 * screen->GetHeight() / 200, @@ -995,7 +996,7 @@ public: y += linesize * 3 / 2; } x = 24 * screen->GetWidth() / 320; - for (i = 0; mDialogueLines[i].Width >= 0; ++i) + for (int i = 0; mDialogueLines[i].Width >= 0; ++i) { screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, mDialogueLines[i].Text, DTA_CleanNoMove, true, TAG_DONE); @@ -1024,14 +1025,14 @@ public: fontheight = OptionSettings.mLinespacing; int response = 0; - for (i = 0; i < (int)mResponseLines.Size(); i++, y += fontheight) + for (unsigned i = 0; i < 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 (static_cast (i) == mResponses[response]) + if (i == mResponses[response]) { char tbuf[16]; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index be2c4163d..d48ef90d6 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -256,7 +256,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) // end of changed code if (developer && buttonSuccess) { - Printf ("Line special %d activated on line %i\n", special, static_cast (line - lines)); + Printf ("Line special %d activated on line %i\n", special, int(line - lines)); } return true; } From b8fa340986c6a388759c1c5345f716ee2c174945 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 23 Sep 2010 01:51:00 +0000 Subject: [PATCH 059/815] - Fixed: ACS's GetActorX, GetActorY, GetActorZ, GetActorFloorZ, GetActorCeilingZ, GetActorAngle, and GetActorPitch did not have NULL pointer checks for the TID == 0 case. SVN r2845 (trunk) --- src/p_acs.cpp | 95 ++++++++------------------------------------------- 1 file changed, 15 insertions(+), 80 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e147648e5..b789f22b9 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5828,106 +5828,41 @@ int DLevelScript::RunScript () case PCD_GETACTORY: case PCD_GETACTORZ: { - if(STACK(1) == 0) - { - STACK(1) = (&activator->x)[pcd - PCD_GETACTORX]; - } - else - { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) - { - STACK(1) = 0; - } - else - { - STACK(1) = (&actor->x)[pcd - PCD_GETACTORX]; - } - } + AActor *actor = SingleActorFromTID(STACK(1), activator); + STACK(1) = actor == NULL ? 0 : (&actor->x)[pcd - PCD_GETACTORX]; } break; case PCD_GETACTORFLOORZ: + { + AActor *actor = SingleActorFromTID(STACK(1), activator); + STACK(1) = actor == NULL ? 0 : actor->floorz; + } + break; + case PCD_GETACTORCEILINGZ: { - if(STACK(1) == 0) - { - if (pcd == PCD_GETACTORFLOORZ) - { - STACK(1) = activator->floorz; - } - else if(STACK(1) == 0) - { - STACK(1) = activator->ceilingz; - } - } - else - { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) - { - STACK(1) = 0; - } - else if (pcd == PCD_GETACTORFLOORZ) - { - STACK(1) = actor->floorz; - } - else - { - STACK(1) = actor->ceilingz; - } - } + AActor *actor = SingleActorFromTID(STACK(1), activator); + STACK(1) = actor == NULL ? 0 : actor->ceilingz; } break; case PCD_GETACTORANGLE: { - if(STACK(1) == 0) - { - STACK(1) = activator->angle >> 16; - } - else - { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) - { - STACK(1) = 0; - } - else - { - STACK(1) = actor->angle >> 16; - } - } + AActor *actor = SingleActorFromTID(STACK(1), activator); + STACK(1) = actor == NULL ? 0 : actor->angle >> 16; } break; case PCD_GETACTORPITCH: { - if(STACK(1) == 0) - { - STACK(1) = activator->pitch >> 16; - } - else - { - AActor *actor = SingleActorFromTID (STACK(1), activator); - - if (actor == NULL) - { - STACK(1) = 0; - } - else - { - STACK(1) = actor->pitch >> 16; - } - } + AActor *actor = SingleActorFromTID(STACK(1), activator); + STACK(1) = actor == NULL ? 0 : actor->pitch >> 16; } break; case PCD_GETLINEROWOFFSET: - if (activationline) + if (activationline != NULL) { PushToStack (activationline->sidedef[0]->GetTextureYOffset(side_t::mid) >> FRACBITS); } From b9185f7c627063a10e166ca473b3b26f04a3f19f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 23 Sep 2010 01:56:04 +0000 Subject: [PATCH 060/815] - Added a NULL skins[] check to AActor::SetState(). SVN r2846 (trunk) --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 0257fc23c..71e0a2512 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -547,7 +547,7 @@ bool AActor::SetState (FState *newstate, bool nofunction) // for Dehacked, I would move sprite changing out of the states // altogether, since actors rarely change their sprites after // spawning. - if (player != NULL) + if (player != NULL && skins != NULL) { sprite = skins[player->userinfo.skin].sprite; } From 917e93e63354d7ef7365dfe29815b457ad0e79a8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 23 Sep 2010 06:14:30 +0000 Subject: [PATCH 061/815] - fixed: The check for savegames without picture was reversed. - fixed: Savegames that were saved without picture did not display the 'No picture' message because the empty PNG container was still recognized as image. SVN r2847 (trunk) --- src/menu/loadsavemenu.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index d0ac38894..be59acdb6 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -506,8 +506,12 @@ void DLoadSaveMenu::ExtractSaveData (int index) // Extract pic SavePic = PNGTexture_CreateFromFile(png, node->Filename); - delete png; + if (SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1) + { + delete SavePic; + SavePic = NULL; + } } fclose (file); } @@ -551,7 +555,7 @@ void DLoadSaveMenu::Drawer () if (SaveGames.Size() > 0) { const char *text = - (Selected == -1 || SaveGames[Selected]->bOldVersion) + (Selected == -1 || !SaveGames[Selected]->bOldVersion) ? GStrings("MNU_NOPICTURE") : GStrings("MNU_DIFFVERSION"); const int textlen = SmallFont->StringWidth (text)*CleanXfac; From 46eebe29a485ef4044c809050657bbdbf30f5774 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 23 Sep 2010 22:37:25 +0000 Subject: [PATCH 062/815] - Update the HMI player to use the division information stored in the song and check the full file signature. SVN r2848 (trunk) --- src/sound/music_hmi_midiout.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index c2075bf1a..c847217ee 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -41,7 +41,7 @@ // MACROS ------------------------------------------------------------------ -#define SONG_MAGIC "HMI-MIDISONG" +#define SONG_MAGIC "HMI-MIDISONG061595" #define TRACK_MAGIC "HMI-MIDITRACK" // Used by SendCommand to check for unexpected end-of-track conditions. @@ -53,6 +53,7 @@ } // In song header +#define DIVISION_OFFSET 0xD2 #define TRACK_COUNT_OFFSET 0xE4 #define TRACK_DIR_PTR_OFFSET 0xE8 @@ -164,7 +165,7 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) } // Do some validation of the MIDI file - if (memcmp(MusHeader, SONG_MAGIC, 12) != 0) + if (memcmp(MusHeader, SONG_MAGIC, sizeof(SONG_MAGIC)) != 0) return; NumTracks = GetShort(MusHeader + TRACK_COUNT_OFFSET); @@ -174,7 +175,8 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) } // The division is the number of pulses per quarter note (PPQN). - Division = 60; + Division = GetShort(MusHeader + DIVISION_OFFSET); + InitialTempo = 4000000; Tracks = new TrackInfo[NumTracks + 1]; int track_dir = GetInt(MusHeader + TRACK_DIR_PTR_OFFSET); From 4397ef3323420d392324530d4ebf098dde8632e2 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 24 Sep 2010 02:46:48 +0000 Subject: [PATCH 063/815] - Added HMP file support. SVN r2849 (trunk) --- src/sound/i_music.cpp | 7 + src/sound/i_musicinterns.h | 6 + src/sound/music_hmi_midiout.cpp | 259 ++++++++++++++++++++++++++------ 3 files changed, 230 insertions(+), 42 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 172553df5..76e33ff83 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -463,6 +463,13 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { miditype = MIDI_HMI; } + // Check for HMP format + else + if (id[0] == MAKE_ID('H','M','I','M') && + id[1] == MAKE_ID('I','D','I','P')) + { + miditype = MIDI_HMI; + } // Check for MIDI format else if (id[0] == MAKE_ID('M','T','h','d')) { diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index ccf014555..32acaec48 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -500,6 +500,8 @@ public: protected: HMISong(const HMISong *original, const char *filename, EMIDIDevice type); // file dump constructor + void SetupForHMI(int len); + void SetupForHMP(int len); void CheckCaps(int tech); void DoInitialSetup(); void DoRestart(); @@ -514,6 +516,9 @@ protected: TrackInfo *FindNextDue (); void SetTempo(int new_tempo); + static DWORD ReadVarLenHMI(TrackInfo *); + static DWORD ReadVarLenHMP(TrackInfo *); + struct AutoNoteOff { DWORD Delay; @@ -541,6 +546,7 @@ protected: TrackInfo *Tracks; TrackInfo *TrackDue; TrackInfo *FakeTrack; + DWORD (*ReadVarLen)(TrackInfo *); NoteOffQueue NoteOffs; }; diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index c847217ee..e22149f8d 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -41,7 +41,8 @@ // MACROS ------------------------------------------------------------------ -#define SONG_MAGIC "HMI-MIDISONG061595" +#define HMP_NEW_DATE "013195" +#define HMI_SONG_MAGIC "HMI-MIDISONG061595" #define TRACK_MAGIC "HMI-MIDITRACK" // Used by SendCommand to check for unexpected end-of-track conditions. @@ -53,15 +54,26 @@ } // In song header -#define DIVISION_OFFSET 0xD2 -#define TRACK_COUNT_OFFSET 0xE4 -#define TRACK_DIR_PTR_OFFSET 0xE8 +#define HMI_DIVISION_OFFSET 0xD2 +#define HMI_TRACK_COUNT_OFFSET 0xE4 +#define HMI_TRACK_DIR_PTR_OFFSET 0xE8 + +#define HMP_DIVISION_OFFSET 0x38 +#define HMP_TRACK_COUNT_OFFSET 0x30 +#define HMP_DESIGNATIONS_OFFSET 0x94 +#define HMP_TRACK_OFFSET_0 0x308 // original HMP +#define HMP_TRACK_OFFSET_1 0x388 // newer HMP // In track header -#define TRACK_DATA_PTR_OFFSET 0x57 -#define TRACK_DESIGNATION_OFFSET 0x99 +#define HMITRACK_DATA_PTR_OFFSET 0x57 +#define HMITRACK_DESIGNATION_OFFSET 0x99 -#define NUM_DESIGNATIONS 8 +#define HMPTRACK_LEN_OFFSET 4 +#define HMPTRACK_DESIGNATION_OFFSET 8 +#define HMPTRACK_MIDI_DATA_OFFSET 12 + +#define NUM_HMP_DESIGNATIONS 5 +#define NUM_HMI_DESIGNATIONS 8 // MIDI device types for designation #define HMI_DEV_GM 0xA000 // Generic General MIDI (not a real device) @@ -103,12 +115,13 @@ struct HMISong::TrackInfo size_t MaxTrackP; DWORD Delay; DWORD PlayedTime; - WORD Designation[NUM_DESIGNATIONS]; + WORD Designation[NUM_HMI_DESIGNATIONS]; bool Enabled; bool Finished; BYTE RunningStatus; - DWORD ReadVarLen (); + DWORD ReadVarLenHMI(); + DWORD ReadVarLenHMP(); }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -139,9 +152,6 @@ extern char MIDI_CommonLengths[15]; HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { - int p; - int i; - #ifdef _WIN32 if (ExitEvent == NULL) { @@ -154,6 +164,7 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) } MusHeader = new BYTE[len]; SongLen = len; + NumTracks = 0; if (file != NULL) { if (fread(MusHeader, 1, len, file) != (size_t)len) @@ -165,21 +176,59 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) } // Do some validation of the MIDI file - if (memcmp(MusHeader, SONG_MAGIC, sizeof(SONG_MAGIC)) != 0) - return; + if (memcmp(MusHeader, HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0) + { + SetupForHMI(len); + } + else if (((DWORD *)MusHeader)[0] == MAKE_ID('H','M','I','M') && + ((DWORD *)MusHeader)[1] == MAKE_ID('I','D','I','P')) + { + SetupForHMP(len); + } +} + +//========================================================================== +// +// HMISong Destructor +// +//========================================================================== + +HMISong::~HMISong() +{ + if (Tracks != NULL) + { + delete[] Tracks; + } + if (MusHeader != NULL) + { + delete[] MusHeader; + } +} + +//========================================================================== +// +// HMISong :: SetupForHMI +// +//========================================================================== + +void HMISong::SetupForHMI(int len) +{ + int i, p; + + ReadVarLen = ReadVarLenHMI; + NumTracks = GetShort(MusHeader + HMI_TRACK_COUNT_OFFSET); - NumTracks = GetShort(MusHeader + TRACK_COUNT_OFFSET); if (NumTracks <= 0) { return; } // The division is the number of pulses per quarter note (PPQN). - Division = GetShort(MusHeader + DIVISION_OFFSET); + Division = GetShort(MusHeader + HMI_DIVISION_OFFSET); InitialTempo = 4000000; Tracks = new TrackInfo[NumTracks + 1]; - int track_dir = GetInt(MusHeader + TRACK_DIR_PTR_OFFSET); + int track_dir = GetInt(MusHeader + HMI_TRACK_DIR_PTR_OFFSET); // Gather information about each track for (i = 0, p = 0; i < NumTracks; ++i) @@ -187,7 +236,7 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) int start = GetInt(MusHeader + track_dir + i*4); int tracklen, datastart; - if (start > len - TRACK_DESIGNATION_OFFSET - 4) + if (start > len - HMITRACK_DESIGNATION_OFFSET - 4) { // Track is incomplete. continue; } @@ -216,7 +265,7 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) } // Offset to actual MIDI events. - datastart = GetInt(MusHeader + start + TRACK_DATA_PTR_OFFSET); + datastart = GetInt(MusHeader + start + HMITRACK_DATA_PTR_OFFSET); tracklen -= datastart; if (tracklen <= 0) { @@ -230,9 +279,9 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) // Retrieve track designations. We can't check them yet, since we have not yet // connected to the MIDI device. - for (int ii = 0; ii < NUM_DESIGNATIONS; ++ii) + for (int ii = 0; ii < NUM_HMI_DESIGNATIONS; ++ii) { - Tracks[p].Designation[ii] = GetShort(MusHeader + start + TRACK_DESIGNATION_OFFSET + ii*2); + Tracks[p].Designation[ii] = GetShort(MusHeader + start + HMITRACK_DESIGNATION_OFFSET + ii*2); } p++; @@ -241,29 +290,108 @@ HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) // In case there were fewer actual chunks in the file than the // header specified, update NumTracks with the current value of p. NumTracks = p; - - if (NumTracks == 0) - { // No tracks, so nothing to play - return; - } } //========================================================================== // -// HMISong Destructor +// HMISong :: SetupForHMP // //========================================================================== -HMISong::~HMISong () +void HMISong::SetupForHMP(int len) { - if (Tracks != NULL) + int track_data; + int i, p; + + ReadVarLen = ReadVarLenHMP; + if (MusHeader[8] == 0) { - delete[] Tracks; + track_data = HMP_TRACK_OFFSET_0; } - if (MusHeader != NULL) + else if (memcmp(MusHeader + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0) { - delete[] MusHeader; + track_data = HMP_TRACK_OFFSET_1; } + else + { // unknown HMIMIDIP version + return; + } + + NumTracks = GetInt(MusHeader + HMP_TRACK_COUNT_OFFSET); + + if (NumTracks <= 0) + { + return; + } + + // The division is the number of pulses per quarter note (PPQN). + Division = GetInt(MusHeader + HMP_DIVISION_OFFSET); + InitialTempo = 1000000; + + Tracks = new TrackInfo[NumTracks + 1]; + + // Gather information about each track + for (i = 0, p = 0; i < NumTracks; ++i) + { + int start = track_data; + int tracklen; + + if (start > len - HMPTRACK_MIDI_DATA_OFFSET) + { // Track is incomplete. + break; + } + + tracklen = GetInt(MusHeader + start + HMPTRACK_LEN_OFFSET); + track_data += tracklen; + + // Clamp incomplete tracks to the end of the file. + tracklen = MIN(tracklen, len - start); + if (tracklen <= 0) + { + continue; + } + + // Subtract track header size. + tracklen -= HMPTRACK_MIDI_DATA_OFFSET; + if (tracklen <= 0) + { + continue; + } + + // Store track information + Tracks[p].TrackBegin = MusHeader + start + HMPTRACK_MIDI_DATA_OFFSET; + Tracks[p].TrackP = 0; + Tracks[p].MaxTrackP = tracklen; + + // Retrieve track designations. We can't check them yet, since we have not yet + // connected to the MIDI device. +#if 0 + // This is completely a guess based on knowledge of how designations work with + // HMI files. Some songs contain nothing but zeroes for this data, so I'd rather + // not go around using it without confirmation. + + Printf("Track %d: %d %08x %d: \034I", i, GetInt(MusHeader + start), + GetInt(MusHeader + start + 4), GetInt(MusHeader + start + 8)); + + int designations = HMP_DESIGNATIONS_OFFSET + + GetInt(MusHeader + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS; + for (int ii = 0; ii < NUM_HMP_DESIGNATIONS; ++ii) + { + Printf(" %04x", GetInt(MusHeader + designations + ii*4)); + } + Printf("\n"); +#endif + Tracks[p].Designation[0] = HMI_DEV_GM; + Tracks[p].Designation[1] = HMI_DEV_GUS; + Tracks[p].Designation[2] = HMI_DEV_OPL2; + Tracks[p].Designation[3] = 0; + + p++; + } + + // In case there were fewer actual chunks in the file than the + // header specified, update NumTracks with the current value of p. + NumTracks = p; } //========================================================================== @@ -295,7 +423,7 @@ void HMISong::CheckCaps(int tech) { Tracks[i].Enabled = false; // Track designations are stored in a 0-terminated array. - for (int j = 0; j < NUM_DESIGNATIONS && Tracks[i].Designation[j] != 0; ++j) + for (int j = 0; j < countof(Tracks[i].Designation) && Tracks[i].Designation[j] != 0; ++j) { if (Tracks[i].Designation[j] == tech) { @@ -367,7 +495,7 @@ void HMISong :: DoRestart() ProcessInitialMetaEvents (); for (i = 0; i < NumTracks; ++i) { - Tracks[i].Delay = Tracks[i].ReadVarLen(); + Tracks[i].Delay = ReadVarLen(&Tracks[i]); } Tracks[i].Delay = 0; // for the FakeTrack Tracks[i].Enabled = true; @@ -534,9 +662,9 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } events += 3; - if ((event & 0x70) == (MIDI_NOTEON & 0x70)) + if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70)) { // HMI note on events include the time until an implied note off event. - NoteOffs.AddNoteOff(track->ReadVarLen(), event & 0x0F, data1); + NoteOffs.AddNoteOff(track->ReadVarLenHMI(), event & 0x0F, data1); } } else @@ -546,7 +674,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) // anything that played before. if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) { - len = track->ReadVarLen (); + len = ReadVarLen(track); track->TrackP += len; } else if (event == MIDI_META) @@ -554,7 +682,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) // It's a meta-event event = track->TrackBegin[track->TrackP++]; CHECK_FINISHED - len = track->ReadVarLen (); + len = ReadVarLen(track); CHECK_FINISHED if (track->TrackP + len <= track->MaxTrackP) @@ -614,7 +742,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } if (!track->Finished) { - track->Delay = track->ReadVarLen(); + track->Delay = ReadVarLen(track); } return events; } @@ -644,7 +772,7 @@ void HMISong::ProcessInitialMetaEvents () { event = track->TrackBegin[track->TrackP+2]; track->TrackP += 3; - len = track->ReadVarLen (); + len = ReadVarLen(track); if (track->TrackP + len <= track->MaxTrackP) { switch (event) @@ -673,13 +801,35 @@ void HMISong::ProcessInitialMetaEvents () //========================================================================== // -// HMISong :: TrackInfo :: ReadVarLen +// HMISong :: ReadVarLenHMI static +// +//========================================================================== + +DWORD HMISong::ReadVarLenHMI(TrackInfo *track) +{ + return track->ReadVarLenHMI(); +} + +//========================================================================== +// +// HMISong :: ReadVarLenHMP static +// +//========================================================================== + +DWORD HMISong::ReadVarLenHMP(TrackInfo *track) +{ + return track->ReadVarLenHMP(); +} + +//========================================================================== +// +// HMISong :: TrackInfo :: ReadVarLenHMI // // Reads a variable-length SMF number. // //========================================================================== -DWORD HMISong::TrackInfo::ReadVarLen () +DWORD HMISong::TrackInfo::ReadVarLenHMI() { DWORD time = 0, t = 0x80; @@ -691,6 +841,31 @@ DWORD HMISong::TrackInfo::ReadVarLen () return time; } +//========================================================================== +// +// HMISong :: TrackInfo :: ReadVarLenHMP +// +// Reads a variable-length HMP number. This is similar to the standard SMF +// variable length number, except it's stored little-endian, and the high +// bit set means the number is done. +// +//========================================================================== + +DWORD HMISong::TrackInfo::ReadVarLenHMP() +{ + DWORD time = 0; + BYTE t = 0; + int off = 0; + + while (!(t & 0x80) && TrackP < MaxTrackP) + { + t = TrackBegin[TrackP++]; + time |= (t & 127) << off; + off += 7; + } + return time; +} + //========================================================================== // // HMISong :: NoteOffQueue :: AddNoteOff From 3f420c97bda59bda52ccd3fed8daa5a34353c66d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Sep 2010 14:27:52 +0000 Subject: [PATCH 064/815] - allow setting the startup screen's title through GAMEINFO lump. SVN r2850 (trunk) --- src/d_iwad.cpp | 6 ++++++ src/d_main.cpp | 14 ++++++++++++++ src/d_main.h | 9 +++++++++ src/p_setup.cpp | 2 ++ src/win32/i_main.cpp | 27 ++++++++++++++++++++------- src/win32/i_system.cpp | 16 ---------------- src/win32/i_system.h | 3 --- 7 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 499d7cbd7..ae208e25a 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -668,6 +668,12 @@ const IWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const ch EIWADType iwadType = IdentifyVersion(wadfiles, iwad, basewad); gameiwad = iwadType; const IWADInfo *iwad_info = &IWADInfos[iwadType]; + if (DoomStartupInfo.Name.IsEmpty()) DoomStartupInfo.Name = iwad_info->Name; + if (DoomStartupInfo.BkColor == 0 && DoomStartupInfo.FgColor == 0) + { + DoomStartupInfo.BkColor = iwad_info->BkColor; + DoomStartupInfo.FgColor = iwad_info->FgColor; + } I_SetIWADInfo(iwad_info); return iwad_info; } \ No newline at end of file diff --git a/src/d_main.cpp b/src/d_main.cpp index d24b8c57d..b26d29575 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -203,6 +203,7 @@ bool PageBlank; FTexture *Page; FTexture *Advisory; bool nospriterename; +FStartupInfo DoomStartupInfo; cycle_t FrameCycles; @@ -1697,6 +1698,19 @@ static FString ParseGameInfo(TArray &pwads, const char *fn, const char { nospriterename = true; } + else if (!nextKey.CompareNoCase("STARTUPTITLE")) + { + sc.MustGetString(); + DoomStartupInfo.Name = sc.String; + } + else if (!nextKey.CompareNoCase("STARTUPCOLORS")) + { + sc.MustGetString(); + DoomStartupInfo.FgColor = V_GetColor(NULL, sc.String); + sc.MustGetStringName(","); + sc.MustGetString(); + DoomStartupInfo.BkColor = V_GetColor(NULL, sc.String); + } } return iwad; } diff --git a/src/d_main.h b/src/d_main.h index 517653bec..f72f7a69b 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -106,6 +106,15 @@ struct IWADInfo int flags; }; +struct FStartupInfo +{ + FString Name; + DWORD FgColor; // Foreground color for title banner + DWORD BkColor; // Background color for title banner +}; + +extern FStartupInfo DoomStartupInfo; + extern const IWADInfo IWADInfos[NUM_IWAD_TYPES]; extern EIWADType gameiwad; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 09d2d2e8d..b26d44ee5 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -2250,6 +2250,8 @@ static void P_LoopSidedefs (bool firstloop) right = bestright; } } + assert((unsigned)i<(unsigned)numsides); + assert(right<(unsigned)numsides); sides[i].RightSide = right; sides[right].LeftSide = i; } diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 808ae4fbe..782f9fa5f 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -355,7 +355,7 @@ void LayoutMainWindow (HWND hWnd, HWND pane) w = rect.right; h = rect.bottom; - if (DoomStartupInfo != NULL && GameTitleWindow != NULL) + if (DoomStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL) { bannerheight = GameTitleFontHeight + 5; MoveWindow (GameTitleWindow, 0, 0, w, bannerheight, TRUE); @@ -399,6 +399,19 @@ void LayoutMainWindow (HWND hWnd, HWND pane) } } + +//========================================================================== +// +// I_SetIWADInfo +// +//========================================================================== + +void I_SetIWADInfo(const IWADInfo *info) +{ + // Make the startup banner show itself + LayoutMainWindow(Window, NULL); +} + //========================================================================== // // LConProc @@ -501,7 +514,7 @@ LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DRAWITEM: // Draw title banner. - if (wParam == IDC_STATIC_TITLE && DoomStartupInfo != NULL) + if (wParam == IDC_STATIC_TITLE && DoomStartupInfo.Name.IsNotEmpty()) { const PalEntry *c; @@ -511,7 +524,7 @@ LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) // Draw the background. rect = drawitem->rcItem; rect.bottom -= 1; - c = (const PalEntry *)&DoomStartupInfo->BkColor; + c = (const PalEntry *)&DoomStartupInfo.BkColor; hbr = CreateSolidBrush (RGB(c->r,c->g,c->b)); FillRect (drawitem->hDC, &drawitem->rcItem, hbr); DeleteObject (hbr); @@ -519,14 +532,14 @@ LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) // Calculate width of the title string. SetTextAlign (drawitem->hDC, TA_TOP); oldfont = SelectObject (drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject (DEFAULT_GUI_FONT)); - titlelen = (int)strlen (DoomStartupInfo->Name); - GetTextExtentPoint32 (drawitem->hDC, DoomStartupInfo->Name, titlelen, &size); + titlelen = (int)DoomStartupInfo.Name.Len(); + GetTextExtentPoint32 (drawitem->hDC, DoomStartupInfo.Name, titlelen, &size); // Draw the title. - c = (const PalEntry *)&DoomStartupInfo->FgColor; + c = (const PalEntry *)&DoomStartupInfo.FgColor; SetTextColor (drawitem->hDC, RGB(c->r,c->g,c->b)); SetBkMode (drawitem->hDC, TRANSPARENT); - TextOut (drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, DoomStartupInfo->Name, titlelen); + TextOut (drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, DoomStartupInfo.Name, titlelen); SelectObject (drawitem->hDC, oldfont); return TRUE; } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index f2178ffbf..d0362b13f 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -143,8 +143,6 @@ UINT MillisecondsPerTic; HANDLE NewTicArrived; uint32 LanguageIDs[4]; -const IWADInfo *DoomStartupInfo; - int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); void (*I_FreezeTime) (bool frozen); @@ -833,20 +831,6 @@ void STACK_ARGS I_Error(const char *error, ...) throw CRecoverableError(errortext); } -//========================================================================== -// -// I_SetIWADInfo -// -//========================================================================== - -void I_SetIWADInfo(const IWADInfo *info) -{ - DoomStartupInfo = info; - - // Make the startup banner show itself - LayoutMainWindow(Window, NULL); -} - //========================================================================== // // ToEditControl diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 1a5e755e3..d7af1b4b1 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -138,9 +138,6 @@ bool I_WriteIniFailed (); unsigned int I_MSTime (void); unsigned int I_FPSTime(); -// [RH] Title banner to display during startup -extern const IWADInfo *DoomStartupInfo; - // [RH] Used by the display code to set the normal window procedure void I_SetWndProc(); From 3f69b63873cc4d61f145d6d33ced570a08bff8d9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Sep 2010 23:28:44 +0000 Subject: [PATCH 065/815] - fixed: Backing out of a skill confirmation message screen caused that skill to be used for the next game if the menu wasn't fully closed first. SVN r2851 (trunk) --- src/menu/menu.cpp | 5 +++-- src/namedef.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 2732b7cfb..c0ef72a31 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -382,14 +382,15 @@ void M_SetMenu(FName menu, int param) const char *msg = AllSkills[param].MustConfirmText; if (*msg==0) msg = GStrings("NIGHTMARE"); - M_StartMessage (msg, 0, NAME_Startgame); + M_StartMessage (msg, 0, NAME_StartgameConfirmed); 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; + GameStartupInfo.Skill = param; + case NAME_StartgameConfirmed: G_DeferedInitNew (&GameStartupInfo); if (gamestate == GS_FULLCONSOLE) diff --git a/src/namedef.h b/src/namedef.h index 012d85afa..ecbe97865 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -475,6 +475,7 @@ xx(HexenDefaultPlayerclassmenu) xx(Skillmenu) xx(Startgame) xx(StartgameConfirm) +xx(StartgameConfirmed) xx(Loadgamemenu) xx(Savegamemenu) xx(Readthismenu) From 17f9e687bde6c264579c605027d74ae5bc207d32 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 26 Sep 2010 05:31:52 +0000 Subject: [PATCH 066/815] - Added cursorpic gameinfo property to set the mouse cursor image. SVN r2852 (trunk) --- src/d_main.cpp | 5 ++++- src/gi.cpp | 1 + src/gi.h | 1 + wadsrc/static/mapinfo/doomcommon.txt | 1 + wadsrc/static/mapinfo/heretic.txt | 1 + wadsrc/static/mapinfo/hexen.txt | 1 + wadsrc/static/mapinfo/strife.txt | 1 + 7 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index b26d29575..e86db958f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -901,7 +901,10 @@ void D_DoomLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; - I_SetCursor(TexMan["cursor"]); + if (gameinfo.CursorPic.IsEmpty() || !I_SetCursor(TexMan[gameinfo.CursorPic])) + { + I_SetCursor(TexMan["cursor"]); + } for (;;) { diff --git a/src/gi.cpp b/src/gi.cpp index 4cd1284c9..712638ace 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -274,6 +274,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRING(backpacktype, "backpacktype") GAMEINFOKEY_STRING(statusbar, "statusbar") GAMEINFOKEY_STRING(intermissionMusic, "intermissionMusic") + GAMEINFOKEY_STRING(CursorPic, "CursorPic") GAMEINFOKEY_BOOL(noloopfinalemusic, "noloopfinalemusic") GAMEINFOKEY_BOOL(drawreadthis, "drawreadthis") GAMEINFOKEY_BOOL(intermissioncounter, "intermissioncounter") diff --git a/src/gi.h b/src/gi.h index 483b4a1b0..cb26a058d 100644 --- a/src/gi.h +++ b/src/gi.h @@ -106,6 +106,7 @@ struct gameinfo_t FString backpacktype; FString statusbar; FString intermissionMusic; + FString CursorPic; DWORD dimcolor; float dimamount; int definventorymaxamount; diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index a4d12b7c7..393dbc2ea 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -55,6 +55,7 @@ gameinfo playerclasses = "DoomPlayer" pausesign = "M_PAUSE" gibfactor = 1 + cursorpic = "doomcurs" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 1596f45d8..17cd942f2 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -55,6 +55,7 @@ gameinfo nightmarefast = true pausesign = "PAUSED" gibfactor = 0.5 + cursorpic = "herecurs" } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index af6528699..29dddd8d7 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -53,6 +53,7 @@ gameinfo nightmarefast = true pausesign = "PAUSED" gibfactor = 0.5 + cursorpic = "hexncurs" } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index 8595c56c9..e65b7a9a7 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -55,6 +55,7 @@ gameinfo PlayerClasses = "StrifePlayer" pausesign = "PAUSED" gibfactor = 0.5 + cursorpic = "strfcurs" } skill baby From 79f26d9e5c29aa71e2c4659ff79897d545c05a17 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 26 Sep 2010 05:33:46 +0000 Subject: [PATCH 067/815] - I do believe I forgot to add some things. SVN r2853 (trunk) --- wadsrc/static/graphics/doomcurs.png | Bin 0 -> 741 bytes wadsrc/static/graphics/herecurs.png | Bin 0 -> 1298 bytes wadsrc/static/graphics/hexncurs.png | Bin 0 -> 403 bytes wadsrc/static/graphics/strfcurs.png | Bin 0 -> 302 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 wadsrc/static/graphics/doomcurs.png create mode 100644 wadsrc/static/graphics/herecurs.png create mode 100644 wadsrc/static/graphics/hexncurs.png create mode 100644 wadsrc/static/graphics/strfcurs.png diff --git a/wadsrc/static/graphics/doomcurs.png b/wadsrc/static/graphics/doomcurs.png new file mode 100644 index 0000000000000000000000000000000000000000..1f4b73dcdee3b5a720d88ce9d9c4a5e79fc2ae89 GIT binary patch literal 741 zcmVJy${^Eg}*~JsxmZEoMz9q;Nu9005D7 zT+_FkTu3MY006~}i%C7Uuu9g4*>$jVTa95820MUPDqyPX-Gak%!Tz_Uw(|=~3 z7!0pH9;7HJi2wk~VK}KX7_Kt_vsW#=LI4a5482z^VE_QdNJt_W43|P8xJ@Yksc_uD zr0$<)#9TACJw3$$0AWZoyiF*Ga7e|Zj6y;};Gcv0jEs5=46*qk>G_IpZZs^6tztVF zr_Z~V!BCv1B?XaTbFNY*oMp@+EV3vc*`9IGYz5?+85>ht?Abhdx{F@OL8Z%yc8QN) zmN_{j;<`p8c8S3bE1Fm2_ICmYN$hP=FW$V(P5y2^p9`mBu#=rdO$^LZS9ReNBUV9# z!3$MHkXVq3nyq;D(I0O#nSqW`<78NsDcYEhJ-~+U)mWQY0Sq9<*rNS3fW1PcXs`og z3Y}$N?A{1epJ=SsU<$-321<)iq^>VJH8cvyCs{xP$^ X_%wMh}x1EKq3x*e}7q7SxZYx9UUDoF)&b3P+(zTC?_cT#!%#&8X6iKvvD7&Wf-Mn z7^r3$sb?CEO9_um3Zh*UpIH=DC;*yL4VzODOCthCAp%rXRbDn5G#d<2Dicd66E7JI z8WUE+P#+KlT3}%FuP;|G6}@~E zGBr3V77D|F7|x9<8xIIN9S&GN8KtGAWlAHsbsK|&gKAGHe0+RXSXy*+bg^z9ZEbC0 zMImTQCUjLVR#sL{PEK!AF-%QOMn*DwTViMbE zH8CwGCMA7fIx7zVj70$~Dk(TNHJ3{Q#KgogE-Fz{Qiw$eI6FSKbRj=PM;IFzCn+m& zJq~qPG^oiFwKb>T`U58f`zMS8x$89OfeHPGc$E< zWQR{4Y;t!*NK0o_L?tC9ymk~d7y+|!7-l>hR!KiyCICAa02LJ#5fKp&4-cbP3@;}j zt!o^qRL5cf0004WQchC0KGXqvnAP#Va^fA^f zhdm3=k$d#`<5Z6~90xdTVM%peT|JFHH(+iXabci#D;RsgVM|)C!7zG(3zy51q`?{7IWvVs?ctiO|SR9F@8?LNw-8BJ`Nuls(Ft$%z|7z$M z5~rlS>E^!LGm)=1)C{|B5x;>fH#hkZwvZ)e&9YhI1gBmzd3pCvLr~UJ zF)uOc`Ke}8e(t;F!KLtG6qJfYqT4|;)LHhTz=`VGu(wahknbowp~ zUzuL?MJ5SH1l4)fiVTH9rTQftgJ*E7=cS(j!wl+bz-hg}NN;Ix>F@1RD1gn}t|?c9 z8qgA2(#62d-TSJLbOceW5`ZoF84N8D{zx#WhZ|Lh5Z{Lmp&@=)6C_M_k17=!}#GGJ30RR9107*qo IM6N<$f=MaoTL1t6 literal 0 HcmV?d00001 diff --git a/wadsrc/static/graphics/hexncurs.png b/wadsrc/static/graphics/hexncurs.png new file mode 100644 index 0000000000000000000000000000000000000000..6551bf1b1b53b9d741d88046f77c7f534a60a645 GIT binary patch literal 403 zcmV;E0c`$>P)#W~I-r9oM92%&PkYg9$SN1TXx6OZr$T{HB@I x1_KCm0tL^TnnX;8_ojNGnNGT1v5B2yO9RuaHJPGCIM-7AZ9R_ zv#|_FE%9`545^6I?cKX5|56^#BWmzKdtm?ANBbkECWez(wDe&;->2f)LUmF|q`c=g5ey#a>vmLLz z_`Fq4#_`te@ZTR;e%>*#FK5{wtNxj=lOjev%h7?Vh=f`j1ZFZKbe%-r)A$#H_*?dj_0vd$@?2>@4AbL9X4 literal 0 HcmV?d00001 From cf9792ed531e08e4fa54a2e646556c351fc3dc94 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Sep 2010 06:53:40 +0000 Subject: [PATCH 068/815] - fixed: The order of items in the main menu was wrong. Doom is supposed to have 'Options' in second place. Many vanilla-compatible mods with special one-patch menus would not work correctly due to this. Fortunately the only mod I could find that relied on ZDoom's order was Action Doom 2, which as an IWAD can easily be handled by a simple configuration option. - added 'else' blocks to MENUDEF parser. SVN r2854 (trunk) --- src/d_iwad.cpp | 2 +- src/gi.cpp | 1 + src/gi.h | 1 + src/menu/menudef.cpp | 9 +++++++-- wadsrc/static/mapinfo/urbanbrawl.txt | 7 +++++++ wadsrc/static/menudef.txt | 15 ++++++++++++--- 6 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 wadsrc/static/mapinfo/urbanbrawl.txt diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index ae208e25a..119d85b74 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -80,7 +80,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "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", GI_NOTEXTCOLOR }, - { "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, + { "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/urbanbrawl.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/gi.cpp b/src/gi.cpp index 712638ace..c8ca25b89 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -277,6 +277,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRING(CursorPic, "CursorPic") GAMEINFOKEY_BOOL(noloopfinalemusic, "noloopfinalemusic") GAMEINFOKEY_BOOL(drawreadthis, "drawreadthis") + GAMEINFOKEY_BOOL(swapmenu, "swapmenu") GAMEINFOKEY_BOOL(intermissioncounter, "intermissioncounter") GAMEINFOKEY_BOOL(nightmarefast, "nightmarefast") GAMEINFOKEY_COLOR(dimcolor, "dimcolor") diff --git a/src/gi.h b/src/gi.h index cb26a058d..40ddfb9ad 100644 --- a/src/gi.h +++ b/src/gi.h @@ -76,6 +76,7 @@ struct gameinfo_t bool noloopfinalemusic; bool intermissioncounter; bool nightmarefast; + bool swapmenu; TArray creditPages; TArray finalePages; TArray infoPages; diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 66145bff6..d30e35ee7 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -147,6 +147,7 @@ static bool CheckSkipOptionBlock(FScanner &sc) { sc.MustGetString(); if (sc.Compare("ReadThis")) filter |= gameinfo.drawreadthis; + else if (sc.Compare("Swapmenu")) filter |= gameinfo.swapmenu; else if (sc.Compare("Windows")) { #ifdef _WIN32 @@ -171,7 +172,7 @@ static bool CheckSkipOptionBlock(FScanner &sc) if (!filter) { SkipSubBlock(sc); - return true; + return !sc.CheckString("else"); } return false; } @@ -188,7 +189,11 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) while (!sc.CheckString("}")) { sc.MustGetString(); - if (sc.Compare("ifgame")) + if (sc.Compare("else")) + { + SkipSubBlock(sc); + } + else if (sc.Compare("ifgame")) { if (!CheckSkipGameBlock(sc)) { diff --git a/wadsrc/static/mapinfo/urbanbrawl.txt b/wadsrc/static/mapinfo/urbanbrawl.txt new file mode 100644 index 000000000..094975bf5 --- /dev/null +++ b/wadsrc/static/mapinfo/urbanbrawl.txt @@ -0,0 +1,7 @@ +include "mapinfo/doom2.txt" + +gameinfo +{ + swapmenu = true +} + diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index edbeecb6a..c3f9ef144 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -69,9 +69,18 @@ LISTMENU "MainMenu" 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(SwapMenu) + { + PatchItem "M_LOADG", "l", "LoadGameMenu" + PatchItem "M_SAVEG", "s", "SaveGameMenu" + PatchItem "M_OPTION","o", "OptionsMenu" + } + else + { + PatchItem "M_OPTION","o", "OptionsMenu" + PatchItem "M_LOADG", "l", "LoadGameMenu" + PatchItem "M_SAVEG", "s", "SaveGameMenu" + } ifOption(ReadThis) { PatchItem "M_RDTHIS","r", "ReadThisMenu" From 9abaaa17857b51d25d3ad53ea23b582af06a6f39 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Sep 2010 07:46:34 +0000 Subject: [PATCH 069/815] - added a default cursor for Chex Quest. - set 'cursor' as default for Action Doom 2. Doom's bunny is probably not the best thing here... - made cursor user-settable in the menu. SVN r2855 (trunk) --- src/d_main.cpp | 22 ++++++++++++--- src/win32/i_system.cpp | 39 +++++++++++++++------------ wadsrc/static/graphics/chexcurs.png | Bin 0 -> 358 bytes wadsrc/static/mapinfo/chex.txt | 1 + wadsrc/static/mapinfo/urbanbrawl.txt | 1 + wadsrc/static/menudef.txt | 13 +++++++++ 6 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 wadsrc/static/graphics/chexcurs.png diff --git a/src/d_main.cpp b/src/d_main.cpp index e86db958f..fc282fa5f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -184,6 +184,23 @@ CUSTOM_CVAR (Int, fraglimit, 0, CVAR_SERVERINFO) CVAR (Float, timelimit, 0.f, CVAR_SERVERINFO); CVAR (Int, wipetype, 1, CVAR_ARCHIVE); CVAR (Int, snd_drawoutput, 0, 0); +CUSTOM_CVAR (String, vid_cursor, "None", CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + bool res = false; + + if (!stricmp(self, "None" ) && gameinfo.CursorPic.IsNotEmpty()) + { + res = I_SetCursor(TexMan[gameinfo.CursorPic]); + } + else + { + res = I_SetCursor(TexMan[self]); + } + if (!res) + { + I_SetCursor(TexMan["cursor"]); + } +} bool DrawFSHUD; // [RH] Draw fullscreen HUD? TArray allwads; @@ -901,10 +918,7 @@ void D_DoomLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; - if (gameinfo.CursorPic.IsEmpty() || !I_SetCursor(TexMan[gameinfo.CursorPic])) - { - I_SetCursor(TexMan["cursor"]); - } + vid_cursor.Callback(); for (;;) { diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index d0362b13f..095ef1ee5 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1186,28 +1186,33 @@ bool I_SetCursor(FTexture *cursorpic) { HCURSOR cursor; - // Must be no larger than 32x32. - if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) + if (cursorpic != NULL && cursorpic->UseType != FTexture::TEX_Null) { - return false; - } + // Must be no larger than 32x32. + if (cursorpic->GetWidth() > 32 || cursorpic->GetHeight() > 32) + { + return false; + } - cursor = CreateAlphaCursor(cursorpic); - if (cursor == NULL) - { - cursor = CreateCompatibleCursor(cursorpic); + cursor = CreateAlphaCursor(cursorpic); + if (cursor == NULL) + { + cursor = CreateCompatibleCursor(cursorpic); + } + if (cursor == NULL) + { + return false; + } + // Replace the existing cursor with the new one. + DestroyCustomCursor(); + CustomCursor = cursor; + atterm(DestroyCustomCursor); } - if (cursor == NULL) + else { - return false; + DestroyCustomCursor(); + cursor = LoadCursor(NULL, IDC_ARROW); } - // 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; } diff --git a/wadsrc/static/graphics/chexcurs.png b/wadsrc/static/graphics/chexcurs.png new file mode 100644 index 0000000000000000000000000000000000000000..658c71f7c2fac276894e886db4cd9ad653982a49 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~&3?$8t&jboc*-@Pdd89W-(2V@Sm<-w7M@4mk+8toLmGuTaBS?<~F4-@v}%Np_Inhp7q=t);v>58TV0 zpj7;P?)=k(qESJ_Z-XuiaRrNzHIlq?oaxuAT3 zpK~_fp{$kM3o;f}a!yP6eQIBZQqA#cn+!xlDt)#^`LQdcuK60X)jRU|lS2;#W zUC_hz;dr-~A9sg&Z~23YFIivLe!W>1I$3tH8AtWYQ-2Pg`0iDAy4tk9VY}p4zN+u7T-Mk%(pLJwpi)Uzq7>uFY`i+Qt^9N8n%G^>*?y}vd$@?2>{JH BmAwD} literal 0 HcmV?d00001 diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 2f2d77bf7..f75838b7f 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -55,6 +55,7 @@ gameinfo playerclasses = "ChexPlayer" pausesign = "M_PAUSE" gibfactor = 1 + cursorpic = "chexcurs" } skill baby diff --git a/wadsrc/static/mapinfo/urbanbrawl.txt b/wadsrc/static/mapinfo/urbanbrawl.txt index 094975bf5..c21e8b675 100644 --- a/wadsrc/static/mapinfo/urbanbrawl.txt +++ b/wadsrc/static/mapinfo/urbanbrawl.txt @@ -3,5 +3,6 @@ include "mapinfo/doom2.txt" gameinfo { swapmenu = true + cursorpic = "cursor" } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index c3f9ef144..5c6873d14 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -510,6 +510,18 @@ OptionValue "MenuMouse" 2, "Touchscreen-like" } +OptionString "Cursors" +{ + "None", "Default" + "cursor", "Simple arrow" + "doomcurs", "Doom" + "herecurs", "Heretic" + "hexncurs", "Hexen" + "strfcurs", "Strife" + "chexcurs", "Chex" + "-", "System cursor" +} + OptionMenu "MouseOptions" { Title "MOUSE OPTIONS" @@ -518,6 +530,7 @@ OptionMenu "MouseOptions" { Option "Enable mouse in menus", "m_use_mouse", "MenuMouse", "use_mouse" Option "Show back button", "m_show_backbutton", "Corners", "use_mouse" + Option "Cursor", "vid_cursor", "Cursors" } StaticText "" Slider "Overall sensitivity", "mouse_sensitivity", 0.5, 2.5, 0.1 From add5518a04b79bb5894ccd74113988e6d7a84643 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 27 Sep 2010 05:49:56 +0000 Subject: [PATCH 070/815] - added some constant definitions for action specials to DECORATE header. SVN r2856 (trunk) --- wadsrc/static/actors/constants.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index d6fdd42de..0b432db91 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -170,6 +170,34 @@ const float ATTN_NORM = 1; const float ATTN_IDLE = 1.001; const float ATTN_STATIC = 3; +// For SetPlayerProprty action special +Const Int PROP_FROZEN = 0; +Const Int PROP_NOTARGET = 1; +Const Int PROP_INSTANTWEAPONSWITCH = 2; +Const Int PROP_FLY = 3; +Const Int PROP_TOTALLYFROZEN = 4; +Const Int PROP_INVULNERABILITY = 5; // (Deprecated) +Const Int PROP_STRENGTH = 6; // (Deprecated) +Const Int PROP_INVISIBILITY = 7; // (Deprecated) +Const Int PROP_RADIATIONSUIT = 8; // (Deprecated) +Const Int PROP_ALLMAP = 9; // (Deprecated) +Const Int PROP_INFRARED = 10; // (Deprecated) +Const Int PROP_WEAPONLEVEL2 = 11; // (Deprecated) +Const Int PROP_FLIGHT = 12; // (Deprecated) +Const Int PROP_SPEED = 15; // (Deprecated) +Const Int PROP_BUDDHA = 16; + +// Line_SetBlocking +Const Int BLOCKF_CREATURES = 1; +Const Int BLOCKF_MONSTERS = 2; +Const Int BLOCKF_PLAYERS = 4; +Const Int BLOCKF_FLOATERS = 8; +Const Int BLOCKF_PROJECTILES = 16; +Const Int BLOCKF_EVERYTHING = 32; +Const Int BLOCKF_RAILING = 64; +Const Int BLOCKF_USE = 128; + + // This is only here to provide one global variable for testing. native int testglobalvar; From 77709f1847a26be1a0fa7503b336e2296c690a48 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 27 Sep 2010 22:36:47 +0000 Subject: [PATCH 071/815] - fixed: The loadgame menu responder did not check for an empty list of savegames. SVN r2857 (trunk) --- src/menu/loadsavemenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index be59acdb6..0b3ccf1aa 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -1056,7 +1056,7 @@ bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) { return true; } - if (Selected == -1) + if (Selected == -1 || SaveGames.Size() == 0) { return false; } From 2d5755a80ed3070c878e0cf4e319999257e7ed8c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 27 Sep 2010 22:42:35 +0000 Subject: [PATCH 072/815] - fix GCC warning. SVN r2858 (trunk) --- src/sound/music_hmi_midiout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index e22149f8d..24bd9e041 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -423,7 +423,7 @@ void HMISong::CheckCaps(int tech) { Tracks[i].Enabled = false; // Track designations are stored in a 0-terminated array. - for (int j = 0; j < countof(Tracks[i].Designation) && Tracks[i].Designation[j] != 0; ++j) + for (unsigned int j = 0; j < countof(Tracks[i].Designation) && Tracks[i].Designation[j] != 0; ++j) { if (Tracks[i].Designation[j] == tech) { From 5a3b3631c328a5cbeff9a960d237b6d5b87fbec4 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 28 Sep 2010 03:58:41 +0000 Subject: [PATCH 073/815] - Added XMIDI support (including subsongs). - Moved unaligned accessors into m_swap.h. SVN r2859 (trunk) --- src/CMakeLists.txt | 1 + src/m_swap.h | 42 ++ src/sound/i_music.cpp | 14 + src/sound/i_musicinterns.h | 84 +++- src/sound/music_hmi_midiout.cpp | 46 +- src/sound/music_midistream.cpp | 33 ++ src/sound/music_smf_midiout.cpp | 3 +- src/sound/music_xmi_midiout.cpp | 746 +++++++++++++++++++++++++++++++ zdoom.vcproj | 754 ++++++++++++++++---------------- 9 files changed, 1295 insertions(+), 428 deletions(-) create mode 100644 src/sound/music_xmi_midiout.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 672267e54..64250ca64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -828,6 +828,7 @@ add_executable( zdoom WIN32 sound/music_mus_midiout.cpp sound/music_smf_midiout.cpp sound/music_hmi_midiout.cpp + sound/music_xmi_midiout.cpp sound/music_midistream.cpp sound/music_midi_base.cpp sound/music_midi_timidity.cpp diff --git a/src/m_swap.h b/src/m_swap.h index 473e4b3c7..6e0bc88cc 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -174,4 +174,46 @@ inline int BigLong (int x) #endif // __BIG_ENDIAN__ #endif // __APPLE__ + + +// Data accessors, since some data is highly likely to be unaligned. +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} +inline int GetBigInt(const unsigned char *foo) +{ + return BigLong(GetInt(foo)); +} +#else +inline int GetShort(const unsigned char *foo) +{ + return short(foo[0] | (foo[1] << 8)); +} +inline int GetInt(const unsigned char *foo) +{ + return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); +} +inline int GetBigInt(const unsigned char *foo) +{ + return int((foo[0] << 24) | (foo[1] << 16) | (foo[2] << 8) | foo[3]); +} +#endif +#ifdef __BIG_ENDIAN__ +inline int GetNativeInt(const unsigned char *foo) +{ + return GetBigInt(foo); +} +#else +inline int GetNativeInt(const unsigned char *foo) +{ + return GetInt(foo); +} +#endif + #endif // __M_SWAP_H__ diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 76e33ff83..ab8cd8f66 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -89,6 +89,7 @@ enum EMIDIType MIDI_NOTMIDI, MIDI_MIDI, MIDI_HMI, + MIDI_XMI, MIDI_MUS }; @@ -344,6 +345,10 @@ static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccach { return new HMISong(file, musiccache, len, devtype); } + else if (miditype == MIDI_XMI) + { + return new XMISong(file, musiccache, len, devtype); + } return NULL; } @@ -470,6 +475,15 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { miditype = MIDI_HMI; } + // Check for XMI format + else + if ((id[0] == MAKE_ID('F','O','R','M') && + id[2] == MAKE_ID('X','D','I','R')) || + ((id[0] == MAKE_ID('C','A','T',' ') || id[0] == MAKE_ID('F','O','R','M')) && + id[2] == MAKE_ID('X','M','I','D'))) + { + miditype = MIDI_XMI; + } // Check for MIDI format else if (id[0] == MAKE_ID('M','T','h','d')) { diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 32acaec48..6912cdb66 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -357,6 +357,7 @@ public: bool IsPlaying(); bool IsMIDI() const; bool IsValid() const; + bool SetSubsong(int subsong); void Update(); FString GetStats(); void FluidSettingInt(const char *setting, int value); @@ -380,6 +381,7 @@ protected: virtual void DoRestart() = 0; virtual bool CheckDone() = 0; virtual void Precache(); + virtual bool SetMIDISubsong(int subsong); virtual DWORD *MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) = 0; enum @@ -488,6 +490,27 @@ protected: // HMI file played with a MIDI stream --------------------------------------- +struct AutoNoteOff +{ + DWORD Delay; + BYTE Channel, Key; +}; +// Sorry, std::priority_queue, but I want to be able to modify the contents of the heap. +class NoteOffQueue : public TArray +{ +public: + void AddNoteOff(DWORD delay, BYTE channel, BYTE key); + void AdvanceTime(DWORD time); + bool Pop(AutoNoteOff &item); + +protected: + void Heapify(); + + unsigned int Parent(unsigned int i) { return (i + 1u) / 2u - 1u; } + unsigned int Left(unsigned int i) { return (i + 1u) * 2u - 1u; } + unsigned int Right(unsigned int i) { return (i + 1u) * 2u; } +}; + class HMISong : public MIDIStreamer { public: @@ -519,27 +542,6 @@ protected: static DWORD ReadVarLenHMI(TrackInfo *); static DWORD ReadVarLenHMP(TrackInfo *); - struct AutoNoteOff - { - DWORD Delay; - BYTE Channel, Key; - }; - // Sorry, std::priority_queue, but I want to be able to modify the contents of the heap. - class NoteOffQueue : public TArray - { - public: - void AddNoteOff(DWORD delay, BYTE channel, BYTE key); - void AdvanceTime(DWORD time); - bool Pop(AutoNoteOff &item); - - protected: - void Heapify(); - - unsigned int Parent(unsigned int i) { return (i + 1u) / 2u - 1u; } - unsigned int Left(unsigned int i) { return (i + 1u) * 2u - 1u; } - unsigned int Right(unsigned int i) { return (i + 1u) * 2u; } - }; - BYTE *MusHeader; int SongLen; int NumTracks; @@ -550,6 +552,46 @@ protected: NoteOffQueue NoteOffs; }; +// XMI file played with a MIDI stream --------------------------------------- + +class XMISong : public MIDIStreamer +{ +public: + XMISong(FILE *file, BYTE *musiccache, int length, EMIDIDevice type); + ~XMISong(); + + MusInfo *GetOPLDumper(const char *filename); + MusInfo *GetWaveDumper(const char *filename, int rate); + +protected: + struct TrackInfo; + enum EventSource { EVENT_None, EVENT_Real, EVENT_Fake }; + + XMISong(const XMISong *original, const char *filename, EMIDIDevice type); // file dump constructor + + int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const; + void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const; + bool SetMIDISubsong(int subsong); + void DoInitialSetup(); + void DoRestart(); + bool CheckDone(); + DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); + void AdvanceSong(DWORD time); + + void ProcessInitialMetaEvents(); + DWORD *SendCommand (DWORD *event, EventSource track, DWORD delay); + EventSource FindNextDue(); + void SetTempo(int new_tempo); + + BYTE *MusHeader; + int SongLen; // length of the entire file + int NumSongs; + TrackInfo *Songs; + TrackInfo *CurrSong; + NoteOffQueue NoteOffs; + EventSource EventDue; +}; + // Anything supported by FMOD out of the box -------------------------------- class StreamSong : public MusInfo diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 24bd9e041..465a20b1b 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -84,28 +84,6 @@ #define HMI_DEV_OPL3 0xA009 // SoundBlaster 16, Microsoft Sound System, Pro Audio Spectrum 16 #define HMI_DEV_GUS 0xA00A // Gravis UltraSound, Gravis UltraSound Max/Ace - -// Data accessors, since this data is highly likely to be unaligned. -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) -inline int GetShort(const BYTE *foo) -{ - return *(const short *)foo; -} -inline int GetInt(const BYTE *foo) -{ - return *(const int *)foo; -} -#else -inline int GetShort(const BYTE *foo) -{ - return short(foo[0] | (foo[1] << 8)); -} -inline int GetInt(const BYTE *foo) -{ - return int(foo[0] | (foo[1] << 8) | (foo[2] << 16) | (foo[3] << 24)); -} -#endif - // TYPES ------------------------------------------------------------------- struct HMISong::TrackInfo @@ -567,7 +545,7 @@ DWORD *HMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // // HMISong :: AdvanceTracks // -// Advaces time for all tracks by the specified amount. +// Advances time for all tracks by the specified amount. // //========================================================================== @@ -868,11 +846,11 @@ DWORD HMISong::TrackInfo::ReadVarLenHMP() //========================================================================== // -// HMISong :: NoteOffQueue :: AddNoteOff +// NoteOffQueue :: AddNoteOff // //========================================================================== -void HMISong::NoteOffQueue::AddNoteOff(DWORD delay, BYTE channel, BYTE key) +void NoteOffQueue::AddNoteOff(DWORD delay, BYTE channel, BYTE key) { unsigned int i = Reserve(1); while (i > 0 && (*this)[Parent(i)].Delay > delay) @@ -887,11 +865,11 @@ void HMISong::NoteOffQueue::AddNoteOff(DWORD delay, BYTE channel, BYTE key) //========================================================================== // -// HMISong :: NoteOffQueue :: Pop +// NoteOffQueue :: Pop // //========================================================================== -bool HMISong::NoteOffQueue::Pop(AutoNoteOff &item) +bool NoteOffQueue::Pop(AutoNoteOff &item) { item = (*this)[0]; if (TArray::Pop((*this)[0])) @@ -904,11 +882,11 @@ bool HMISong::NoteOffQueue::Pop(AutoNoteOff &item) //========================================================================== // -// HMISong :: NoteOffQueue :: AdvanceTime +// NoteOffQueue :: AdvanceTime // //========================================================================== -void HMISong::NoteOffQueue::AdvanceTime(DWORD time) +void NoteOffQueue::AdvanceTime(DWORD time) { // Because the time is decreasing by the same amount for every entry, // the heap property is maintained. @@ -921,11 +899,11 @@ void HMISong::NoteOffQueue::AdvanceTime(DWORD time) //========================================================================== // -// HMISong :: NoteOffQueue :: Heapify +// NoteOffQueue :: Heapify // //========================================================================== -void HMISong::NoteOffQueue::Heapify() +void NoteOffQueue::Heapify() { unsigned int i = 0; for (;;) @@ -965,10 +943,16 @@ HMISong::TrackInfo *HMISong::FindNextDue () DWORD best; int i; + // Give precedence to whichever track last had events taken from it. if (TrackDue != FakeTrack && !TrackDue->Finished && TrackDue->Delay == 0) { return TrackDue; } + if (TrackDue == FakeTrack && NoteOffs.Size() != 0 && NoteOffs[0].Delay == 0) + { + FakeTrack->Delay = 0; + return FakeTrack; + } // Check regular tracks. track = NULL; diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 454ff0d3f..f30256155 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -259,6 +259,7 @@ void MIDIStreamer::Play(bool looping, int subsong) return; } + SetMIDISubsong(subsong); CheckCaps(MIDI->GetTechnology()); Precache(); IgnoreLoops = false; @@ -1100,6 +1101,38 @@ FString MIDIStreamer::GetStats() return MIDI->GetStats(); } +//========================================================================== +// +// MIDIStreamer :: SetSubsong +// +// Selects which subsong to play in an already-playing file. This is public. +// +//========================================================================== + +bool MIDIStreamer::SetSubsong(int subsong) +{ + if (SetMIDISubsong(subsong)) + { + Stop(); + Play(m_Looping, subsong); + return true; + } + return false; +} + +//========================================================================== +// +// MIDIStreamer :: SetMIDISubsong +// +// Selects which subsong to play. This is private. +// +//========================================================================== + +bool MIDIStreamer::SetMIDISubsong(int subsong) +{ + return subsong == 0; +} + //========================================================================== // // MIDIDevice stubs. diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 0f01be75b..64d3849b2 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -348,7 +348,7 @@ DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // // MIDISong2 :: AdvanceTracks // -// Advaces time for all tracks by the specified amount. +// Advances time for all tracks by the specified amount. // //========================================================================== @@ -726,6 +726,7 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue () DWORD best; int i; + // Give precedence to whichever track last had events taken from it. if (!TrackDue->Finished && TrackDue->Delay == 0) { return TrackDue; diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp new file mode 100644 index 000000000..26c8a3557 --- /dev/null +++ b/src/sound/music_xmi_midiout.cpp @@ -0,0 +1,746 @@ +/* +** music_xmi_midiout.cpp +** Code to let ZDoom play XMIDI music through the MIDI streaming API. +** +**--------------------------------------------------------------------------- +** Copyright 2010 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. +**--------------------------------------------------------------------------- +** +** This file also supports the Apogee Sound System's EMIDI files. That +** basically means you can play the Duke3D songs without any editing and +** have them sound right. +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_FOR_DEPTH 4 + +#define GET_DELAY (EventDue == EVENT_Real ? CurrSong->Delay : NoteOffs[0].Delay) + +// Used by SendCommand to check for unexpected end-of-track conditions. +#define CHECK_FINISHED \ + if (track->EventP >= track->EventLen) \ + { \ + track->Finished = true; \ + return events; \ + } + +// TYPES ------------------------------------------------------------------- + +struct LoopInfo +{ + size_t LoopBegin; + SBYTE LoopCount; + bool LoopFinished; +}; + +struct XMISong::TrackInfo +{ + const BYTE *EventChunk; + size_t EventLen; + size_t EventP; + + const BYTE *TimbreChunk; + size_t TimbreLen; + + DWORD Delay; + DWORD PlayedTime; + bool Finished; + + LoopInfo ForLoops[MAX_FOR_DEPTH]; + int ForDepth; + + DWORD ReadVarLen(); + DWORD ReadDelay(); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern char MIDI_EventLengths[7]; +extern char MIDI_CommonLengths[15]; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// XMISong Constructor +// +// Buffers the file and does some validation of the SMF header. +// +//========================================================================== + +XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) +: MIDIStreamer(type), MusHeader(0), Songs(0) +{ +#ifdef _WIN32 + if (ExitEvent == NULL) + { + return; + } +#endif + MusHeader = new BYTE[len]; + SongLen = len; + if (file != NULL) + { + if (fread(MusHeader, 1, len, file) != (size_t)len) + return; + } + else + { + memcpy(MusHeader, musiccache, len); + } + + // Find all the songs in this file. + NumSongs = FindXMIDforms(MusHeader, len, NULL); + if (NumSongs == 0) + { + return; + } + Songs = new TrackInfo[NumSongs]; + memset(Songs, 0, sizeof(*Songs) * NumSongs); + FindXMIDforms(MusHeader, len, Songs); + CurrSong = Songs; + DPrintf("XMI song count: %d\n", NumSongs); + + // The division is the number of pulses per quarter note (PPQN). + Division = 60; +} + +//========================================================================== +// +// XMISong Destructor +// +//========================================================================== + +XMISong::~XMISong () +{ + if (Songs != NULL) + { + delete[] Songs; + } + if (MusHeader != NULL) + { + delete[] MusHeader; + } +} + +//========================================================================== +// +// XMISong :: FindXMIDforms +// +// Find all FORM XMID chunks in this chunk. +// +//========================================================================== + +int XMISong::FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const +{ + int count = 0; + + for (int p = 0; p <= len - 12; ) + { + int chunktype = GetNativeInt(chunk + p); + int chunklen = GetBigInt(chunk + p + 4); + + if (chunktype == MAKE_ID('F','O','R','M')) + { + if (GetNativeInt(chunk + p + 8) == MAKE_ID('X','M','I','D')) + { + if (songs != NULL) + { + FoundXMID(chunk + p + 12, chunklen - 4, songs + count); + } + count++; + } + } + else if (chunktype == MAKE_ID('C','A','T',' ')) + { + // Recurse to handle CAT chunks. + count += FindXMIDforms(chunk + p + 12, chunklen - 4, songs + count); + } + // IFF chunks are padded to even byte boundaries to avoid + // unaligned reads on 68k processors. + p += 8 + chunklen + (chunklen & 1); + } + return count; +} + +//========================================================================== +// +// XMISong :: FoundXMID +// +// Records information about this XMID song. +// +//========================================================================== + +void XMISong::FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const +{ + for (int p = 0; p <= len - 8; ) + { + int chunktype = GetNativeInt(chunk + p); + int chunklen = GetBigInt(chunk + p + 4); + + if (chunktype == MAKE_ID('T','I','M','B')) + { + song->TimbreChunk = chunk + p + 8; + song->TimbreLen = chunklen; + } + else if (chunktype == MAKE_ID('E','V','N','T')) + { + song->EventChunk = chunk + p + 8; + song->EventLen = chunklen; + // EVNT must be the final chunk in the FORM. + break; + } + p += 8 + chunklen + (chunklen & 1); + } +} + +//========================================================================== +// +// XMISong :: SetMIDISubsong +// +// Selects which song in this file to play. +// +//========================================================================== + +bool XMISong::SetMIDISubsong(int subsong) +{ + if ((unsigned)subsong >= (unsigned)NumSongs) + { + return false; + } + CurrSong = &Songs[subsong]; + return true; +} + +//========================================================================== +// +// XMISong :: DoInitialSetup +// +// Sets the starting channel volumes. +// +//========================================================================== + +void XMISong::DoInitialSetup() +{ + for (int i = 0; i < 16; ++i) + { + ChannelVolumes[i] = 100; + } +} + +//========================================================================== +// +// XMISong :: DoRestart +// +// Rewinds the current song. +// +//========================================================================== + +void XMISong::DoRestart() +{ + CurrSong->EventP = 0; + CurrSong->Finished = false; + CurrSong->PlayedTime = 0; + CurrSong->ForDepth = 0; + NoteOffs.Clear(); + + ProcessInitialMetaEvents (); + + CurrSong->Delay = CurrSong->ReadDelay(); + EventDue = FindNextDue(); +} + +//========================================================================== +// +// XMISong :: CheckDone +// +//========================================================================== + +bool XMISong::CheckDone() +{ + return EventDue == EVENT_None; +} + +//========================================================================== +// +// XMISong :: MakeEvents +// +// Copies MIDI events from the SMF and puts them into a MIDI stream +// buffer. Returns the new position in the buffer. +// +//========================================================================== + +DWORD *XMISong::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) +{ + DWORD *start_events; + DWORD tot_time = 0; + DWORD time = 0; + DWORD delay; + + start_events = events; + while (EventDue != EVENT_None && events < max_event_p && tot_time <= max_time) + { + // It's possible that this tick may be nothing but meta-events and + // not generate any real events. Repeat this until we actually + // get some output so we don't send an empty buffer to the MIDI + // device. + do + { + delay = GET_DELAY; + time += delay; + // Advance time for all tracks by the amount needed for the one up next. + tot_time += delay * Tempo / Division; + AdvanceSong(delay); + // Play all events for this tick. + do + { + DWORD *new_events = SendCommand(events, EventDue, time); + EventDue = FindNextDue(); + if (new_events != events) + { + time = 0; + } + events = new_events; + } + while (EventDue != EVENT_None && GET_DELAY == 0 && events < max_event_p); + } + while (start_events == events && EventDue != EVENT_None); + time = 0; + } + return events; +} + +//========================================================================== +// +// XMISong :: AdvanceSong +// +// Advances time for the current song by the specified amount. +// +//========================================================================== + +void XMISong::AdvanceSong(DWORD time) +{ + if (time != 0) + { + if (!CurrSong->Finished) + { + CurrSong->Delay -= time; + CurrSong->PlayedTime += time; + } + NoteOffs.AdvanceTime(time); + } +} + +//========================================================================== +// +// XMISong :: SendCommand +// +// Places a single MIDIEVENT in the event buffer. +// +//========================================================================== + +DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay) +{ + DWORD len; + BYTE event, data1 = 0, data2 = 0; + + if (due == EVENT_Fake) + { + AutoNoteOff off; + NoteOffs.Pop(off); + events[0] = delay; + events[1] = 0; + events[2] = MIDI_NOTEON | off.Channel | (off.Key << 8); + return events + 3; + } + + TrackInfo *track = CurrSong; + + CHECK_FINISHED + event = track->EventChunk[track->EventP++]; + CHECK_FINISHED + + if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND) + { + // Normal short message + if ((event & 0xF0) == 0xF0) + { + if (MIDI_CommonLengths[event & 15] > 0) + { + data1 = track->EventChunk[track->EventP++]; + if (MIDI_CommonLengths[event & 15] > 1) + { + data2 = track->EventChunk[track->EventP++]; + } + } + } + else + { + data1 = track->EventChunk[track->EventP++]; + } + + CHECK_FINISHED + + if (MIDI_EventLengths[(event&0x70)>>4] == 2) + { + data2 = track->EventChunk[track->EventP++]; + } + + if ((event & 0x70) == (MIDI_CTRLCHANGE & 0x70)) + { + switch (data1) + { + case 7: // Channel volume + data2 = VolumeControllerChange(event & 15, data2); + break; + + case 110: // XMI channel lock + case 111: // XMI channel lock protect + case 112: // XMI voice protect + case 113: // XMI timbre protect + case 115: // XMI indirect controller prefix + case 118: // XMI clear beat/bar count + case 119: // XMI callback trigger + case 120: + event = MIDI_META; // none of these are relevant to us. + break; + + case 114: // XMI patch bank select + data1 = 0; // Turn this into a standard MIDI bank select controller. + break; + + case 116: // XMI for loop controller + if (!IgnoreLoops && track->ForDepth < MAX_FOR_DEPTH) + { + track->ForLoops[track->ForDepth].LoopBegin = track->EventP; + track->ForLoops[track->ForDepth].LoopCount = data2; + track->ForLoops[track->ForDepth].LoopFinished = track->Finished; + } + track->ForDepth++; + event = MIDI_META; + break; + + case 117: // XMI next loop controller + if (track->ForDepth > 0) + { + int depth = track->ForDepth - 1; + if (depth < MAX_FOR_DEPTH) + { + if (data2 < 64 || (track->ForLoops[depth].LoopCount == 0 && !m_Looping)) + { // throw away this loop. + track->ForLoops[depth].LoopCount = 1; + } + // A loop count of 0 loops forever. + if (track->ForLoops[depth].LoopCount == 0 || --track->ForLoops[depth].LoopCount > 0) + { + track->EventP = track->ForLoops[depth].LoopBegin; + track->Finished = track->ForLoops[depth].LoopFinished; + } + else + { // done with this loop + track->ForDepth = depth; + } + } + else + { // ignore any loops deeper than the max depth + track->ForDepth = depth; + } + } + event = MIDI_META; + break; + } + } + events[0] = delay; + events[1] = 0; + if (event != MIDI_META) + { + events[2] = event | (data1<<8) | (data2<<16); + } + else + { + events[2] = MEVT_NOP; + } + events += 3; + + + if ((event & 0x70) == (MIDI_NOTEON & 0x70)) + { // XMI note on events include the time until an implied note off event. + NoteOffs.AddNoteOff(track->ReadVarLen(), event & 0x0F, data1); + } + } + else + { + // Skip SysEx events just because I don't want to bother with them. + // The old MIDI player ignored them too, so this won't break + // anything that played before. + if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) + { + len = track->ReadVarLen (); + track->EventP += len; + } + else if (event == MIDI_META) + { + // It's a meta-event + event = track->EventChunk[track->EventP++]; + CHECK_FINISHED + len = track->ReadVarLen (); + CHECK_FINISHED + + if (track->EventP + len <= track->EventLen) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + Tempo = + (track->EventChunk[track->EventP+0]<<16) | + (track->EventChunk[track->EventP+1]<<8) | + (track->EventChunk[track->EventP+2]); + events[0] = delay; + events[1] = 0; + events[2] = (MEVT_TEMPO << 24) | Tempo; + events += 3; + break; + } + track->EventP += len; + if (track->EventP == track->EventLen) + { + track->Finished = true; + } + } + else + { + track->Finished = true; + } + } + } + if (!track->Finished) + { + track->Delay = track->ReadDelay(); + } + return events; +} + +//========================================================================== +// +// XMISong :: ProcessInitialMetaEvents +// +// Handle all the meta events at the start of the current song. +// +//========================================================================== + +void XMISong::ProcessInitialMetaEvents () +{ + TrackInfo *track = CurrSong; + BYTE event; + DWORD len; + + while (!track->Finished && + track->EventP < track->EventLen - 3 && + track->EventChunk[track->EventP] == 0xFF) + { + event = track->EventChunk[track->EventP+1]; + track->EventP += 2; + len = track->ReadVarLen(); + if (track->EventP + len <= track->EventLen) + { + switch (event) + { + case MIDI_META_EOT: + track->Finished = true; + break; + + case MIDI_META_TEMPO: + SetTempo( + (track->EventChunk[track->EventP+0]<<16) | + (track->EventChunk[track->EventP+1]<<8) | + (track->EventChunk[track->EventP+2]) + ); + break; + } + } + track->EventP += len; + } + if (track->EventP >= track->EventLen - 1) + { + track->Finished = true; + } +} + +//========================================================================== +// +// XMISong :: TrackInfo :: ReadVarLen +// +// Reads a variable length SMF number. +// +//========================================================================== + +DWORD XMISong::TrackInfo::ReadVarLen() +{ + DWORD time = 0, t = 0x80; + + while ((t & 0x80) && EventP < EventLen) + { + t = EventChunk[EventP++]; + time = (time << 7) | (t & 127); + } + return time; +} + +//========================================================================== +// +// XMISong :: TrackInfo :: ReadDelay +// +// XMI does not use variable length numbers for delays. Instead, it uses +// runs of bytes with the high bit clear. +// +//========================================================================== + +DWORD XMISong::TrackInfo::ReadDelay() +{ + DWORD time = 0, t; + + while (EventP < EventLen && !((t = EventChunk[EventP]) & 0x80)) + { + time += t; + EventP++; + } + return time; +} + +//========================================================================== +// +// XMISong :: FindNextDue +// +// Decides whether the next event should come from the actual stong or +// from the auto note offs. +// +//========================================================================== + +XMISong::EventSource XMISong::FindNextDue() +{ + // Are there still events available? + if (CurrSong->Finished && NoteOffs.Size() == 0) + { + return EVENT_None; + } + + // Which is due sooner? The current song or the note-offs? + DWORD real_delay = CurrSong->Finished ? 0xFFFFFFFF : CurrSong->Delay; + DWORD fake_delay = NoteOffs.Size() == 0 ? 0xFFFFFFFF : NoteOffs[0].Delay; + + return (fake_delay <= real_delay) ? EVENT_Fake : EVENT_Real; +} + + +//========================================================================== +// +// XMISong :: SetTempo +// +// Sets the tempo from a track's initial meta events. +// +//========================================================================== + +void XMISong::SetTempo(int new_tempo) +{ + if (0 == MIDI->SetTempo(new_tempo)) + { + Tempo = new_tempo; + } +} + +//========================================================================== +// +// XMISong :: GetOPLDumper +// +//========================================================================== + +MusInfo *XMISong::GetOPLDumper(const char *filename) +{ + return new XMISong(this, filename, MIDI_OPL); +} + +//========================================================================== +// +// XMISong :: GetWaveDumper +// +//========================================================================== + +MusInfo *XMISong::GetWaveDumper(const char *filename, int rate) +{ + return new XMISong(this, filename, MIDI_GUS); +} + +//========================================================================== +// +// XMISong File Dumping Constructor +// +//========================================================================== + +XMISong::XMISong(const XMISong *original, const char *filename, EMIDIDevice type) +: MIDIStreamer(filename, type) +{ + SongLen = original->SongLen; + MusHeader = new BYTE[original->SongLen]; + memcpy(MusHeader, original->MusHeader, original->SongLen); + NumSongs = original->NumSongs; + Tempo = InitialTempo = original->InitialTempo; + Songs = new TrackInfo[NumSongs]; + for (int i = 0; i < NumSongs; ++i) + { + TrackInfo *newtrack = &Songs[i]; + const TrackInfo *oldtrack = &original->Songs[i]; + + newtrack->EventChunk = MusHeader + (oldtrack->EventChunk - original->MusHeader); + newtrack->EventLen = oldtrack->EventLen; + newtrack->EventP = 0; + + newtrack->TimbreChunk = MusHeader + (oldtrack->TimbreChunk - original->MusHeader); + newtrack->TimbreLen = oldtrack->TimbreLen; + } +} diff --git a/zdoom.vcproj b/zdoom.vcproj index 11e243a83..8e6c9b9ca 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - @@ -1892,6 +1884,14 @@ Outputs="$(IntDir)/$(InputName).obj" /> + + + @@ -2081,6 +2081,14 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + @@ -2091,14 +2099,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - + + + - - - + + + @@ -5391,14 +5399,6 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> - - - @@ -5517,6 +5517,10 @@ RelativePath=".\src\sound\music_win_mididevice.cpp" > + + @@ -5689,7 +5693,7 @@ /> Date: Tue, 28 Sep 2010 15:14:53 +0000 Subject: [PATCH 074/815] - fixed: G_DoLoadLevel must reset level.maptime before calling P_SetupLevel or all actors that read it in their BeginPlay method get the wrong time. SVN r2860 (trunk) --- src/g_level.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 610a1108a..62eb809aa 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -941,6 +941,7 @@ void G_DoLoadLevel (int position, bool autosave) level.flags2 &= ~LEVEL2_NOMONSTERS; } + level.maptime = 0; P_SetupLevel (level.mapname, position); AM_LevelInit(); @@ -982,7 +983,6 @@ void G_DoLoadLevel (int position, bool autosave) } level.starttime = gametic; - level.maptime = 0; G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level. G_FinishTravel (); if (players[consoleplayer].camera == NULL || From 6014250f3ade79f49667c1e10e107288f6fb480a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 28 Sep 2010 16:28:44 +0000 Subject: [PATCH 075/815] - fixed: Option menus with no selectable items crashed. SVN r2861 (trunk) --- src/menu/menu.h | 1 + src/menu/optionmenu.cpp | 59 +++++++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/menu/menu.h b/src/menu/menu.h index 692542198..f13300bc0 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -580,6 +580,7 @@ public: FOptionMenuItem *GetItem(FName name); DOptionMenu(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL); virtual void Init(DMenu *parent = NULL, FOptionMenuDescriptor *desc = NULL); + int FirstSelectable(); bool Responder (event_t *ev); bool MenuEvent (int mkey, bool fromcontroller); bool MouseEvent(int type, int x, int y); diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 9d3213716..5c45d0efb 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -96,18 +96,30 @@ void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc) mParentMenu = parent; GC::WriteBarrier(this, parent); mDesc = desc; + if (mDesc != NULL) mDesc->mSelectedItem = FirstSelectable(); + +} + +//============================================================================= +// +// +// +//============================================================================= + +int DOptionMenu::FirstSelectable() +{ 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; + while (i < (int)mDesc->mItems.Size() && !mDesc->mItems[i]->Selectable()); + if (i>=0 && i < (int)mDesc->mItems.Size()) return i; } + return -1; } //============================================================================= @@ -176,6 +188,11 @@ bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) switch (mkey) { case MKEY_Up: + if (mDesc->mSelectedItem == -1) + { + mDesc->mSelectedItem = FirstSelectable(); + break; + } do { --mDesc->mSelectedItem; @@ -214,6 +231,11 @@ bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) break; case MKEY_Down: + if (mDesc->mSelectedItem == -1) + { + mDesc->mSelectedItem = FirstSelectable(); + break; + } do { ++mDesc->mSelectedItem; @@ -225,8 +247,17 @@ bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) } if (mDesc->mSelectedItem >= (int)mDesc->mItems.Size()) { - mDesc->mSelectedItem = 0; - mDesc->mScrollPos = 0; + if (startedAt == -1) + { + mDesc->mSelectedItem = -1; + mDesc->mScrollPos = -1; + break; + } + else + { + mDesc->mSelectedItem = 0; + mDesc->mScrollPos = 0; + } } } while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable() && mDesc->mSelectedItem != startedAt); @@ -240,10 +271,13 @@ bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) { mDesc->mScrollPos = 0; } - mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1; - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + if (mDesc->mSelectedItem != -1) { - ++mDesc->mSelectedItem; + mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos + 1; + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + { + ++mDesc->mSelectedItem; + } } } break; @@ -257,10 +291,13 @@ bool DOptionMenu::MenuEvent (int mkey, bool fromcontroller) { mDesc->mScrollPos = mDesc->mItems.Size() - mDesc->mScrollTop - pagesize; } - mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos; - while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + if (mDesc->mSelectedItem != -1) { - ++mDesc->mSelectedItem; + mDesc->mSelectedItem = mDesc->mScrollTop + mDesc->mScrollPos; + while (!mDesc->mItems[mDesc->mSelectedItem]->Selectable()) + { + ++mDesc->mSelectedItem; + } } } break; From e2c105b4474063515c20a9c1c55501849feb7f91 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Sep 2010 00:35:47 +0000 Subject: [PATCH 076/815] - fixed: XMI music files were not converted for playback with FMod or Timidity++. - Was there any reason why the MIDI_GUS device was so well hidden from the user? It sure does not sound broken. Added it to MIDI menu and $mididevice. SVN r2862 (trunk) --- src/s_advsound.cpp | 1 + src/s_sound.h | 1 + src/sound/i_music.cpp | 6 ++- src/sound/music_midi_base.cpp | 75 +++++++++++++++-------------------- 4 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index a2fa8ccd0..e6e7ce2a8 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1295,6 +1295,7 @@ static void S_AddSNDINFO (int lump) else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; else if (sc.Compare("fluidsynth")) MidiDevices[nm] = MDEV_FLUIDSYNTH; + else if (sc.Compare("gus")) MidiDevices[nm] = MDEV_GUS; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); } break; diff --git a/src/s_sound.h b/src/s_sound.h index 05237ba27..5c2796d5b 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -379,6 +379,7 @@ enum EMidiDevice MDEV_FMOD = 2, MDEV_TIMIDITY = 3, MDEV_FLUIDSYNTH = 4, + MDEV_GUS = 5, }; typedef TMap MidiDeviceMap; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index ab8cd8f66..52a4698e4 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -527,7 +527,7 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { devtype = MIDI_OPL; } - else if (snd_mididevice == -4 && device == MDEV_DEFAULT) + else if (device == MDEV_GUS || (snd_mididevice == -4 && device == MDEV_DEFAULT)) { devtype = MIDI_GUS; } @@ -554,6 +554,10 @@ retry_as_fmod: { streamer = new MUSSong2(file, musiccache, len, MIDI_Null); } + else if (miditype == MIDI_XMI) + { + streamer = new XMISong(file, musiccache, len, MIDI_Null); + } else { assert(miditype == MIDI_HMI); diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 97eb88708..2c8617c4a 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -10,6 +10,34 @@ static DWORD nummididevices; static bool nummididevicesset; +#ifdef HAVE_FLUIDSYNTH +#define NUM_DEF_DEVICES 5 +#else +#define NUM_DEF_DEVICES 4 +#endif + +static void AddDefaultMidiDevices(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 = "GUS"; + pair[p].Value = -4.0; + pair[p+1].Text = "OPL Synth Emulation"; + pair[p+1].Value = -3.0; + pair[p+2].Text = "TiMidity++"; + pair[p+2].Value = -2.0; + pair[p+3].Text = "FMOD"; + pair[p+3].Value = -1.0; + +} + #ifdef _WIN32 UINT mididevice; @@ -71,29 +99,9 @@ void I_ShutdownMusicWin32 () } } -#ifdef HAVE_FLUIDSYNTH -#define NUM_DEF_DEVICES 4 -#else -#define NUM_DEF_DEVICES 3 -#endif 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; - + AddDefaultMidiDevices(opt); for (DWORD id = 0; id < nummididevices; ++id) { @@ -104,7 +112,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) assert(res == MMSYSERR_NOERROR); if (res == MMSYSERR_NOERROR) { - pair = &opt->mValues[opt->mValues.Reserve(1)]; + FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(1)]; pair->Text = caps.szPname; pair->Value = (float)id; } @@ -156,6 +164,7 @@ CCMD (snd_listmididevices) #ifdef HAVE_FLUIDSYNTH PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0); #endif + PrintMidiDevice (-4, "GUS", 0, MOD_SWSYNTH); PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0); PrintMidiDevice (-2, "TiMidity++", 0, MOD_SWSYNTH); PrintMidiDevice (-1, "FMOD", 0, MOD_SWSYNTH); @@ -188,28 +197,9 @@ CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) self = -1; } -#ifdef HAVE_FLUIDSYNTH -#define NUM_DEF_DEVICES 4 -#else -#define NUM_DEF_DEVICES 3 -#endif 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; + AddDefaultMidiDevices(opt); } CCMD (snd_listmididevices) @@ -217,6 +207,7 @@ CCMD (snd_listmididevices) #ifdef HAVE_FLUIDSYNTH Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #endif + Printf("%s-4. GUS\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); From 64784b2dc81d6e2fd280cf070d7dff394d75a4c5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Sep 2010 00:54:14 +0000 Subject: [PATCH 077/815] - fixed: DOptionMenu::FirstSelectable should not be called when the menu already has a valid selection. SVN r2863 (trunk) --- src/menu/optionmenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/menu/optionmenu.cpp b/src/menu/optionmenu.cpp index 5c45d0efb..12294a36c 100644 --- a/src/menu/optionmenu.cpp +++ b/src/menu/optionmenu.cpp @@ -96,7 +96,7 @@ void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc) mParentMenu = parent; GC::WriteBarrier(this, parent); mDesc = desc; - if (mDesc != NULL) mDesc->mSelectedItem = FirstSelectable(); + if (mDesc != NULL && mDesc->mSelectedItem == -1) mDesc->mSelectedItem = FirstSelectable(); } @@ -108,7 +108,7 @@ void DOptionMenu::Init(DMenu *parent, FOptionMenuDescriptor *desc) int DOptionMenu::FirstSelectable() { - if (mDesc != NULL && mDesc->mSelectedItem < 0) + if (mDesc != NULL) { // Go down to the first selectable item int i = -1; From bc08502132d9cf64fb0f0f033c33ad7663daeb8f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 29 Sep 2010 03:35:53 +0000 Subject: [PATCH 078/815] - Pass SMF files through the MIDI conversion process too, to give TiMidity++ and FMOD some degree of support for songs that use loop controllers to loop the song back to a point after the very beginning of the song. - Enable loops during SMF generation. Infinite loops will be clamped to some finite amount. (This is currently 30, so a 3 minute song will still restart from the very beginning after 90 minutes) - Fixed: The SMF, HMI, and XMI readers all generated invalid MEVT_NOP events. - Fixed: SMF generation died on songs that set their tempo during the initial beat. SVN r2864 (trunk) --- src/sound/i_music.cpp | 8 +++- src/sound/i_musicinterns.h | 7 ++-- src/sound/music_hmi_midiout.cpp | 18 +-------- src/sound/music_midistream.cpp | 70 ++++++++++++++++++++++++++++++--- src/sound/music_smf_midiout.cpp | 51 +++++++++++------------- src/sound/music_xmi_midiout.cpp | 24 ++--------- 6 files changed, 101 insertions(+), 77 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 52a4698e4..24310b9f0 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -545,12 +545,16 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int #endif retry_as_fmod: - if (miditype != MIDI_MIDI && devtype >= MIDI_Null) + if (devtype >= MIDI_Null) { // Convert to standard MIDI for external sequencers. MIDIStreamer *streamer; - if (miditype == MIDI_MUS) + if (miditype == MIDI_MIDI) + { + streamer = new MIDISong2(file, musiccache, len, MIDI_Null); + } + else if (miditype == MIDI_MUS) { streamer = new MUSSong2(file, musiccache, len, MIDI_Null); } diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 6912cdb66..d518dbab9 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -372,6 +372,8 @@ protected: int FillBuffer(int buffer_num, int max_events, DWORD max_time); int ServiceEvent(); int VolumeControllerChange(int channel, int volume); + int ClampLoopCount(int loopcount); + void SetTempo(int new_tempo); static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); @@ -421,7 +423,7 @@ protected: DWORD Volume; EMIDIDevice DeviceType; bool CallbackIsThreaded; - bool IgnoreLoops; + int LoopLimit; FString DumpFilename; }; @@ -477,7 +479,6 @@ protected: void ProcessInitialMetaEvents (); DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay); TrackInfo *FindNextDue (); - void SetTempo(int new_tempo); BYTE *MusHeader; int SongLen; @@ -537,7 +538,6 @@ protected: void ProcessInitialMetaEvents (); DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay); TrackInfo *FindNextDue (); - void SetTempo(int new_tempo); static DWORD ReadVarLenHMI(TrackInfo *); static DWORD ReadVarLenHMP(TrackInfo *); @@ -581,7 +581,6 @@ protected: void ProcessInitialMetaEvents(); DWORD *SendCommand (DWORD *event, EventSource track, DWORD delay); EventSource FindNextDue(); - void SetTempo(int new_tempo); BYTE *MusHeader; int SongLen; // length of the entire file diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 465a20b1b..fd65fde05 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -636,7 +636,7 @@ DWORD *HMISong::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } else { - events[2] = MEVT_NOP; + events[2] = MEVT_NOP << 24; } events += 3; @@ -975,22 +975,6 @@ HMISong::TrackInfo *HMISong::FindNextDue () } -//========================================================================== -// -// HMISong :: SetTempo -// -// Sets the tempo from a track's initial meta events. -// -//========================================================================== - -void HMISong::SetTempo(int new_tempo) -{ - if (0 == MIDI->SetTempo(new_tempo)) - { - Tempo = new_tempo; - } -} - //========================================================================== // // HMISong :: GetOPLDumper diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index f30256155..5d271a11e 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -43,6 +43,9 @@ #define MAX_TIME (1000000/10) // Send out 1/10 of a sec of events at a time. +#define EXPORT_LOOP_LIMIT 30 // Maximum number of times to loop when exporting a MIDI file. + // (for songs with loop controller events) + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -262,7 +265,7 @@ void MIDIStreamer::Play(bool looping, int subsong) SetMIDISubsong(subsong); CheckCaps(MIDI->GetTechnology()); Precache(); - IgnoreLoops = false; + LoopLimit = 0; // Set time division and tempo. if (0 != MIDI->SetTimeDiv(Division) || @@ -535,7 +538,9 @@ void MIDIStreamer::OutputVolume (DWORD volume) int MIDIStreamer::VolumeControllerChange(int channel, int volume) { ChannelVolumes[channel] = volume; - return IgnoreLoops ? volume : ((volume + 1) * Volume) >> 16; + // If loops are limited, we can assume we're exporting this MIDI file, + // so we should not adjust the volume level. + return LoopLimit != 0 ? volume : ((volume + 1) * Volume) >> 16; } //========================================================================== @@ -867,7 +872,7 @@ void MIDIStreamer::Precache() BYTE found_banks[256] = { 0, }; bool multiple_banks = false; - IgnoreLoops = true; + LoopLimit = 1; DoRestart(); found_banks[0] = true; // Bank 0 is always used. found_banks[128] = true; @@ -968,7 +973,7 @@ void MIDIStreamer::CreateSMF(TArray &file) // Always create songs aimed at GM devices. CheckCaps(MOD_MIDIPORT); - IgnoreLoops = true; + LoopLimit = EXPORT_LOOP_LIMIT; DoRestart(); Tempo = InitialTempo; @@ -1053,7 +1058,7 @@ void MIDIStreamer::CreateSMF(TArray &file) file[20] = BYTE(len >> 8); file[21] = BYTE(len & 255); - IgnoreLoops = false; + LoopLimit = 0; } //========================================================================== @@ -1086,6 +1091,61 @@ static void WriteVarLen (TArray &file, DWORD value) } } +//========================================================================== +// +// MIDIStreamer :: SetTempo +// +// Sets the tempo from a track's initial meta events. Later tempo changes +// create MEVT_TEMPO events instead. +// +//========================================================================== + +void MIDIStreamer::SetTempo(int new_tempo) +{ + if (NULL == MIDI) + { + InitialTempo = new_tempo; + } + else if (0 == MIDI->SetTempo(new_tempo)) + { + Tempo = new_tempo; + } +} + + +//========================================================================== +// +// MIDIStreamer :: ClampLoopCount +// +// We use the XMIDI interpretation of loop count here, where 1 means it +// plays that section once (in other words, no loop) rather than the EMIDI +// interpretation where 1 means to loop it once. +// +// If LoopLimit is 1, we limit all loops, since this pass over the song is +// used to determine instruments for precaching. +// +// If LoopLimit is higher, we only limit infinite loops, since this song is +// being exported. +// +//========================================================================== + +int MIDIStreamer::ClampLoopCount(int loopcount) +{ + if (LoopLimit == 0) + { + return loopcount; + } + if (LoopLimit == 1) + { + return 1; + } + if (loopcount == 0) + { + return LoopLimit; + } + return loopcount; +} + //========================================================================== // // MIDIStreamer :: GetStats diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 64d3849b2..6e59c6087 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -63,7 +63,6 @@ struct MIDISong2::TrackInfo DWORD PlayedTime; bool Finished; BYTE RunningStatus; - SBYTE LoopCount; bool Designated; bool EProgramChange; bool EVolume; @@ -71,6 +70,7 @@ struct MIDISong2::TrackInfo size_t LoopBegin; DWORD LoopDelay; + int LoopCount; bool LoopFinished; DWORD ReadVarLen (); @@ -498,12 +498,18 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) break; case 116: // EMIDI Loop Begin - if (!IgnoreLoops) { - track->LoopBegin = track->TrackP; - track->LoopDelay = 0; - track->LoopCount = data2; - track->LoopFinished = track->Finished; + // We convert the loop count to XMIDI conventions before clamping. + // Then we convert it back to EMIDI conventions after clamping. + // (XMIDI can create "loops" that don't loop. EMIDI cannot.) + int loopcount = ClampLoopCount(data2 == 0 ? 0 : data2 + 1); + if (loopcount != 1) + { + track->LoopBegin = track->TrackP; + track->LoopDelay = 0; + track->LoopCount = loopcount == 0 ? 0 : loopcount - 1; + track->LoopFinished = track->Finished; + } } event = MIDI_META; break; @@ -530,14 +536,17 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) break; case 118: // EMIDI Global Loop Begin - if (!IgnoreLoops) { - for (i = 0; i < NumTracks; ++i) + int loopcount = ClampLoopCount(data2 == 0 ? 0 : data2 + 1); + if (loopcount != 1) { - Tracks[i].LoopBegin = Tracks[i].TrackP; - Tracks[i].LoopDelay = Tracks[i].Delay; - Tracks[i].LoopCount = data2; - Tracks[i].LoopFinished = Tracks[i].Finished; + for (i = 0; i < NumTracks; ++i) + { + Tracks[i].LoopBegin = Tracks[i].TrackP; + Tracks[i].LoopDelay = Tracks[i].Delay; + Tracks[i].LoopCount = loopcount == 0 ? 0 : loopcount - 1; + Tracks[i].LoopFinished = Tracks[i].Finished; + } } } event = MIDI_META; @@ -579,7 +588,7 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } else { - events[2] = MEVT_NOP; + events[2] = MEVT_NOP << 24; } events += 3; } @@ -765,22 +774,6 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue () } -//========================================================================== -// -// MIDISong2 :: SetTempo -// -// Sets the tempo from a track's initial meta events. -// -//========================================================================== - -void MIDISong2::SetTempo(int new_tempo) -{ - if (0 == MIDI->SetTempo(new_tempo)) - { - Tempo = new_tempo; - } -} - //========================================================================== // // MIDISong2 :: GetOPLDumper diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 26c8a3557..75dea937d 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -61,7 +61,7 @@ struct LoopInfo { size_t LoopBegin; - SBYTE LoopCount; + int LoopCount; bool LoopFinished; }; @@ -452,10 +452,10 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay) break; case 116: // XMI for loop controller - if (!IgnoreLoops && track->ForDepth < MAX_FOR_DEPTH) + if (track->ForDepth < MAX_FOR_DEPTH) { track->ForLoops[track->ForDepth].LoopBegin = track->EventP; - track->ForLoops[track->ForDepth].LoopCount = data2; + track->ForLoops[track->ForDepth].LoopCount = ClampLoopCount(data2); track->ForLoops[track->ForDepth].LoopFinished = track->Finished; } track->ForDepth++; @@ -500,7 +500,7 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay) } else { - events[2] = MEVT_NOP; + events[2] = MEVT_NOP << 24; } events += 3; @@ -678,22 +678,6 @@ XMISong::EventSource XMISong::FindNextDue() } -//========================================================================== -// -// XMISong :: SetTempo -// -// Sets the tempo from a track's initial meta events. -// -//========================================================================== - -void XMISong::SetTempo(int new_tempo) -{ - if (0 == MIDI->SetTempo(new_tempo)) - { - Tempo = new_tempo; - } -} - //========================================================================== // // XMISong :: GetOPLDumper From 5be9729872adc334618eddbf37752e06c239804c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Sep 2010 07:25:14 +0000 Subject: [PATCH 079/815] - FScanner::Float is a double so there's no need to cast values to float before assigning them to this variable. SVN r2865 (trunk) --- src/am_map.cpp | 12 ------------ src/sc_man.cpp | 6 +++--- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 73a7f8c98..45cb3f980 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -868,18 +868,6 @@ void AM_initVariables () old_m_h = m_h; } -/* -static void GetComponents (int color, DWORD *palette, float &r, float &g, float &b) -{ - if (palette) - color = palette[color]; - - r = (float)RPART(color); - g = (float)GPART(color); - b = (float)BPART(color); -} -*/ - //============================================================================= // // diff --git a/src/sc_man.cpp b/src/sc_man.cpp index a725c2703..3860105dd 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -606,7 +606,7 @@ bool FScanner::GetNumber () ScriptError ("SC_GetNumber: Bad numeric constant \"%s\".", String); } } - Float = (float)Number; + Float = Number; return true; } else @@ -662,7 +662,7 @@ bool FScanner::CheckNumber () return false; } } - Float = (float)Number; + Float = Number; return true; } else @@ -719,7 +719,7 @@ bool FScanner::GetFloat () CheckOpen (); if (GetString()) { - Float = (float)strtod (String, &stopper); + Float = strtod (String, &stopper); if (*stopper != 0) { ScriptError ("SC_GetFloat: Bad numeric constant \"%s\".", String); From 546467660322f96685dbaa15f38b5b67a4fb3f7e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Sep 2010 19:29:14 +0000 Subject: [PATCH 080/815] - cleaned up i_music.cpp. SVN r2866 (trunk) --- src/s_sound.cpp | 24 +- src/sound/i_music.cpp | 548 +++++++++++++++++--------------- src/sound/i_music.h | 23 +- src/sound/music_midi_base.cpp | 5 +- src/sound/music_xmi_midiout.cpp | 3 - 5 files changed, 308 insertions(+), 295 deletions(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index c8fbd2343..b2565b950 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1688,7 +1688,7 @@ void S_PauseSound (bool notmusic, bool notsfx) { if (!notmusic && mus_playing.handle && !MusicPaused) { - I_PauseSong (mus_playing.handle); + mus_playing.handle->Pause(); MusicPaused = true; } if (!notsfx) @@ -1709,7 +1709,7 @@ void S_ResumeSound (bool notsfx) { if (mus_playing.handle && MusicPaused) { - I_ResumeSong (mus_playing.handle); + mus_playing.handle->Resume(); MusicPaused = false; } if (!notsfx) @@ -1868,11 +1868,11 @@ void S_UpdateSounds (AActor *listenactor) I_UpdateMusic(); - // [RH] Update music and/or playlist. I_QrySongPlaying() must be called + // [RH] Update music and/or playlist. IsPlaying() must be called // to attempt to reconnect to broken net streams and to advance the // playlist when the current song finishes. if (mus_playing.handle != NULL && - !I_QrySongPlaying(mus_playing.handle) && + !mus_playing.handle->IsPlaying() && PlayList) { PlayList->Advance(); @@ -2479,7 +2479,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) if (mus_playing.handle != 0) { // play it - I_PlaySong (mus_playing.handle, looping, S_GetMusicVolume (musicname), order); + mus_playing.handle->Start(looping, S_GetMusicVolume (musicname), order); mus_playing.baseorder = order; return true; } @@ -2537,15 +2537,17 @@ void S_StopMusic (bool force) // [RH] Don't stop if a playlist is active. if ((force || PlayList == NULL) && !mus_playing.name.IsEmpty()) { - if (MusicPaused) - I_ResumeSong(mus_playing.handle); - - I_StopSong(mus_playing.handle); - I_UnRegisterSong(mus_playing.handle); + if (mus_playing.handle != NULL) + { + if (MusicPaused) + mus_playing.handle->Resume(); + mus_playing.handle->Stop(); + delete mus_playing.handle; + mus_playing.handle = NULL; + } LastSong = mus_playing.name; mus_playing.name = ""; - mus_playing.handle = 0; } } diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 24310b9f0..0ae9af0cf 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -140,6 +140,68 @@ CUSTOM_CVAR (Float, snd_musicvolume, 0.5f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } +//========================================================================== +// +// +// +//========================================================================== + +void I_InitMusic (void) +{ + static bool setatterm = false; + + Timidity::LoadConfig(); + + snd_musicvolume.Callback (); + + nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound"); + +#ifdef _WIN32 + I_InitMusicWin32 (); +#endif // _WIN32 + + if (!setatterm) + { + setatterm = true; + atterm (I_ShutdownMusic); + +#ifndef _WIN32 + signal (SIGCHLD, ChildSigHandler); +#endif + } + MusicDown = false; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void I_ShutdownMusic(void) +{ + if (MusicDown) + return; + MusicDown = true; + if (currSong) + { + S_StopMusic (true); + assert (currSong == NULL); + } + Timidity::FreeAll(); +#ifdef _WIN32 + I_ShutdownMusicWin32(); +#endif // _WIN32 +} + + +//========================================================================== +// +// +// +//========================================================================== + MusInfo::MusInfo() : m_Status(STATE_Stopped), m_Looping(false), m_NotStartedYet(true) { @@ -147,8 +209,39 @@ MusInfo::MusInfo() MusInfo::~MusInfo () { + if (currSong == this) currSong = NULL; } +//========================================================================== +// +// starts playing this song +// +//========================================================================== + +void MusInfo::Start(bool loop, float rel_vol, int subsong) +{ + if (nomusic) return; + + if (rel_vol > 0.f) saved_relative_volume = relative_volume = rel_vol; + Stop (); + Play (loop, subsong); + m_NotStartedYet = false; + + if (m_Status == MusInfo::STATE_Playing) + currSong = this; + else + currSong = NULL; + + // Notify the sound system of the changed relative volume + snd_musicvolume.Callback(); +} + +//========================================================================== +// +// +// +//========================================================================== + bool MusInfo::SetPosition (unsigned int ms) { return false; @@ -198,121 +291,38 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) return NULL; } -void I_InitMusic (void) +//========================================================================== +// +// create a streamer based on MIDI file type +// +//========================================================================== + +static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, EMIDIDevice devtype, EMIDIType miditype) { - static bool setatterm = false; - - Timidity::LoadConfig(); - - snd_musicvolume.Callback (); - - nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound"); - -#ifdef _WIN32 - I_InitMusicWin32 (); -#endif // _WIN32 - - if (!setatterm) + switch (miditype) { - setatterm = true; - atterm (I_ShutdownMusic); - -#ifndef _WIN32 - signal (SIGCHLD, ChildSigHandler); -#endif - } - MusicDown = false; -} + case MIDI_MUS: + return new MUSSong2(file, musiccache, len, devtype); + case MIDI_MIDI: + return new MIDISong2(file, musiccache, len, devtype); -void I_ShutdownMusic(void) -{ - if (MusicDown) - return; - MusicDown = true; - if (currSong) - { - S_StopMusic (true); - assert (currSong == NULL); - } - Timidity::FreeAll(); -#ifdef _WIN32 - I_ShutdownMusicWin32(); -#endif // _WIN32 -} + case MIDI_HMI: + return new HMISong(file, musiccache, len, devtype); + case MIDI_XMI: + return new XMISong(file, musiccache, len, devtype); -void I_PlaySong (void *handle, int _looping, float rel_vol, int subsong) -{ - MusInfo *info = (MusInfo *)handle; - - if (!info || nomusic) - return; - - saved_relative_volume = relative_volume = rel_vol; - info->Stop (); - info->Play (!!_looping, subsong); - info->m_NotStartedYet = false; - - if (info->m_Status == MusInfo::STATE_Playing) - currSong = info; - else - currSong = NULL; - - // Notify the sound system of the changed relative volume - snd_musicvolume.Callback(); -} - - -void I_PauseSong (void *handle) -{ - MusInfo *info = (MusInfo *)handle; - - if (info) - info->Pause (); -} - -void I_ResumeSong (void *handle) -{ - MusInfo *info = (MusInfo *)handle; - - if (info) - info->Resume (); -} - -void I_StopSong (void *handle) -{ - MusInfo *info = (MusInfo *)handle; - - if (info) - info->Stop (); - - if (info == currSong) - currSong = NULL; -} - -void I_UnRegisterSong (void *handle) -{ - MusInfo *info = (MusInfo *)handle; - - if (info) - { - delete info; + default: + return NULL; } } -MusInfo *I_RegisterURLSong (const char *url) -{ - StreamSong *song; - - song = new StreamSong(url, 0, 0); - if (song->IsValid()) - { - return song; - } - delete song; - return NULL; -} +//========================================================================== +// +// create a MIDI player +// +//========================================================================== static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype) { @@ -333,35 +343,130 @@ static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccach return new StreamSong(filename, offset, len); } } - else if (miditype == MIDI_MUS) - { - return new MUSSong2(file, musiccache, len, devtype); - } - else if (miditype == MIDI_MIDI) - { - return new MIDISong2(file, musiccache, len, devtype); - } - else if (miditype == MIDI_HMI) - { - return new HMISong(file, musiccache, len, devtype); - } - else if (miditype == MIDI_XMI) - { - return new XMISong(file, musiccache, len, devtype); - } - return NULL; + else return CreateMIDIStreamer(file, musiccache, len, devtype, miditype); } +//========================================================================== +// +// identify MIDI file type +// +//========================================================================== + +static EMIDIType IdentifyMIDIType(DWORD *id, int size) +{ + // Check for MUS format + // Tolerate sloppy wads by searching up to 32 bytes for the header + if (MUSHeaderSearch((BYTE*)id, size) >= 0) + { + return MIDI_MUS; + } + // Check for HMI format + else + if (id[0] == MAKE_ID('H','M','I','-') && + id[1] == MAKE_ID('M','I','D','I') && + id[2] == MAKE_ID('S','O','N','G')) + { + return MIDI_HMI; + } + // Check for HMP format + else + if (id[0] == MAKE_ID('H','M','I','M') && + id[1] == MAKE_ID('I','D','I','P')) + { + return MIDI_HMI; + } + // Check for XMI format + else + if ((id[0] == MAKE_ID('F','O','R','M') && + id[2] == MAKE_ID('X','D','I','R')) || + ((id[0] == MAKE_ID('C','A','T',' ') || id[0] == MAKE_ID('F','O','R','M')) && + id[2] == MAKE_ID('X','M','I','D'))) + { + return MIDI_XMI; + } + // Check for MIDI format + else if (id[0] == MAKE_ID('M','T','h','d')) + { + return MIDI_MIDI; + } + else + { + return MIDI_NOTMIDI; + } +} + +//========================================================================== +// +// select the MIDI device to play on +// +//========================================================================== + +static EMIDIDevice SelectMIDIDevice(int device) +{ + /* MIDI are played as: + - OPL: + - if explicitly selected by $mididevice + - when snd_mididevice is -3 and no midi device is set for the song + + - Timidity: + - if explicitly selected by $mididevice + - when snd_mididevice is -2 and no midi device is set for the song + + - FMod: + - if explicitly selected by $mididevice + - when snd_mididevice is -1 and no midi device is set for the song + - as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0 + + - MMAPI (Win32 only): + - if explicitly selected by $mididevice (non-Win32 redirects this to FMOD) + - when snd_mididevice is >= 0 and no midi device is set for the song + - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 + */ + EMIDIDevice devtype = MIDI_Null; + + // Choose the type of MIDI device we want. + if (device == MDEV_FMOD || (snd_mididevice == -1 && device == MDEV_DEFAULT)) + { + return MIDI_FMOD; + } + else if (device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) + { + return MIDI_Timidity; + } + else if (device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) + { + return MIDI_OPL; + } + else if (device == MDEV_GUS || (snd_mididevice == -4 && device == MDEV_DEFAULT)) + { + return MIDI_GUS; + } + #ifdef HAVE_FLUIDSYNTH + else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT)) + { + return MIDI_Fluid; + } + #endif + #ifdef _WIN32 + return MIDI_Win; + #else + return MIDI_Null; + #endif +} + + +//========================================================================== +// +// identify a music lump's type and set up a player for it +// +//========================================================================== + MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int len, int device) { FILE *file; MusInfo *info = NULL; - union - { - DWORD id[32/4]; - BYTE idstr[32]; - }; const char *fmt; + DWORD id[32/4]; BYTE *ungzipped; int i; @@ -452,134 +557,34 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int } } - EMIDIType miditype = MIDI_NOTMIDI; - - // Check for MUS format - // Tolerate sloppy wads by searching up to 32 bytes for the header - if (MUSHeaderSearch(idstr, sizeof(idstr)) >= 0) - { - miditype = MIDI_MUS; - } - // Check for HMI format - else - if (id[0] == MAKE_ID('H','M','I','-') && - id[1] == MAKE_ID('M','I','D','I') && - id[2] == MAKE_ID('S','O','N','G')) - { - miditype = MIDI_HMI; - } - // Check for HMP format - else - if (id[0] == MAKE_ID('H','M','I','M') && - id[1] == MAKE_ID('I','D','I','P')) - { - miditype = MIDI_HMI; - } - // Check for XMI format - else - if ((id[0] == MAKE_ID('F','O','R','M') && - id[2] == MAKE_ID('X','D','I','R')) || - ((id[0] == MAKE_ID('C','A','T',' ') || id[0] == MAKE_ID('F','O','R','M')) && - id[2] == MAKE_ID('X','M','I','D'))) - { - miditype = MIDI_XMI; - } - // Check for MIDI format - else if (id[0] == MAKE_ID('M','T','h','d')) - { - miditype = MIDI_MIDI; - } - + EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { TArray midi; - /* MIDI are played as: - - OPL: - - if explicitly selected by $mididevice - - when snd_mididevice is -3 and no midi device is set for the song - - Timidity: - - if explicitly selected by $mididevice - - when snd_mididevice is -2 and no midi device is set for the song - - - FMod: - - if explicitly selected by $mididevice - - when snd_mididevice is -1 and no midi device is set for the song - - as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0 - - - MMAPI (Win32 only): - - if explicitly selected by $mididevice (non-Win32 redirects this to FMOD) - - when snd_mididevice is >= 0 and no midi device is set for the song - - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 - */ - EMIDIDevice devtype = MIDI_Null; - - // Choose the type of MIDI device we want. - if (device == MDEV_FMOD || (snd_mididevice == -1 && device == MDEV_DEFAULT)) - { - devtype = MIDI_FMOD; - } - else if (device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) - { - devtype = MIDI_Timidity; - } - else if (device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) - { - devtype = MIDI_OPL; - } - else if (device == MDEV_GUS || (snd_mididevice == -4 && device == MDEV_DEFAULT)) - { - devtype = MIDI_GUS; - } -#ifdef HAVE_FLUIDSYNTH - else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT)) - { - devtype = MIDI_Fluid; - } -#endif -#ifdef _WIN32 - else - { - devtype = MIDI_Win; - } -#endif + EMIDIDevice devtype = SelectMIDIDevice(device); retry_as_fmod: if (devtype >= MIDI_Null) { // Convert to standard MIDI for external sequencers. - MIDIStreamer *streamer; - - if (miditype == MIDI_MIDI) + MIDIStreamer *streamer = CreateMIDIStreamer(file, musiccache, len, MIDI_Null, miditype); + if (streamer != NULL) { - streamer = new MIDISong2(file, musiccache, len, MIDI_Null); - } - else if (miditype == MIDI_MUS) - { - streamer = new MUSSong2(file, musiccache, len, MIDI_Null); - } - else if (miditype == MIDI_XMI) - { - streamer = new XMISong(file, musiccache, len, MIDI_Null); - } - else - { - assert(miditype == MIDI_HMI); - streamer = new HMISong(file, musiccache, len, MIDI_Null); - } - if (streamer->IsValid()) - { - streamer->CreateSMF(midi); - miditype = MIDI_MIDI; - musiccache = &midi[0]; - len = midi.Size(); - if (file != NULL) + if (streamer->IsValid()) { - fclose(file); - file = NULL; + streamer->CreateSMF(midi); + miditype = MIDI_MIDI; + musiccache = &midi[0]; + len = midi.Size(); + if (file != NULL) + { + fclose(file); + file = NULL; + } } + delete streamer; } - delete streamer; } info = CreateMIDISong(file, filename, musiccache, offset, len, devtype, miditype); if (info != NULL && !info->IsValid()) @@ -678,6 +683,12 @@ retry_as_fmod: return info; } +//========================================================================== +// +// play CD music +// +//========================================================================== + MusInfo *I_RegisterCDSong (int track, int id) { MusInfo *info = new CDSong (track, id); @@ -691,6 +702,25 @@ MusInfo *I_RegisterCDSong (int track, int id) return info; } +//========================================================================== +// +// +// +//========================================================================== + +MusInfo *I_RegisterURLSong (const char *url) +{ + StreamSong *song; + + song = new StreamSong(url, 0, 0); + if (song->IsValid()) + { + return song; + } + delete song; + return NULL; +} + //========================================================================== // // ungzip @@ -773,6 +803,12 @@ BYTE *ungzip(BYTE *data, int *complen) return newdata; } +//========================================================================== +// +// +// +//========================================================================== + void I_UpdateMusic() { if (currSong != NULL) @@ -781,22 +817,12 @@ void I_UpdateMusic() } } -// Is the song playing? -bool I_QrySongPlaying (void *handle) -{ - MusInfo *info = (MusInfo *)handle; - - return info ? info->IsPlaying () : false; -} - -// Change to a different part of the song -bool I_SetSongPosition (void *handle, int order) -{ - MusInfo *info = (MusInfo *)handle; - return info ? info->SetPosition (order) : false; -} - +//========================================================================== +// // Sets relative music volume. Takes $musicvolume in SNDINFO into consideration +// +//========================================================================== + void I_SetMusicVolume (float factor) { factor = clamp(factor, 0, 2.0f); @@ -804,6 +830,12 @@ void I_SetMusicVolume (float factor) snd_musicvolume.Callback(); } +//========================================================================== +// +// test a relative music volume +// +//========================================================================== + CCMD(testmusicvol) { if (argv.argc() > 1) diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 5b238cd4c..b620ae52f 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -50,33 +50,12 @@ void I_UpdateMusic (); // Volume. void I_SetMusicVolume (float volume); -// PAUSE game handling. -void I_PauseSong (void *handle); -void I_ResumeSong (void *handle); - // Registers a song handle to song data. class MusInfo; MusInfo *I_RegisterSong (const char *file, BYTE *musiccache, int offset, int length, int device); MusInfo *I_RegisterCDSong (int track, int cdid = 0); MusInfo *I_RegisterURLSong (const char *url); -// Called by anything that wishes to start music. -// Plays a song, and when the song is done, -// starts playing it again in an endless loop. -void I_PlaySong (void *handle, int looping, float relative_vol=1.f, int subsong=0); - -// Stops a song. -void I_StopSong (void *handle); - -// See above (register), then think backwards -void I_UnRegisterSong (void *handle); - -// Set the current order (position) for a MOD -bool I_SetSongPosition (void *handle, int order); - -// Is the song still playing? -bool I_QrySongPlaying (void *handle); - // The base music class. Everything is derived from this -------------------- class MusInfo @@ -103,6 +82,8 @@ public: virtual void FluidSettingNum(const char *setting, double value); // " virtual void FluidSettingStr(const char *setting, const char *value); // " + void Start(bool loop, float rel_vol = -1.f, int subsong = 0); + enum EState { STATE_Stopped, diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 2c8617c4a..19bd7a232 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -66,11 +66,12 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // If a song is playing, move it to the new device. if (oldmididev != mididevice && currSong != NULL && currSong->IsMIDI()) { + // Does this even work, except for Windows system devices? MusInfo *song = currSong; if (song->m_Status == MusInfo::STATE_Playing) { - I_StopSong (song); - I_PlaySong (song, song->m_Looping); + song->Stop(); + song->Start(song->m_Looping); } } } diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 75dea937d..bece8eafa 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -30,9 +30,6 @@ ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** -** This file also supports the Apogee Sound System's EMIDI files. That -** basically means you can play the Duke3D songs without any editing and -** have them sound right. */ // HEADER FILES ------------------------------------------------------------ From 024bbeb171c866289bfa9945362b72e9ca17091b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 29 Sep 2010 22:24:55 +0000 Subject: [PATCH 081/815] - changed 'load game' menu so that it preselects the last used savegame, either for loading or saving. SVN r2867 (trunk) --- src/menu/loadsavemenu.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 0b3ccf1aa..9b7b0e683 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -57,6 +57,7 @@ class DLoadSaveMenu : public DListMenu protected: static TDeletingArray SaveGames; static int LastSaved; + static int LastAccessed; int Selected; int TopItem; @@ -115,6 +116,7 @@ IMPLEMENT_CLASS(DLoadSaveMenu) TDeletingArray DLoadSaveMenu::SaveGames; int DLoadSaveMenu::LastSaved = -1; +int DLoadSaveMenu::LastAccessed = -1; FSaveGameNode *quickSaveSlot; @@ -336,7 +338,7 @@ void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okF if (okForQuicksave) { if (quickSaveSlot == NULL) quickSaveSlot = node; - LastSaved = i; + LastAccessed = LastSaved = i; } return; } @@ -352,7 +354,7 @@ void DLoadSaveMenu::NotifyNewSave (const char *file, const char *title, bool okF if (okForQuicksave) { if (quickSaveSlot == NULL) quickSaveSlot = node; - LastSaved = index; + LastAccessed = LastSaved = index; } } @@ -734,10 +736,16 @@ bool DLoadSaveMenu::MenuEvent (int mkey, bool fromcontroller) { if (Selected != -1) { + int listindex = SaveGames[0]->bNoDelete? Selected-1 : Selected; remove (SaveGames[Selected]->Filename.GetChars()); UnloadSaveData (); Selected = RemoveSaveSlot (Selected); ExtractSaveData (Selected); + + if (LastSaved == listindex) LastSaved = -1; + else if (LastSaved > listindex) LastSaved--; + if (LastAccessed == listindex) LastAccessed = -1; + else if (LastAccessed > listindex) LastAccessed--; } return true; } @@ -1040,6 +1048,10 @@ DLoadMenu::DLoadMenu(DMenu *parent, FListMenuDescriptor *desc) : DLoadSaveMenu(parent, desc) { TopItem = 0; + if (LastAccessed != -1) + { + Selected = LastAccessed; + } ExtractSaveData (Selected); } @@ -1074,6 +1086,7 @@ bool DLoadMenu::MenuEvent (int mkey, bool fromcontroller) } M_ClearMenus(); BorderNeedRefresh = screen->GetPageCount (); + LastAccessed = Selected; return true; } return false; From b6941be55fa6aa4f80880003b963af19fa06695f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Oct 2010 02:19:50 +0000 Subject: [PATCH 082/815] - Reimplemented FMOD MIDI playback as a psuedo-MIDI device so that it can take advantage of XMI subsongs. SVN r2872 (trunk) --- src/sound/i_musicinterns.h | 54 ++++- src/sound/music_midistream.cpp | 157 +++++++++++--- src/sound/music_psuedo_mididevice.cpp | 260 +++++++++++++++++++++++ src/sound/music_softsynth_mididevice.cpp | 52 +---- zdoom.vcproj | 4 + 5 files changed, 434 insertions(+), 93 deletions(-) create mode 100644 src/sound/music_psuedo_mididevice.cpp diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index d518dbab9..6c43cd904 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -72,6 +72,8 @@ typedef BYTE *LPSTR; #endif #endif +class MIDIStreamer; + class MIDIDevice { public: @@ -88,16 +90,17 @@ public: virtual int StreamOutSync(MIDIHDR *data) = 0; virtual int Resume() = 0; virtual void Stop() = 0; - virtual int PrepareHeader(MIDIHDR *data) = 0; - virtual int UnprepareHeader(MIDIHDR *data) = 0; - virtual bool FakeVolume() = 0; + virtual int PrepareHeader(MIDIHDR *data); + virtual int UnprepareHeader(MIDIHDR *data); + virtual bool FakeVolume(); virtual bool Pause(bool paused) = 0; - virtual bool NeedThreadedCallback() = 0; + virtual bool NeedThreadedCallback(); virtual void PrecacheInstruments(const WORD *instruments, int count); virtual void TimidityVolumeChanged(); virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingStr(const char *setting, const char *value); + virtual bool Preprocess(MIDIStreamer *song); virtual FString GetStats(); }; @@ -139,6 +142,42 @@ protected: }; #endif +// Base class for psuedo-MIDI devices --------------------------------------- + +class PsuedoMIDIDevice : public MIDIDevice +{ +public: + PsuedoMIDIDevice(); + ~PsuedoMIDIDevice(); + + void Close(); + bool IsOpen() const; + int GetTechnology() const; + bool Pause(bool paused); + int Resume(); + void Stop(); + int StreamOut(MIDIHDR *data); + int StreamOutSync(MIDIHDR *data); + int SetTempo(int tempo); + int SetTimeDiv(int timediv); + FString GetStats(); + +protected: + SoundStream *Stream; + bool Started; + int SampleRate; + +}; + +// FMOD psuedo-MIDI device -------------------------------------------------- + +class FMODMIDIDevice : public PsuedoMIDIDevice +{ +public: + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + bool Preprocess(MIDIStreamer *song); +}; + // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -156,10 +195,6 @@ public: int StreamOutSync(MIDIHDR *data); int Resume(); void Stop(); - int PrepareHeader(MIDIHDR *data); - int UnprepareHeader(MIDIHDR *data); - bool FakeVolume(); - bool NeedThreadedCallback(); bool Pause(bool paused); protected: @@ -335,10 +370,10 @@ enum EMIDIDevice MIDI_OPL, MIDI_GUS, MIDI_Fluid, + MIDI_FMOD, // only used by I_RegisterSong MIDI_Null, - MIDI_FMOD, MIDI_Timidity }; @@ -378,6 +413,7 @@ protected: static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); // Virtuals for subclasses to override + virtual void StartPlayback(); virtual void CheckCaps(int tech); virtual void DoInitialSetup() = 0; virtual void DoRestart() = 0; diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 5d271a11e..aa1e3feab 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -239,6 +239,10 @@ void MIDIStreamer::Play(bool looping, int subsong) break; #endif + case MIDI_FMOD: + MIDI = new FMODMIDIDevice; + break; + case MIDI_GUS: MIDI = new TimidityMIDIDevice; break; @@ -264,6 +268,49 @@ void MIDIStreamer::Play(bool looping, int subsong) SetMIDISubsong(subsong); CheckCaps(MIDI->GetTechnology()); + + if (MIDI->Preprocess(this)) + { + StartPlayback(); + } + + if (0 != MIDI->Resume()) + { + Printf ("Starting MIDI playback failed\n"); + Stop(); + } + else + { +#ifdef _WIN32 + if (MIDI->NeedThreadedCallback()) + { + PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid); + if (PlayerThread == NULL) + { + Printf ("Creating MIDI thread failed\n"); + Stop(); + } + else + { + m_Status = STATE_Playing; + } + } + else +#endif + { + m_Status = STATE_Playing; + } + } +} + +//========================================================================== +// +// MIDIStreamer :: StartPlayback +// +//========================================================================== + +void MIDIStreamer::StartPlayback() +{ Precache(); LoopLimit = 0; @@ -312,34 +359,6 @@ void MIDIStreamer::Play(bool looping, int subsong) } } while (BufferNum != 0); - - if (0 != MIDI->Resume()) - { - Printf ("Starting MIDI playback failed\n"); - Stop(); - } - else - { -#ifdef _WIN32 - if (MIDI->NeedThreadedCallback()) - { - PlayerThread = CreateThread(NULL, 0, PlayerProc, this, 0, &tid); - if (PlayerThread == NULL) - { - Printf ("Creating MIDI thread failed\n"); - Stop(); - } - else - { - m_Status = STATE_Playing; - } - } - else -#endif - { - m_Status = STATE_Playing; - } - } } //========================================================================== @@ -457,6 +476,12 @@ void MIDIStreamer::MusicVolumeChanged() } } +//========================================================================== +// +// MIDIStreamer :: TimidityVolumeChanged +// +//========================================================================== + void MIDIStreamer::TimidityVolumeChanged() { if (MIDI != NULL) @@ -1102,11 +1127,8 @@ static void WriteVarLen (TArray &file, DWORD value) void MIDIStreamer::SetTempo(int new_tempo) { - if (NULL == MIDI) - { - InitialTempo = new_tempo; - } - else if (0 == MIDI->SetTempo(new_tempo)) + InitialTempo = new_tempo; + if (NULL != MIDI && 0 == MIDI->SetTempo(new_tempo)) { Tempo = new_tempo; } @@ -1227,6 +1249,75 @@ void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count) { } +//========================================================================== +// +// MIDIDevice :: Preprocess +// +// Gives the MIDI device a chance to do some processing with the song before +// it starts playing it. Returns true if MIDIStreamer should perform its +// standard playback startup sequence. +// +//========================================================================== + +bool MIDIDevice::Preprocess(MIDIStreamer *song) +{ + return true; +} + +//========================================================================== +// +// MIDIDevice :: PrepareHeader +// +// Wrapper for MCI's midiOutPrepareHeader. +// +//========================================================================== + +int MIDIDevice::PrepareHeader(MIDIHDR *header) +{ + return 0; +} + +//========================================================================== +// +// MIDIDevice :: UnprepareHeader +// +// Wrapper for MCI's midiOutUnprepareHeader. +// +//========================================================================== + +int MIDIDevice::UnprepareHeader(MIDIHDR *header) +{ + return 0; +} + +//========================================================================== +// +// MIDIDevice :: FakeVolume +// +// Since most implementations render as a normal stream, their volume is +// controlled through the GSnd interface, not here. +// +//========================================================================== + +bool MIDIDevice::FakeVolume() +{ + return false; +} + +//========================================================================== +// +// MIDIDevice :: NeedThreadedCallabck +// +// Most implementations can service the callback directly rather than using +// a separate thread. +// +//========================================================================== + +bool MIDIDevice::NeedThreadedCallback() +{ + return false; +} + //========================================================================== // // MIDIDevice :: TimidityVolumeChanged diff --git a/src/sound/music_psuedo_mididevice.cpp b/src/sound/music_psuedo_mididevice.cpp new file mode 100644 index 000000000..11ddea63e --- /dev/null +++ b/src/sound/music_psuedo_mididevice.cpp @@ -0,0 +1,260 @@ +/* +** music_psuedo_mididevice.cpp +** Common base class for psuedo MIDI devices. +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// PsuedoMIDIDevice Constructor +// +//========================================================================== + +PsuedoMIDIDevice::PsuedoMIDIDevice() +{ + Stream = NULL; + Started = false; + SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100; +} + +//========================================================================== +// +// PsuedoMIDIDevice Destructor +// +//========================================================================== + +PsuedoMIDIDevice::~PsuedoMIDIDevice() +{ + Close(); +} + +//========================================================================== +// +// PsuedoMIDIDevice :: Close +// +//========================================================================== + +void PsuedoMIDIDevice::Close() +{ + if (Stream != NULL) + { + delete Stream; + Stream = NULL; + } + Started = false; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: IsOpen +// +//========================================================================== + +bool PsuedoMIDIDevice::IsOpen() const +{ + return Stream != NULL; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: GetTechnology +// +//========================================================================== + +int PsuedoMIDIDevice::GetTechnology() const +{ + return MOD_MIDIPORT; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: Resume +// +//========================================================================== + +int PsuedoMIDIDevice::Resume() +{ + if (!Started) + { + if (Stream->Play(true, 1)) + { + Started = true; + return 0; + } + return 1; + } + return 0; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: Stop +// +//========================================================================== + +void PsuedoMIDIDevice::Stop() +{ + if (Started) + { + Stream->Stop(); + Started = false; + } +} + +//========================================================================== +// +// PsuedoMIDIDevice :: Pause +// +//========================================================================== + +bool PsuedoMIDIDevice::Pause(bool paused) +{ + if (Stream != NULL) + { + return Stream->SetPaused(paused); + } + return true; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: StreamOutSync +// +//========================================================================== + +int PsuedoMIDIDevice::StreamOutSync(MIDIHDR *header) +{ + assert(0); + return 0; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: StreamOut +// +//========================================================================== + +int PsuedoMIDIDevice::StreamOut(MIDIHDR *header) +{ + assert(0); + return 0; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: SetTempo +// +//========================================================================== + +int PsuedoMIDIDevice::SetTempo(int tempo) +{ + return 0; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: SetTimeDiv +// +//========================================================================== + +int PsuedoMIDIDevice::SetTimeDiv(int timediv) +{ + return 0; +} + +//========================================================================== +// +// PsuedoMIDIDevice :: GetStats +// +//========================================================================== + +FString PsuedoMIDIDevice::GetStats() +{ + if (Stream != NULL) + { + return Stream->GetStats(); + } + return "Psuedo MIDI device not open"; +} + + +//========================================================================== +// +// FMODMIDIDevice :: Open +// +//========================================================================== + +int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + return 0; +} + +//========================================================================== +// +// FMODMIDIDevice :: Preprocess +// +// Create a standard MIDI file and stream it. +// +//========================================================================== + +bool FMODMIDIDevice::Preprocess(MIDIStreamer *song) +{ + TArray midi; + + song->CreateSMF(midi); + Stream = GSnd->OpenStream((char *)&midi[0], SoundStream::Loop, -1, midi.Size()); + return false; +} diff --git a/src/sound/music_softsynth_mididevice.cpp b/src/sound/music_softsynth_mididevice.cpp index 9d2e69298..5cb21687a 100644 --- a/src/sound/music_softsynth_mididevice.cpp +++ b/src/sound/music_softsynth_mididevice.cpp @@ -1,6 +1,6 @@ /* ** music_softsynth_mididevice.cpp -** Common base clase for software synthesis MIDI devices. +** Common base class for software synthesis MIDI devices. ** **--------------------------------------------------------------------------- ** Copyright 2008-2010 Randy Heit @@ -269,56 +269,6 @@ int SoftSynthMIDIDevice::StreamOut(MIDIHDR *header) return 0; } -//========================================================================== -// -// SoftSynthMIDIDevice :: PrepareHeader -// -//========================================================================== - -int SoftSynthMIDIDevice::PrepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// SoftSynthMIDIDevice :: UnprepareHeader -// -//========================================================================== - -int SoftSynthMIDIDevice::UnprepareHeader(MIDIHDR *header) -{ - return 0; -} - -//========================================================================== -// -// SoftSynthMIDIDevice :: FakeVolume -// -// Since the softsynth output is rendered as a normal stream, its volume is -// controlled through the GSnd interface, not here. -// -//========================================================================== - -bool SoftSynthMIDIDevice::FakeVolume() -{ - return false; -} - -//========================================================================== -// -// SoftSynthMIDIDevice :: NeedThreadedCallabck -// -// We can service the callback directly rather than using a separate -// thread. -// -//========================================================================== - -bool SoftSynthMIDIDevice::NeedThreadedCallback() -{ - return false; -} - //========================================================================== // // SoftSynthMIDIDevice :: Pause diff --git a/zdoom.vcproj b/zdoom.vcproj index 8e6c9b9ca..9986f70b9 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5497,6 +5497,10 @@ RelativePath=".\src\oplsynth\music_opldumper_mididevice.cpp" > + + From fa429ad8445837f2641a21f26232bc333e51f05a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Oct 2010 02:23:42 +0000 Subject: [PATCH 083/815] - Fix incorrect parameters to PrintMidiDevice(). - Add music_psuedo_mididevice.cpp to CMakeLists.txt. SVN r2873 (trunk) --- src/CMakeLists.txt | 1 + src/sound/music_midi_base.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64250ca64..40f5772e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -838,6 +838,7 @@ add_executable( zdoom WIN32 sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp + sound/music_psuedo_mididevice.cpp textures/automaptexture.cpp textures/bitmap.cpp textures/buildtexture.cpp diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 19bd7a232..dc8145d75 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -165,10 +165,10 @@ CCMD (snd_listmididevices) #ifdef HAVE_FLUIDSYNTH PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0); #endif - PrintMidiDevice (-4, "GUS", 0, MOD_SWSYNTH); + PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MOD_SWSYNTH, 0); PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0); - PrintMidiDevice (-2, "TiMidity++", 0, MOD_SWSYNTH); - PrintMidiDevice (-1, "FMOD", 0, MOD_SWSYNTH); + PrintMidiDevice (-2, "TiMidity++", MOD_SWSYNTH, 0); + PrintMidiDevice (-1, "FMOD", MOD_SWSYNTH, 0); if (nummididevices != 0) { for (id = 0; id < nummididevices; ++id) @@ -208,7 +208,7 @@ CCMD (snd_listmididevices) #ifdef HAVE_FLUIDSYNTH Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #endif - Printf("%s-4. GUS\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : ""); + Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); From 90dd40c58ffb01a510fca82eeba859d8eecdb777 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Oct 2010 03:36:19 +0000 Subject: [PATCH 084/815] - Reimplemented TiMidity++ playback so that it also can handle XMI subsongs. SVN r2874 (trunk) --- src/sound/i_music.cpp | 7 +- src/sound/i_musicinterns.h | 89 ++--- src/sound/music_midi_timidity.cpp | 452 +++++++++++--------------- src/sound/music_midistream.cpp | 8 +- src/sound/music_psuedo_mididevice.cpp | 8 +- 5 files changed, 252 insertions(+), 312 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 0ae9af0cf..40fa7f46b 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -326,12 +326,7 @@ static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, E static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype) { - if (devtype == MIDI_Timidity) - { - assert(miditype == MIDI_MIDI); - return new TimiditySong(file, musiccache, len); - } - else if (devtype >= MIDI_Null) + if (devtype >= MIDI_Null) { assert(miditype == MIDI_MIDI); if (musiccache != NULL) diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 6c43cd904..be5a70125 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -100,7 +100,7 @@ public: virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingStr(const char *setting, const char *value); - virtual bool Preprocess(MIDIStreamer *song); + virtual bool Preprocess(MIDIStreamer *song, bool looping); virtual FString GetStats(); }; @@ -165,8 +165,7 @@ public: protected: SoundStream *Stream; bool Started; - int SampleRate; - + bool bLooping; }; // FMOD psuedo-MIDI device -------------------------------------------------- @@ -175,9 +174,51 @@ class FMODMIDIDevice : public PsuedoMIDIDevice { public: int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); - bool Preprocess(MIDIStreamer *song); + bool Preprocess(MIDIStreamer *song, bool looping); }; +// MIDI file played with TiMidity++ and possibly streamed through FMOD ------ + +class TimidityPPMIDIDevice : public PsuedoMIDIDevice +{ +public: + TimidityPPMIDIDevice(); + ~TimidityPPMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + bool Preprocess(MIDIStreamer *song, bool looping); + bool IsOpen() const; + int Resume(); + + void Stop(); + bool IsOpen(); + void TimidityVolumeChanged(); + +protected: + bool LaunchTimidity(); + + FTempFileName DiskName; +#ifdef _WIN32 + HANDLE ReadWavePipe; + HANDLE WriteWavePipe; + HANDLE KillerEvent; + HANDLE ChildProcess; + bool Validated; + bool ValidateTimidity(); +#else // _WIN32 + int WavePipe[2]; + pid_t ChildProcess; +#endif + FString CommandLine; + size_t LoopPos; + + static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata); +#ifdef _WIN32 + static const char EventName[]; +#endif +}; + + // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -371,10 +412,10 @@ enum EMIDIDevice MIDI_GUS, MIDI_Fluid, MIDI_FMOD, + MIDI_Timidity, // only used by I_RegisterSong MIDI_Null, - MIDI_Timidity }; class MIDIStreamer : public MusInfo @@ -651,44 +692,6 @@ protected: SoundStream *m_Stream; }; -// MIDI file played with Timidity and possibly streamed through FMOD -------- - -class TimiditySong : public StreamSong -{ -public: - TimiditySong (FILE *file, BYTE *musiccache, int length); - ~TimiditySong (); - void Play (bool looping, int subsong); - void Stop (); - bool IsPlaying (); - bool IsValid () const { return CommandLine.Len() > 0; } - void TimidityVolumeChanged(); - -protected: - void PrepTimidity (); - bool LaunchTimidity (); - - FTempFileName DiskName; -#ifdef _WIN32 - HANDLE ReadWavePipe; - HANDLE WriteWavePipe; - HANDLE KillerEvent; - HANDLE ChildProcess; - bool Validated; - bool ValidateTimidity (); -#else // _WIN32 - int WavePipe[2]; - pid_t ChildProcess; -#endif - FString CommandLine; - size_t LoopPos; - - static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata); -#ifdef _WIN32 - static const char EventName[]; -#endif -}; - // MUS file played by a software OPL2 synth and streamed through FMOD ------- class OPLMUSSong : public StreamSong diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 6e2acb32d..28a77eada 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -3,31 +3,6 @@ #include "cmdlib.h" #include "templates.h" -#if !defined(_WIN32) && 0 -// Under Linux, buffer output from Timidity to try to avoid "bubbles" -// in the sound output. -class FPipeBuffer -{ -public: - FPipeBuffer::FPipeBuffer (int fragSize, int nFrags, int pipe); - ~FPipeBuffer (); - - int ReadFrag (BYTE *Buf); - -private: - int PipeHandle; - int FragSize; - int BuffSize; - int WritePos, ReadPos; - BYTE *Buffer; - bool GotFull; - SDL_mutex *BufferMutex; - SDL_thread *Reader; - - static int ThreadProc (void *data); -}; -#endif - #ifndef _WIN32 #include @@ -45,7 +20,7 @@ void ChildSigHandler (int signum) #endif #ifdef _WIN32 -const char TimiditySong::EventName[] = "TiMidity Killer"; +const char TimidityPPMIDIDevice::EventName[] = "TiMidity Killer"; static char TimidityTitle[] = "TiMidity (ZDoom Launched)"; CVAR (String, timidity_exe, "timidity.exe", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -58,6 +33,7 @@ CVAR (String, timidity_reverb, "0", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, timidity_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, timidity_8bit, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, timidity_byteswap, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + // added because Timidity's output is rather loud. CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -69,8 +45,7 @@ CUSTOM_CVAR (Float, timidity_mastervolume, 1.0f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) currSong->TimidityVolumeChanged(); } - -CUSTOM_CVAR (Int, timidity_pipe, 60, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Int, timidity_pipe, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { // pipe size in ms if (timidity_pipe < 0) { // a negative size makes no sense @@ -86,64 +61,42 @@ CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) self = 65000; } -void TimiditySong::Play (bool looping, int subsong) -{ - m_Status = STATE_Stopped; - m_Looping = looping; +//========================================================================== +// +// TimidityPPMIDIDevice Constructor +// +//========================================================================== - if (LaunchTimidity ()) - { - if (m_Stream != NULL) - { - if (m_Stream->Play (true, timidity_mastervolume)) - { - m_Status = STATE_Playing; - } - } - else - { // Assume success if not mixing with FMOD - m_Status = STATE_Playing; - } - } -} - -void TimiditySong::Stop () -{ - if (m_Status != STATE_Stopped) - { - if (m_Stream != NULL) - { - m_Stream->Stop (); - } +TimidityPPMIDIDevice::TimidityPPMIDIDevice() + : DiskName("zmid"), #ifdef _WIN32 - if (ChildProcess != INVALID_HANDLE_VALUE) - { - SetEvent (KillerEvent); - if (WaitForSingleObject (ChildProcess, 500) != WAIT_OBJECT_0) - { - TerminateProcess (ChildProcess, 666); - } - CloseHandle (ChildProcess); - ChildProcess = INVALID_HANDLE_VALUE; - } + ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), + KillerEvent(INVALID_HANDLE_VALUE), + ChildProcess(INVALID_HANDLE_VALUE), + Validated(false) #else - if (ChildProcess != -1) - { - if (kill (ChildProcess, SIGTERM) != 0) - { - kill (ChildProcess, SIGKILL); - } - waitpid (ChildProcess, NULL, 0); - ChildProcess = -1; - } + ChildProcess(-1) #endif +{ +#ifndef _WIN32 + WavePipe[0] = WavePipe[1] = -1; +#endif + + if (DiskName == NULL) + { + Printf(PRINT_BOLD, "Could not create temp music file\n"); + return; } - m_Status = STATE_Stopped; } -TimiditySong::~TimiditySong () +//========================================================================== +// +// TimidityPPMIDIDevice Destructor +// +//========================================================================== + +TimidityPPMIDIDevice::~TimidityPPMIDIDevice () { - Stop (); #if _WIN32 if (WriteWavePipe != INVALID_HANDLE_VALUE) { @@ -174,68 +127,53 @@ TimiditySong::~TimiditySong () #endif } -TimiditySong::TimiditySong (FILE *file, BYTE *musiccache, int len) - : DiskName ("zmid"), -#ifdef _WIN32 - ReadWavePipe (INVALID_HANDLE_VALUE), WriteWavePipe (INVALID_HANDLE_VALUE), - KillerEvent (INVALID_HANDLE_VALUE), - ChildProcess (INVALID_HANDLE_VALUE), - Validated (false) -#else - ChildProcess (-1) -#endif +//========================================================================== +// +// TimidityPPMIDIDevice :: Preprocess +// +//========================================================================== + +bool TimidityPPMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) { + TArray midi; bool success; FILE *f; -#ifndef _WIN32 - WavePipe[0] = WavePipe[1] = -1; -#endif - - if (DiskName == NULL) + if (CommandLine.IsEmpty()) { - Printf (PRINT_BOLD, "Could not create temp music file\n"); - return; + return false; } - f = fopen (DiskName, "wb"); + // Tell TiMidity++ whether it should loop or not + CommandLine.LockBuffer()[LoopPos] = looping ? 'l' : ' '; + CommandLine.UnlockBuffer(); + + // Write MIDI song to temporary file + song->CreateSMF(midi); + + f = fopen(DiskName, "wb"); if (f == NULL) { - Printf (PRINT_BOLD, "Could not open temp music file\n"); - return; + Printf(PRINT_BOLD, "Could not open temp music file\n"); + return false; } - - BYTE *buf; - - if (file != NULL) - { - buf = new BYTE[len]; - fread (buf, 1, len, file); - } - else - { - buf = musiccache; - } - - // Write to temporary file - success = (fwrite (buf, 1, len, f) == (size_t)len); + success = (fwrite(&midi[0], 1, midi.Size(), f) == (size_t)midi.Size()); fclose (f); - if (file != NULL) - { - delete[] buf; - } - if (success) + if (!success) { - PrepTimidity (); - } - else - { - Printf (PRINT_BOLD, "Could not write temp music file\n"); + Printf(PRINT_BOLD, "Could not write temp music file\n"); } + return false; } -void TimiditySong::PrepTimidity () +//========================================================================== +// +// TimidityPPMIDIDevice :: Open +// +//========================================================================== + +int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) { int pipeSize; @@ -244,19 +182,19 @@ void TimiditySong::PrepTimidity () if (!Validated && !ValidateTimidity ()) { - return; + return 101; } Validated = true; - KillerEvent = CreateEvent (NULL, FALSE, FALSE, EventName); + KillerEvent = CreateEvent(NULL, FALSE, FALSE, EventName); if (KillerEvent == INVALID_HANDLE_VALUE) { - Printf (PRINT_BOLD, "Could not create TiMidity++ kill event.\n"); - return; + Printf(PRINT_BOLD, "Could not create TiMidity++ kill event.\n"); + return 102; } #endif // WIN32 - CommandLine.Format ("%s %s -EFchorus=%s -EFreverb=%s -s%d ", + CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", *timidity_exe, *timidity_extargs, *timidity_chorus, *timidity_reverb, *timidity_frequency); @@ -274,31 +212,31 @@ void TimiditySong::PrepTimidity () bitmask <<= 1; pipeSize = bitmask; - if (!CreatePipe (&ReadWavePipe, &WriteWavePipe, &inheritable, pipeSize)) + if (!CreatePipe(&ReadWavePipe, &WriteWavePipe, &inheritable, pipeSize)) #else // WIN32 if (pipe (WavePipe) == -1) #endif { - Printf (PRINT_BOLD, "Could not create a data pipe for TiMidity++.\n"); + Printf(PRINT_BOLD, "Could not create a data pipe for TiMidity++.\n"); pipeSize = 0; } else { - m_Stream = GSnd->CreateStream (FillStream, pipeSize, + Stream = GSnd->CreateStream(FillStream, pipeSize, (timidity_stereo ? 0 : SoundStream::Mono) | (timidity_8bit ? SoundStream::Bits8 : 0), timidity_frequency, this); - if (m_Stream == NULL) + if (Stream == NULL) { - Printf (PRINT_BOLD, "Could not create music stream.\n"); + Printf(PRINT_BOLD, "Could not create music stream.\n"); pipeSize = 0; #ifdef _WIN32 - CloseHandle (WriteWavePipe); - CloseHandle (ReadWavePipe); + CloseHandle(WriteWavePipe); + CloseHandle(ReadWavePipe); ReadWavePipe = WriteWavePipe = INVALID_HANDLE_VALUE; #else - close (WavePipe[1]); - close (WavePipe[0]); + close(WavePipe[1]); + close(WavePipe[0]); WavePipe[0] = WavePipe[1] = -1; #endif } @@ -306,7 +244,7 @@ void TimiditySong::PrepTimidity () if (pipeSize == 0) { - Printf (PRINT_BOLD, "If your soundcard cannot play more than one\n" + Printf(PRINT_BOLD, "If your soundcard cannot play more than one\n" "wave at a time, you will hear no music.\n"); } else @@ -331,14 +269,22 @@ void TimiditySong::PrepTimidity () CommandLine += " -idl "; CommandLine += DiskName.GetName(); + return 0; } -#ifdef _WIN32 +//========================================================================== +// +// TimidityPPMIDIDevice :: ValidateTimidity +// // Check that this TiMidity++ knows about the TiMidity Killer event. // If not, then we can't use it, because Win32 provides no other way // to conveniently signal it to quit. The check is done by simply // searching for the event's name somewhere in the executable. -bool TimiditySong::ValidateTimidity () +// +//========================================================================== + +#ifdef _WIN32 +bool TimidityPPMIDIDevice::ValidateTimidity() { char foundPath[MAX_PATH]; char *filePart; @@ -354,12 +300,12 @@ bool TimiditySong::ValidateTimidity () pathLen = SearchPath (NULL, timidity_exe, NULL, MAX_PATH, foundPath, &filePart); if (pathLen == 0) { - Printf (PRINT_BOLD, "Please set the timidity_exe cvar to the location of TiMidity++\n"); + Printf(PRINT_BOLD, "Please set the timidity_exe cvar to the location of TiMidity++\n"); return false; } if (pathLen > MAX_PATH) { - Printf (PRINT_BOLD, "The path to TiMidity++ is too long\n"); + Printf(PRINT_BOLD, "The path to TiMidity++ is too long\n"); return false; } @@ -367,21 +313,21 @@ bool TimiditySong::ValidateTimidity () OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (diskFile == INVALID_HANDLE_VALUE) { - Printf (PRINT_BOLD, "Could not access %s\n", foundPath); + Printf(PRINT_BOLD, "Could not access %s\n", foundPath); return false; } fileLen = GetFileSize (diskFile, NULL); mapping = CreateFileMapping (diskFile, NULL, PAGE_READONLY, 0, 0, NULL); if (mapping == NULL) { - Printf (PRINT_BOLD, "Could not create mapping for %s\n", foundPath); + Printf(PRINT_BOLD, "Could not create mapping for %s\n", foundPath); CloseHandle (diskFile); return false; } exeBase = (const BYTE *)MapViewOfFile (mapping, FILE_MAP_READ, 0, 0, 0); if (exeBase == NULL) { - Printf (PRINT_BOLD, "Could not map %s\n", foundPath); + Printf(PRINT_BOLD, "Could not map %s\n", foundPath); CloseHandle (mapping); CloseHandle (diskFile); return false; @@ -392,12 +338,12 @@ bool TimiditySong::ValidateTimidity () { for (exe = exeBase, exeEnd = exeBase+fileLen; exe < exeEnd; ) { - const char *tSpot = (const char *)memchr (exe, 'T', exeEnd - exe); + const char *tSpot = (const char *)memchr(exe, 'T', exeEnd - exe); if (tSpot == NULL) { break; } - if (memcmp (tSpot+1, EventName+1, sizeof(EventName)-1) == 0) + if (memcmp(tSpot+1, EventName+1, sizeof(EventName)-1) == 0) { good = true; break; @@ -407,32 +353,35 @@ bool TimiditySong::ValidateTimidity () } catch (...) { - Printf (PRINT_BOLD, "Error reading %s\n", foundPath); + Printf(PRINT_BOLD, "Error reading %s\n", foundPath); } if (!good) { - Printf (PRINT_BOLD, "ZDoom requires a special version of TiMidity++\n"); + Printf(PRINT_BOLD, "ZDoom requires a special version of TiMidity++\n"); } - UnmapViewOfFile ((LPVOID)exeBase); - CloseHandle (mapping); - CloseHandle (diskFile); + UnmapViewOfFile((LPVOID)exeBase); + CloseHandle(mapping); + CloseHandle(diskFile); return good; } #endif // _WIN32 -bool TimiditySong::LaunchTimidity () +//========================================================================== +// +// TimidityPPMIDIDevice :: LaunchTimidity +// +//========================================================================== + +bool TimidityPPMIDIDevice::LaunchTimidity () { if (CommandLine.IsEmpty()) { return false; } - // Tell Timidity whether it should loop or not - char *cmdline = CommandLine.LockBuffer(); - cmdline[LoopPos] = m_Looping ? 'l' : ' '; - DPrintf ("cmd: \x1cG%s\n", cmdline); + DPrintf ("cmd: \x1cG%s\n", CommandLine.GetChars()); #ifdef _WIN32 STARTUPINFO startup = { sizeof(startup), }; @@ -448,7 +397,7 @@ bool TimiditySong::LaunchTimidity () startup.lpTitle = TimidityTitle; startup.wShowWindow = SW_SHOWMINNOACTIVE; - if (CreateProcess (NULL, cmdline, NULL, NULL, TRUE, + if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, TRUE, /*HIGH_PRIORITY_CLASS|*/DETACHED_PROCESS, NULL, NULL, &startup, &procInfo)) { ChildProcess = procInfo.hProcess; @@ -461,18 +410,18 @@ bool TimiditySong::LaunchTimidity () char hres[9]; LPTSTR msgBuf; - HRESULT err = GetLastError (); + HRESULT err = GetLastError(); if (!FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPTSTR)&msgBuf, 0, NULL)) { - mysnprintf (hres, countof(hres), "%08lx", err); + mysnprintf(hres, countof(hres), "%08lx", err); msgBuf = hres; } - Printf (PRINT_BOLD, "Could not run timidity with the command line:\n%s\n" + Printf(PRINT_BOLD, "Could not run timidity with the command line:\n%s\n" "Reason: %s\n", CommandLine.GetChars(), msgBuf); if (msgBuf != hres) { @@ -543,14 +492,20 @@ bool TimiditySong::LaunchTimidity () #endif // _WIN32 } -bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *userdata) +//========================================================================== +// +// TimidityPPMIDIDevice :: FillStream +// +//========================================================================== + +bool TimidityPPMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata) { - TimiditySong *song = (TimiditySong *)userdata; + TimidityPPMIDIDevice *song = (TimidityPPMIDIDevice *)userdata; #ifdef _WIN32 DWORD avail, got, didget; - if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0) + if (!PeekNamedPipe(song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0) { // If nothing is available from the pipe, play silence. memset (buff, 0, len); } @@ -559,14 +514,14 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u didget = 0; for (;;) { - ReadFile (song->ReadWavePipe, (BYTE *)buff+didget, len-didget, &got, NULL); + ReadFile(song->ReadWavePipe, (BYTE *)buff+didget, len-didget, &got, NULL); didget += got; if (didget >= (DWORD)len) break; - // Give TiMidity a chance to output something more to the pipe + // Give TiMidity++ a chance to output something more to the pipe Sleep (10); - if (!PeekNamedPipe (song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0) + if (!PeekNamedPipe(song->ReadWavePipe, NULL, 0, NULL, &avail, NULL) || avail == 0) { memset ((BYTE *)buff+didget, 0, len-didget); break; @@ -581,7 +536,7 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u if (ChildQuit == song->ChildProcess) { ChildQuit = 0; - fprintf (stderr, "child gone\n"); + fprintf(stderr, "child gone\n"); song->ChildProcess = -1; return false; } @@ -599,32 +554,44 @@ bool TimiditySong::FillStream (SoundStream *stream, void *buff, int len, void *u } // fprintf(stderr,"something\n"); - got = read (song->WavePipe[0], (BYTE *)buff, len); + got = read(song->WavePipe[0], (BYTE *)buff, len); if (got < len) { - memset ((BYTE *)buff+got, 0, len-got); + memset((BYTE *)buff+got, 0, len-got); } #endif return true; } -void TimiditySong::TimidityVolumeChanged() +//========================================================================== +// +// TimidityPPMIDIDevice :: TimidityVolumeChanged +// +//========================================================================== + +void TimidityPPMIDIDevice::TimidityVolumeChanged() { - if (m_Stream != NULL) + if (Stream != NULL) { - m_Stream->SetVolume(timidity_mastervolume); + Stream->SetVolume(timidity_mastervolume); } } -bool TimiditySong::IsPlaying () +//========================================================================== +// +// TimidityPPMIDIDevice :: IsOpen +// +//========================================================================== + +bool TimidityPPMIDIDevice::IsOpen() const { #ifdef _WIN32 if (ChildProcess != INVALID_HANDLE_VALUE) { - if (WaitForSingleObject (ChildProcess, 0) != WAIT_TIMEOUT) - { // Timidity has quit - CloseHandle (ChildProcess); - ChildProcess = INVALID_HANDLE_VALUE; + if (WaitForSingleObject(ChildProcess, 0) != WAIT_TIMEOUT) + { // Timidity++ has quit + CloseHandle(ChildProcess); + const_cast(this)->ChildProcess = INVALID_HANDLE_VALUE; #else if (ChildProcess != -1) { @@ -632,15 +599,6 @@ bool TimiditySong::IsPlaying () { ChildProcess = -1; #endif - if (m_Looping) - { - if (!LaunchTimidity ()) - { - Stop (); - return false; - } - return true; - } return false; } return true; @@ -648,86 +606,66 @@ bool TimiditySong::IsPlaying () return false; } -#if !defined(_WIN32) && 0 -FPipeBuffer::FPipeBuffer (int fragSize, int nFrags, int pipe) - : PipeHandle (pipe), - FragSize (fragSize), - BuffSize (fragSize * nFrags), - WritePos (0), ReadPos (0), GotFull (false), - Reader (0), PleaseExit (0) +//========================================================================== +// +// TimidityPPMIDIDevice :: Resume +// +//========================================================================== + +int TimidityPPMIDIDevice::Resume() { - Buffer = new BYTE[BuffSize]; - if (Buffer != NULL) + if (!Started) { - BufferMutex = SDL_CreateMutex (); - if (BufferMutex == NULL) + if (LaunchTimidity()) { - Reader = SDL_CreateThread (ThreadProc, (void *)this); + // Assume success if not mixing with FMOD + if (Stream == NULL || Stream->Play(true, timidity_mastervolume)) + { + Started = true; + return 0; + } } + return 1; } + return 0; } -FPipeBuffer::~FPipeBuffer () -{ - if (Reader != NULL) - { // I like the Win32 IPC facilities better than SDL's - // Fortunately, this is a simple thread, so I can cheat - // like this. - SDL_KillThread (ThreadProc); - } - if (BufferMutex != NULL) - { - SDL_DestroyMutex (BufferMutex); - } - if (Buffer != NULL) - { - delete[] Buffer; - } -} +//========================================================================== +// +// TimidityPPMIDIDevice :: Stop +// +//========================================================================== -int FPipeBuffer::ReadFrag (BYTE *buf) +void TimidityPPMIDIDevice::Stop () { - int startavvail; - int avail; - int pos; - int opos; - - if (SDL_mutexP (BufferMutex) == -1) - return 0; - - if (WritePos > ReadPos) + if (Started) { - avail = WritePos - ReadPos; - } - else - { - avail = BuffSize - ReadPos + WritePos; - } - if (avail > FragSize) - avail = FragSize; - - startavail = avali; - pos = ReadPos; - opos = 0; - - while (avail != 0) - { - int thistime; - - thistime = (pos + avail > BuffSize) ? BuffSize - pos : avail; - memcpy (buf + opos, Buffer + pos, thistime); - if (thistime != avail) + if (Stream != NULL) { - pos = 0; - avail -= thistime; + Stream->Stop(); } - opos += thistime; +#ifdef _WIN32 + if (ChildProcess != INVALID_HANDLE_VALUE) + { + SetEvent(KillerEvent); + if (WaitForSingleObject(ChildProcess, 500) != WAIT_OBJECT_0) + { + TerminateProcess(ChildProcess, 666); + } + CloseHandle(ChildProcess); + ChildProcess = INVALID_HANDLE_VALUE; + } +#else + if (ChildProcess != -1) + { + if (kill(ChildProcess, SIGTERM) != 0) + { + kill(ChildProcess, SIGKILL); + } + waitpid(ChildProcess, NULL, 0); + ChildProcess = -1; + } +#endif } - - ReadPos = pos; - - SDL_mutexV (BufferMutex); - - return startavail; + Started = false; } -#endif // !_WIN32 diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index aa1e3feab..473a9ef75 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -251,6 +251,10 @@ void MIDIStreamer::Play(bool looping, int subsong) MIDI = new OPLMIDIDevice; break; + case MIDI_Timidity: + MIDI = new TimidityPPMIDIDevice; + break; + default: MIDI = NULL; break; @@ -269,7 +273,7 @@ void MIDIStreamer::Play(bool looping, int subsong) SetMIDISubsong(subsong); CheckCaps(MIDI->GetTechnology()); - if (MIDI->Preprocess(this)) + if (MIDI->Preprocess(this, looping)) { StartPlayback(); } @@ -1259,7 +1263,7 @@ void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count) // //========================================================================== -bool MIDIDevice::Preprocess(MIDIStreamer *song) +bool MIDIDevice::Preprocess(MIDIStreamer *song, bool looping) { return true; } diff --git a/src/sound/music_psuedo_mididevice.cpp b/src/sound/music_psuedo_mididevice.cpp index 11ddea63e..ec72794e3 100644 --- a/src/sound/music_psuedo_mididevice.cpp +++ b/src/sound/music_psuedo_mididevice.cpp @@ -67,7 +67,7 @@ PsuedoMIDIDevice::PsuedoMIDIDevice() { Stream = NULL; Started = false; - SampleRate = GSnd != NULL ? (int)GSnd->GetOutputRate() : 44100; + bLooping = true; } //========================================================================== @@ -129,7 +129,7 @@ int PsuedoMIDIDevice::Resume() { if (!Started) { - if (Stream->Play(true, 1)) + if (Stream->Play(bLooping, 1)) { Started = true; return 0; @@ -250,11 +250,11 @@ int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), v // //========================================================================== -bool FMODMIDIDevice::Preprocess(MIDIStreamer *song) +bool FMODMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) { TArray midi; song->CreateSMF(midi); - Stream = GSnd->OpenStream((char *)&midi[0], SoundStream::Loop, -1, midi.Size()); + Stream = GSnd->OpenStream((char *)&midi[0], looping ? SoundStream::Loop : 0, -1, midi.Size()); return false; } From b2b84ad11f5e1ccafec98857fd02bf80d126613b Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 2 Oct 2010 04:12:21 +0000 Subject: [PATCH 085/815] - Fixed snd_mididevice so that changing it immediately restarts the song for all devices, not just Windows system devices. - Remove SMF generation from i_music.cpp, since the psuedo-MIDI devices do this now. SVN r2875 (trunk) --- src/sound/i_music.cpp | 123 +++------------------------- src/sound/i_music.h | 2 +- src/sound/i_musicinterns.h | 43 ++++------ src/sound/music_hmi_midiout.cpp | 8 +- src/sound/music_midi_base.cpp | 45 +++++------ src/sound/music_midistream.cpp | 139 +++++++++++++++++++++++--------- src/sound/music_mus_midiout.cpp | 8 +- src/sound/music_smf_midiout.cpp | 8 +- src/sound/music_xmi_midiout.cpp | 8 +- 9 files changed, 167 insertions(+), 217 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 40fa7f46b..ba98b470d 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -247,6 +247,11 @@ bool MusInfo::SetPosition (unsigned int ms) return false; } +bool MusInfo::IsMIDI() const +{ + return false; +} + bool MusInfo::SetSubsong (int subsong) { return false; @@ -297,7 +302,7 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) // //========================================================================== -static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, EMIDIDevice devtype, EMIDIType miditype) +static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, EMidiDevice devtype, EMIDIType miditype) { switch (miditype) { @@ -318,29 +323,6 @@ static MIDIStreamer *CreateMIDIStreamer(FILE *file, BYTE *musiccache, int len, E } } -//========================================================================== -// -// create a MIDI player -// -//========================================================================== - -static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype) -{ - if (devtype >= MIDI_Null) - { - assert(miditype == MIDI_MIDI); - if (musiccache != NULL) - { - return new StreamSong((char *)musiccache, -1, len); - } - else - { - return new StreamSong(filename, offset, len); - } - } - else return CreateMIDIStreamer(file, musiccache, len, devtype, miditype); -} - //========================================================================== // // identify MIDI file type @@ -390,66 +372,6 @@ static EMIDIType IdentifyMIDIType(DWORD *id, int size) } } -//========================================================================== -// -// select the MIDI device to play on -// -//========================================================================== - -static EMIDIDevice SelectMIDIDevice(int device) -{ - /* MIDI are played as: - - OPL: - - if explicitly selected by $mididevice - - when snd_mididevice is -3 and no midi device is set for the song - - - Timidity: - - if explicitly selected by $mididevice - - when snd_mididevice is -2 and no midi device is set for the song - - - FMod: - - if explicitly selected by $mididevice - - when snd_mididevice is -1 and no midi device is set for the song - - as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0 - - - MMAPI (Win32 only): - - if explicitly selected by $mididevice (non-Win32 redirects this to FMOD) - - when snd_mididevice is >= 0 and no midi device is set for the song - - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 - */ - EMIDIDevice devtype = MIDI_Null; - - // Choose the type of MIDI device we want. - if (device == MDEV_FMOD || (snd_mididevice == -1 && device == MDEV_DEFAULT)) - { - return MIDI_FMOD; - } - else if (device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) - { - return MIDI_Timidity; - } - else if (device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) - { - return MIDI_OPL; - } - else if (device == MDEV_GUS || (snd_mididevice == -4 && device == MDEV_DEFAULT)) - { - return MIDI_GUS; - } - #ifdef HAVE_FLUIDSYNTH - else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT)) - { - return MIDI_Fluid; - } - #endif - #ifdef _WIN32 - return MIDI_Win; - #else - return MIDI_Null; - #endif -} - - //========================================================================== // // identify a music lump's type and set up a player for it @@ -557,45 +479,24 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int { TArray midi; - EMIDIDevice devtype = SelectMIDIDevice(device); + EMidiDevice devtype = (EMidiDevice)device; retry_as_fmod: - if (devtype >= MIDI_Null) - { - // Convert to standard MIDI for external sequencers. - MIDIStreamer *streamer = CreateMIDIStreamer(file, musiccache, len, MIDI_Null, miditype); - if (streamer != NULL) - { - if (streamer->IsValid()) - { - streamer->CreateSMF(midi); - miditype = MIDI_MIDI; - musiccache = &midi[0]; - len = midi.Size(); - if (file != NULL) - { - fclose(file); - file = NULL; - } - } - delete streamer; - } - } - info = CreateMIDISong(file, filename, musiccache, offset, len, devtype, miditype); + info = CreateMIDIStreamer(file, musiccache, len, devtype, miditype); if (info != NULL && !info->IsValid()) { delete info; info = NULL; } - if (info == NULL && devtype != MIDI_FMOD && snd_mididevice < 0) + if (info == NULL && devtype != MDEV_FMOD && snd_mididevice < 0) { - devtype = MIDI_FMOD; + devtype = MDEV_FMOD; goto retry_as_fmod; } #ifdef _WIN32 - if (info == NULL && devtype != MIDI_Win && snd_mididevice >= 0) + if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0) { - info = CreateMIDISong(file, filename, musiccache, offset, len, MIDI_Win, miditype); + info = CreateMIDIStreamer(file, musiccache, len, MDEV_MMAPI, miditype); } #endif } diff --git a/src/sound/i_music.h b/src/sound/i_music.h index b620ae52f..17775ebe0 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -70,7 +70,7 @@ public: virtual void Resume () = 0; virtual void Stop () = 0; virtual bool IsPlaying () = 0; - virtual bool IsMIDI () const = 0; + virtual bool IsMIDI () const; virtual bool IsValid () const = 0; virtual bool SetPosition (unsigned int ms); virtual bool SetSubsong (int subsong); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index be5a70125..bf0bfaa14 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -23,6 +23,7 @@ #include "mus2midi.h" #include "i_sound.h" #include "i_music.h" +#include "s_sound.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -404,24 +405,10 @@ protected: // Base class for streaming MUS and MIDI files ------------------------------ -// MIDI device selection. -enum EMIDIDevice -{ - MIDI_Win, - MIDI_OPL, - MIDI_GUS, - MIDI_Fluid, - MIDI_FMOD, - MIDI_Timidity, - - // only used by I_RegisterSong - MIDI_Null, -}; - class MIDIStreamer : public MusInfo { public: - MIDIStreamer(EMIDIDevice type); + MIDIStreamer(EMidiDevice type); ~MIDIStreamer(); void MusicVolumeChanged(); @@ -442,7 +429,7 @@ public: void CreateSMF(TArray &file); protected: - MIDIStreamer(const char *dumpname, EMIDIDevice type); + MIDIStreamer(const char *dumpname, EMidiDevice type); void OutputVolume (DWORD volume); int FillBuffer(int buffer_num, int max_events, DWORD max_time); @@ -450,7 +437,9 @@ protected: int VolumeControllerChange(int channel, int volume); int ClampLoopCount(int loopcount); void SetTempo(int new_tempo); - + static EMidiDevice SelectMIDIDevice(EMidiDevice devtype); + MIDIDevice *CreateMIDIDevice(EMidiDevice devtype) const; + static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2); // Virtuals for subclasses to override @@ -498,7 +487,7 @@ protected: int InitialTempo; BYTE ChannelVolumes[16]; DWORD Volume; - EMIDIDevice DeviceType; + EMidiDevice DeviceType; bool CallbackIsThreaded; int LoopLimit; FString DumpFilename; @@ -509,14 +498,14 @@ protected: class MUSSong2 : public MIDIStreamer { public: - MUSSong2(FILE *file, BYTE *musiccache, int length, EMIDIDevice type); + MUSSong2(FILE *file, BYTE *musiccache, int length, EMidiDevice type); ~MUSSong2(); MusInfo *GetOPLDumper(const char *filename); MusInfo *GetWaveDumper(const char *filename, int rate); protected: - MUSSong2(const MUSSong2 *original, const char *filename, EMIDIDevice type); // file dump constructor + MUSSong2(const MUSSong2 *original, const char *filename, EMidiDevice type); // file dump constructor void DoInitialSetup(); void DoRestart(); @@ -535,14 +524,14 @@ protected: class MIDISong2 : public MIDIStreamer { public: - MIDISong2(FILE *file, BYTE *musiccache, int length, EMIDIDevice type); + MIDISong2(FILE *file, BYTE *musiccache, int length, EMidiDevice type); ~MIDISong2(); MusInfo *GetOPLDumper(const char *filename); MusInfo *GetWaveDumper(const char *filename, int rate); protected: - MIDISong2(const MIDISong2 *original, const char *filename, EMIDIDevice type); // file dump constructor + MIDISong2(const MIDISong2 *original, const char *filename, EMidiDevice type); // file dump constructor void CheckCaps(int tech); void DoInitialSetup(); @@ -592,14 +581,14 @@ protected: class HMISong : public MIDIStreamer { public: - HMISong(FILE *file, BYTE *musiccache, int length, EMIDIDevice type); + HMISong(FILE *file, BYTE *musiccache, int length, EMidiDevice type); ~HMISong(); MusInfo *GetOPLDumper(const char *filename); MusInfo *GetWaveDumper(const char *filename, int rate); protected: - HMISong(const HMISong *original, const char *filename, EMIDIDevice type); // file dump constructor + HMISong(const HMISong *original, const char *filename, EMidiDevice type); // file dump constructor void SetupForHMI(int len); void SetupForHMP(int len); @@ -634,7 +623,7 @@ protected: class XMISong : public MIDIStreamer { public: - XMISong(FILE *file, BYTE *musiccache, int length, EMIDIDevice type); + XMISong(FILE *file, BYTE *musiccache, int length, EMidiDevice type); ~XMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -644,7 +633,7 @@ protected: struct TrackInfo; enum EventSource { EVENT_None, EVENT_Real, EVENT_Fake }; - XMISong(const XMISong *original, const char *filename, EMIDIDevice type); // file dump constructor + XMISong(const XMISong *original, const char *filename, EMidiDevice type); // file dump constructor int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const; void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const; @@ -680,7 +669,6 @@ public: void Resume (); void Stop (); bool IsPlaying (); - bool IsMIDI () const { return false; } bool IsValid () const { return m_Stream != NULL; } bool SetPosition (unsigned int pos); bool SetSubsong (int subsong); @@ -732,7 +720,6 @@ public: void Resume (); void Stop (); bool IsPlaying (); - bool IsMIDI () const { return false; } bool IsValid () const { return m_Inited; } protected: diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index fd65fde05..09089d964 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -127,7 +127,7 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) +HMISong::HMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { #ifdef _WIN32 @@ -983,7 +983,7 @@ HMISong::TrackInfo *HMISong::FindNextDue () MusInfo *HMISong::GetOPLDumper(const char *filename) { - return new HMISong(this, filename, MIDI_OPL); + return new HMISong(this, filename, MDEV_OPL); } //========================================================================== @@ -994,7 +994,7 @@ MusInfo *HMISong::GetOPLDumper(const char *filename) MusInfo *HMISong::GetWaveDumper(const char *filename, int rate) { - return new HMISong(this, filename, MIDI_GUS); + return new HMISong(this, filename, MDEV_GUS); } //========================================================================== @@ -1003,7 +1003,7 @@ MusInfo *HMISong::GetWaveDumper(const char *filename, int rate) // //========================================================================== -HMISong::HMISong(const HMISong *original, const char *filename, EMIDIDevice type) +HMISong::HMISong(const HMISong *original, const char *filename, EMidiDevice type) : MIDIStreamer(filename, type) { SongLen = original->SongLen; diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index dc8145d75..86134c954 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -38,13 +38,28 @@ static void AddDefaultMidiDevices(FOptionValues *opt) } +static void MIDIDeviceChanged(int newdev) +{ + static int oldmididev = INT_MIN; + + // If a song is playing, move it to the new device. + if (oldmididev != newdev && currSong != NULL && currSong->IsMIDI()) + { + MusInfo *song = currSong; + if (song->m_Status == MusInfo::STATE_Playing) + { + song->Stop(); + song->Start(song->m_Looping); + } + } + oldmididev = newdev; +} + #ifdef _WIN32 - UINT mididevice; +UINT mididevice; CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { - UINT oldmididev = mididevice; - if (!nummididevicesset) return; @@ -54,26 +69,8 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) self = 0; return; } - else if (self < 0) - { - mididevice = 0; - } - else - { - mididevice = self; - } - - // If a song is playing, move it to the new device. - if (oldmididev != mididevice && currSong != NULL && currSong->IsMIDI()) - { - // Does this even work, except for Windows system devices? - MusInfo *song = currSong; - if (song->m_Status == MusInfo::STATE_Playing) - { - song->Stop(); - song->Start(song->m_Looping); - } - } + mididevice = MAX(0, self); + MIDIDeviceChanged(self); } void I_InitMusicWin32 () @@ -196,6 +193,8 @@ CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) self = -5; else if (self > -1) self = -1; + else + MIDIDeviceChanged(self); } void I_BuildMIDIMenuList (FOptionValues *opt) diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 473a9ef75..d9008fe0f 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -57,6 +57,7 @@ static void WriteVarLen (TArray &file, DWORD value); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- EXTERN_CVAR(Float, snd_musicvolume) +EXTERN_CVAR(Int, snd_mididevice) #ifdef _WIN32 extern UINT mididevice; @@ -87,7 +88,7 @@ static const BYTE StaticMIDIhead[] = // //========================================================================== -MIDIStreamer::MIDIStreamer(EMIDIDevice type) +MIDIStreamer::MIDIStreamer(EMidiDevice type) : #ifdef _WIN32 PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), @@ -115,7 +116,7 @@ MIDIStreamer::MIDIStreamer(EMIDIDevice type) // //========================================================================== -MIDIStreamer::MIDIStreamer(const char *dumpname, EMIDIDevice type) +MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type) : #ifdef _WIN32 PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), @@ -194,6 +195,98 @@ void MIDIStreamer::CheckCaps(int tech) { } +//========================================================================== +// +// MIDIStreamer :: SelectMIDIDevice static +// +// Select the MIDI device to play on +// +//========================================================================== + +EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) +{ + /* MIDI are played as: + - OPL: + - if explicitly selected by $mididevice + - when snd_mididevice is -3 and no midi device is set for the song + + - Timidity: + - if explicitly selected by $mididevice + - when snd_mididevice is -2 and no midi device is set for the song + + - FMod: + - if explicitly selected by $mididevice + - when snd_mididevice is -1 and no midi device is set for the song + - as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0 + + - MMAPI (Win32 only): + - if explicitly selected by $mididevice (non-Win32 redirects this to FMOD) + - when snd_mididevice is >= 0 and no midi device is set for the song + - as fallback when both OPL and Timidity failed and snd_mididevice is >= 0 + */ + + // Choose the type of MIDI device we want. + if (device != MDEV_DEFAULT) + { + return device; + } + switch (snd_mididevice) + { + case -1: return MDEV_FMOD; + case -2: return MDEV_TIMIDITY; + case -3: return MDEV_OPL; + case -4: return MDEV_GUS; +#ifdef HAVE_FLUIDSYNTH + case -5: return MDEV_FLUIDSYNTH; +#endif + default: + #ifdef _WIN32 + return MDEV_MMAPI; + #else + return MDEV_FMOD; + #endif + } +} + +//========================================================================== +// +// MIDIStreamer :: CreateMIDIDevice +// +//========================================================================== + +MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const +{ + switch (devtype) + { + case MDEV_MMAPI: +#ifdef _WIN32 + return new WinMIDIDevice(mididevice); +#endif + assert(0); + // Intentional fall-through for non-Windows systems. + +#ifdef HAVE_FLUIDSYNTH + case MDEV_FLUIDSYNTH: + return new FluidSynthMIDIDevice; +#endif + + case MDEV_FMOD: + return new FMODMIDIDevice; + + case MDEV_GUS: + return new TimidityMIDIDevice; + + case MDEV_OPL: + return new OPLMIDIDevice; + + case MDEV_TIMIDITY: + return new TimidityPPMIDIDevice; + + default: + return NULL; + } +} + //========================================================================== // // MIDIStreamer :: Play @@ -203,6 +296,7 @@ void MIDIStreamer::CheckCaps(int tech) void MIDIStreamer::Play(bool looping, int subsong) { DWORD tid; + EMidiDevice devtype; m_Status = STATE_Stopped; m_Looping = looping; @@ -212,52 +306,21 @@ void MIDIStreamer::Play(bool looping, int subsong) InitialPlayback = true; assert(MIDI == NULL); + devtype = SelectMIDIDevice(DeviceType); if (DumpFilename.IsNotEmpty()) { - if (DeviceType == MIDI_OPL) + if (devtype == MDEV_OPL) { MIDI = new OPLDumperMIDIDevice(DumpFilename); } - else if (DeviceType == MIDI_GUS) + else if (devtype == MDEV_GUS) { MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0); } } - else switch(DeviceType) + else { - case MIDI_Win: -#ifdef _WIN32 - MIDI = new WinMIDIDevice(mididevice); - break; -#endif - assert(0); - // Intentional fall-through for non-Windows systems. - -#ifdef HAVE_FLUIDSYNTH - case MIDI_Fluid: - MIDI = new FluidSynthMIDIDevice; - break; -#endif - - case MIDI_FMOD: - MIDI = new FMODMIDIDevice; - break; - - case MIDI_GUS: - MIDI = new TimidityMIDIDevice; - break; - - case MIDI_OPL: - MIDI = new OPLMIDIDevice; - break; - - case MIDI_Timidity: - MIDI = new TimidityPPMIDIDevice; - break; - - default: - MIDI = NULL; - break; + MIDI = CreateMIDIDevice(devtype); } #ifndef _WIN32 diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 44a0d45e6..f87754e5a 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -91,7 +91,7 @@ static const BYTE CtrlTranslate[15] = // //========================================================================== -MUSSong2::MUSSong2 (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) +MUSSong2::MUSSong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), MusBuffer(0) { #ifdef _WIN32 @@ -371,7 +371,7 @@ end: MusInfo *MUSSong2::GetOPLDumper(const char *filename) { - return new MUSSong2(this, filename, MIDI_OPL); + return new MUSSong2(this, filename, MDEV_OPL); } //========================================================================== @@ -382,7 +382,7 @@ MusInfo *MUSSong2::GetOPLDumper(const char *filename) MusInfo *MUSSong2::GetWaveDumper(const char *filename, int rate) { - return new MUSSong2(this, filename, MIDI_GUS); + return new MUSSong2(this, filename, MDEV_GUS); } //========================================================================== @@ -391,7 +391,7 @@ MusInfo *MUSSong2::GetWaveDumper(const char *filename, int rate) // //========================================================================== -MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename, EMIDIDevice type) +MUSSong2::MUSSong2(const MUSSong2 *original, const char *filename, EMidiDevice type) : MIDIStreamer(filename, type) { int songstart = LittleShort(original->MusHeader->SongStart); diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index 6e59c6087..6fd51f149 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -101,7 +101,7 @@ char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // //========================================================================== -MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) +MIDISong2::MIDISong2 (FILE *file, BYTE *musiccache, int len, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Tracks(0) { int p; @@ -782,7 +782,7 @@ MIDISong2::TrackInfo *MIDISong2::FindNextDue () MusInfo *MIDISong2::GetOPLDumper(const char *filename) { - return new MIDISong2(this, filename, MIDI_OPL); + return new MIDISong2(this, filename, MDEV_OPL); } //========================================================================== @@ -793,7 +793,7 @@ MusInfo *MIDISong2::GetOPLDumper(const char *filename) MusInfo *MIDISong2::GetWaveDumper(const char *filename, int rate) { - return new MIDISong2(this, filename, MIDI_GUS); + return new MIDISong2(this, filename, MDEV_GUS); } //========================================================================== @@ -802,7 +802,7 @@ MusInfo *MIDISong2::GetWaveDumper(const char *filename, int rate) // //========================================================================== -MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename, EMIDIDevice type) +MIDISong2::MIDISong2(const MIDISong2 *original, const char *filename, EMidiDevice type) : MIDIStreamer(filename, type) { SongLen = original->SongLen; diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index bece8eafa..8a5ad12e7 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -107,7 +107,7 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMIDIDevice type) +XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type) : MIDIStreamer(type), MusHeader(0), Songs(0) { #ifdef _WIN32 @@ -683,7 +683,7 @@ XMISong::EventSource XMISong::FindNextDue() MusInfo *XMISong::GetOPLDumper(const char *filename) { - return new XMISong(this, filename, MIDI_OPL); + return new XMISong(this, filename, MDEV_OPL); } //========================================================================== @@ -694,7 +694,7 @@ MusInfo *XMISong::GetOPLDumper(const char *filename) MusInfo *XMISong::GetWaveDumper(const char *filename, int rate) { - return new XMISong(this, filename, MIDI_GUS); + return new XMISong(this, filename, MDEV_GUS); } //========================================================================== @@ -703,7 +703,7 @@ MusInfo *XMISong::GetWaveDumper(const char *filename, int rate) // //========================================================================== -XMISong::XMISong(const XMISong *original, const char *filename, EMIDIDevice type) +XMISong::XMISong(const XMISong *original, const char *filename, EMidiDevice type) : MIDIStreamer(filename, type) { SongLen = original->SongLen; From 6d78ff3469b8eedbf597ae811c9d89a18b10b407 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 2 Oct 2010 13:49:33 +0000 Subject: [PATCH 086/815] - Backported GZDoom revisions 1018-1019: Fixed spelling of "pseudo" because it bugged me too much. ;) SVN r2880 (trunk) --- src/CMakeLists.txt | 2 +- src/sound/i_musicinterns.h | 14 +- src/sound/music_pseudo_mididevice.cpp | 260 ++++++++++++++++++++++++++ src/timidity/sf2.h | 2 +- zdoom.vcproj | 2 +- 5 files changed, 270 insertions(+), 10 deletions(-) create mode 100644 src/sound/music_pseudo_mididevice.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40f5772e7..298fe16a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -838,7 +838,7 @@ add_executable( zdoom WIN32 sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp - sound/music_psuedo_mididevice.cpp + sound/music_pseudo_mididevice.cpp textures/automaptexture.cpp textures/bitmap.cpp textures/buildtexture.cpp diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index bf0bfaa14..62a9f3710 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -143,13 +143,13 @@ protected: }; #endif -// Base class for psuedo-MIDI devices --------------------------------------- +// Base class for pseudo-MIDI devices --------------------------------------- -class PsuedoMIDIDevice : public MIDIDevice +class PseudoMIDIDevice : public MIDIDevice { public: - PsuedoMIDIDevice(); - ~PsuedoMIDIDevice(); + PseudoMIDIDevice(); + ~PseudoMIDIDevice(); void Close(); bool IsOpen() const; @@ -169,9 +169,9 @@ protected: bool bLooping; }; -// FMOD psuedo-MIDI device -------------------------------------------------- +// FMOD pseudo-MIDI device -------------------------------------------------- -class FMODMIDIDevice : public PsuedoMIDIDevice +class FMODMIDIDevice : public PseudoMIDIDevice { public: int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -180,7 +180,7 @@ public: // MIDI file played with TiMidity++ and possibly streamed through FMOD ------ -class TimidityPPMIDIDevice : public PsuedoMIDIDevice +class TimidityPPMIDIDevice : public PseudoMIDIDevice { public: TimidityPPMIDIDevice(); diff --git a/src/sound/music_pseudo_mididevice.cpp b/src/sound/music_pseudo_mididevice.cpp new file mode 100644 index 000000000..129be8f4c --- /dev/null +++ b/src/sound/music_pseudo_mididevice.cpp @@ -0,0 +1,260 @@ +/* +** music_pseudo_mididevice.cpp +** Common base class for pseudo MIDI devices. +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// PseudoMIDIDevice Constructor +// +//========================================================================== + +PseudoMIDIDevice::PseudoMIDIDevice() +{ + Stream = NULL; + Started = false; + bLooping = true; +} + +//========================================================================== +// +// PseudoMIDIDevice Destructor +// +//========================================================================== + +PseudoMIDIDevice::~PseudoMIDIDevice() +{ + Close(); +} + +//========================================================================== +// +// PseudoMIDIDevice :: Close +// +//========================================================================== + +void PseudoMIDIDevice::Close() +{ + if (Stream != NULL) + { + delete Stream; + Stream = NULL; + } + Started = false; +} + +//========================================================================== +// +// PseudoMIDIDevice :: IsOpen +// +//========================================================================== + +bool PseudoMIDIDevice::IsOpen() const +{ + return Stream != NULL; +} + +//========================================================================== +// +// PseudoMIDIDevice :: GetTechnology +// +//========================================================================== + +int PseudoMIDIDevice::GetTechnology() const +{ + return MOD_MIDIPORT; +} + +//========================================================================== +// +// PseudoMIDIDevice :: Resume +// +//========================================================================== + +int PseudoMIDIDevice::Resume() +{ + if (!Started) + { + if (Stream->Play(bLooping, 1)) + { + Started = true; + return 0; + } + return 1; + } + return 0; +} + +//========================================================================== +// +// PseudoMIDIDevice :: Stop +// +//========================================================================== + +void PseudoMIDIDevice::Stop() +{ + if (Started) + { + Stream->Stop(); + Started = false; + } +} + +//========================================================================== +// +// PseudoMIDIDevice :: Pause +// +//========================================================================== + +bool PseudoMIDIDevice::Pause(bool paused) +{ + if (Stream != NULL) + { + return Stream->SetPaused(paused); + } + return true; +} + +//========================================================================== +// +// PseudoMIDIDevice :: StreamOutSync +// +//========================================================================== + +int PseudoMIDIDevice::StreamOutSync(MIDIHDR *header) +{ + assert(0); + return 0; +} + +//========================================================================== +// +// PseudoMIDIDevice :: StreamOut +// +//========================================================================== + +int PseudoMIDIDevice::StreamOut(MIDIHDR *header) +{ + assert(0); + return 0; +} + +//========================================================================== +// +// PseudoMIDIDevice :: SetTempo +// +//========================================================================== + +int PseudoMIDIDevice::SetTempo(int tempo) +{ + return 0; +} + +//========================================================================== +// +// PseudoMIDIDevice :: SetTimeDiv +// +//========================================================================== + +int PseudoMIDIDevice::SetTimeDiv(int timediv) +{ + return 0; +} + +//========================================================================== +// +// PseudoMIDIDevice :: GetStats +// +//========================================================================== + +FString PseudoMIDIDevice::GetStats() +{ + if (Stream != NULL) + { + return Stream->GetStats(); + } + return "Pseudo MIDI device not open"; +} + + +//========================================================================== +// +// FMODMIDIDevice :: Open +// +//========================================================================== + +int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + return 0; +} + +//========================================================================== +// +// FMODMIDIDevice :: Preprocess +// +// Create a standard MIDI file and stream it. +// +//========================================================================== + +bool FMODMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) +{ + TArray midi; + + song->CreateSMF(midi); + Stream = GSnd->OpenStream((char *)&midi[0], looping ? SoundStream::Loop : 0, -1, midi.Size()); + return false; +} diff --git a/src/timidity/sf2.h b/src/timidity/sf2.h index 0036923da..224bf6058 100644 --- a/src/timidity/sf2.h +++ b/src/timidity/sf2.h @@ -162,7 +162,7 @@ struct SFModList enum { - SFMod_One = 0, // Psuedo-controller that always has the value 1 + SFMod_One = 0, // Pseudo-controller that always has the value 1 SFMod_NoteVelocity = 2, SFMod_KeyNumber = 3, SFMod_PolyPressure = 10, diff --git a/zdoom.vcproj b/zdoom.vcproj index 9986f70b9..05c7e012b 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5498,7 +5498,7 @@ > Date: Sat, 2 Oct 2010 16:26:10 +0000 Subject: [PATCH 087/815] - added new action special Ceiling_LowerAndCrushDist. It's similar to Ceiling_LowerAndCrush but allows to explicitly set the distance from the floor where the ceiling stops moving. This is used to remove the special behavior for Strife from the code that unlike the other games moved the ceiling to the floor, not to 8 units above it. SVN r2881 (trunk) --- src/actionspecials.h | 1 + src/p_ceiling.cpp | 8 +- src/p_lnspec.cpp | 8 +- src/p_spec.h | 1 + src/sound/music_psuedo_mididevice.cpp | 260 -------------------------- wadsrc/static/xlat/strife.txt | 4 +- 6 files changed, 18 insertions(+), 264 deletions(-) delete mode 100644 src/sound/music_psuedo_mididevice.cpp diff --git a/src/actionspecials.h b/src/actionspecials.h index 0f31243e0..8fcb3cf8d 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -95,6 +95,7 @@ DEFINE_SPECIAL(Polyobj_OR_MoveTimes8, 93, 4, 4, 4) DEFINE_SPECIAL(Pillar_BuildAndCrush, 94, 4, 5, 5) DEFINE_SPECIAL(FloorAndCeiling_LowerByValue, 95, 3, 3, 3) DEFINE_SPECIAL(FloorAndCeiling_RaiseByValue, 96, 3, 3, 3) +DEFINE_SPECIAL(Ceiling_LowerAndCrushDist, 97, 3, 5, 5) DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2) diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index c9aec4f37..ec569735c 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -162,6 +162,7 @@ void DCeiling::Tick () { case ceilCrushAndRaise: case ceilLowerAndCrush: + case ceilLowerAndCrushDist: if (m_Speed1 == FRACUNIT && m_Speed2 == FRACUNIT) m_Speed = FRACUNIT / 8; break; @@ -255,11 +256,16 @@ manual_ceiling: case DCeiling::ceilCrushRaiseAndStay: ceiling->m_TopHeight = sec->ceilingplane.d; case DCeiling::ceilLowerAndCrush: + case DCeiling::ceilLowerAndCrushDist: targheight = sec->FindHighestFloorPoint (&spot); - if (type != DCeiling::ceilLowerAndCrush || gameinfo.gametype != GAME_Strife) + if (type == DCeiling::ceilLowerAndCrush) { targheight += 8*FRACUNIT; } + else if (type == DCeiling::ceilLowerAndCrushDist) + { + targheight += height; + } ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); ceiling->m_Direction = -1; break; diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index b0697904a..b5a973cbd 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -547,6 +547,12 @@ FUNC(LS_Ceiling_LowerAndCrush) return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 0, arg2, 0, 0, CRUSHTYPE(arg3)); } +FUNC(LS_Ceiling_LowerAndCrushDist) +// Ceiling_LowerAndCrush (tag, speed, crush, dist, crushtype) +{ + return EV_DoCeiling (DCeiling::ceilLowerAndCrushDist, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4)); +} + FUNC(LS_Ceiling_CrushStop) // Ceiling_CrushStop (tag) { @@ -3150,7 +3156,7 @@ lnSpecFunc LineSpecials[256] = /* 94 */ LS_Pillar_BuildAndCrush, /* 95 */ LS_FloorAndCeiling_LowerByValue, /* 96 */ LS_FloorAndCeiling_RaiseByValue, - /* 97 */ LS_NOP, + /* 97 */ LS_Ceiling_LowerAndCrushDist, /* 98 */ LS_NOP, /* 99 */ LS_NOP, /* 100 */ LS_NOP, // Scroll_Texture_Left diff --git a/src/p_spec.h b/src/p_spec.h index 0171cf796..70b79ec09 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -664,6 +664,7 @@ public: ceilRaiseInstant, ceilCrushAndRaise, ceilLowerAndCrush, + ceilLowerAndCrushDist, ceilCrushRaiseAndStay, ceilRaiseToNearest, ceilLowerToLowest, diff --git a/src/sound/music_psuedo_mididevice.cpp b/src/sound/music_psuedo_mididevice.cpp deleted file mode 100644 index ec72794e3..000000000 --- a/src/sound/music_psuedo_mididevice.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* -** music_psuedo_mididevice.cpp -** Common base class for psuedo MIDI devices. -** -**--------------------------------------------------------------------------- -** Copyright 2008-2010 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. -**--------------------------------------------------------------------------- -** -*/ - -// HEADER FILES ------------------------------------------------------------ - -#include "i_musicinterns.h" -#include "templates.h" -#include "doomdef.h" -#include "m_swap.h" - -// MACROS ------------------------------------------------------------------ - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// PsuedoMIDIDevice Constructor -// -//========================================================================== - -PsuedoMIDIDevice::PsuedoMIDIDevice() -{ - Stream = NULL; - Started = false; - bLooping = true; -} - -//========================================================================== -// -// PsuedoMIDIDevice Destructor -// -//========================================================================== - -PsuedoMIDIDevice::~PsuedoMIDIDevice() -{ - Close(); -} - -//========================================================================== -// -// PsuedoMIDIDevice :: Close -// -//========================================================================== - -void PsuedoMIDIDevice::Close() -{ - if (Stream != NULL) - { - delete Stream; - Stream = NULL; - } - Started = false; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: IsOpen -// -//========================================================================== - -bool PsuedoMIDIDevice::IsOpen() const -{ - return Stream != NULL; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: GetTechnology -// -//========================================================================== - -int PsuedoMIDIDevice::GetTechnology() const -{ - return MOD_MIDIPORT; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: Resume -// -//========================================================================== - -int PsuedoMIDIDevice::Resume() -{ - if (!Started) - { - if (Stream->Play(bLooping, 1)) - { - Started = true; - return 0; - } - return 1; - } - return 0; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: Stop -// -//========================================================================== - -void PsuedoMIDIDevice::Stop() -{ - if (Started) - { - Stream->Stop(); - Started = false; - } -} - -//========================================================================== -// -// PsuedoMIDIDevice :: Pause -// -//========================================================================== - -bool PsuedoMIDIDevice::Pause(bool paused) -{ - if (Stream != NULL) - { - return Stream->SetPaused(paused); - } - return true; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: StreamOutSync -// -//========================================================================== - -int PsuedoMIDIDevice::StreamOutSync(MIDIHDR *header) -{ - assert(0); - return 0; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: StreamOut -// -//========================================================================== - -int PsuedoMIDIDevice::StreamOut(MIDIHDR *header) -{ - assert(0); - return 0; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: SetTempo -// -//========================================================================== - -int PsuedoMIDIDevice::SetTempo(int tempo) -{ - return 0; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: SetTimeDiv -// -//========================================================================== - -int PsuedoMIDIDevice::SetTimeDiv(int timediv) -{ - return 0; -} - -//========================================================================== -// -// PsuedoMIDIDevice :: GetStats -// -//========================================================================== - -FString PsuedoMIDIDevice::GetStats() -{ - if (Stream != NULL) - { - return Stream->GetStats(); - } - return "Psuedo MIDI device not open"; -} - - -//========================================================================== -// -// FMODMIDIDevice :: Open -// -//========================================================================== - -int FMODMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) -{ - return 0; -} - -//========================================================================== -// -// FMODMIDIDevice :: Preprocess -// -// Create a standard MIDI file and stream it. -// -//========================================================================== - -bool FMODMIDIDevice::Preprocess(MIDIStreamer *song, bool looping) -{ - TArray midi; - - song->CreateSMF(midi); - Stream = GSnd->OpenStream((char *)&midi[0], looping ? SoundStream::Loop : 0, -1, midi.Size()); - return false; -} diff --git a/wadsrc/static/xlat/strife.txt b/wadsrc/static/xlat/strife.txt index 0c141c855..b1e170037 100644 --- a/wadsrc/static/xlat/strife.txt +++ b/wadsrc/static/xlat/strife.txt @@ -109,7 +109,7 @@ RetailOnly = 121 193 = WALK|REP, ACS_ExecuteAlways (0, 0, 193, tag) 38 = WALK, Floor_LowerToLowest (tag, F_SLOW) 39 = WALK|MONST, Teleport (0, tag) - 44 = WALK, Ceiling_LowerAndCrush (tag, C_SLOW, 10) + 44 = WALK, Ceiling_LowerAndCrushDist (tag, C_SLOW, 10) 52 = WALK|REP, ACS_ExecuteAlways (0, 0, 52, tag) 53 = WALK, Plat_PerpetualRaiseLip (tag, P_SLOW, PLATWAIT, 0) 54 = WALK, Plat_Stop (tag) @@ -145,7 +145,7 @@ RetailOnly = 121 216 = WALK|REP, ACS_ExecuteAlways (0, 0, 216, tag) 90 = WALK|REP, Door_Raise (tag, D_SLOW, VDOORWAIT) - 72 = WALK|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 10) + 72 = WALK|REP, Ceiling_LowerAndCrushDist (tag, C_SLOW, 10) 73 = WALK|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) 74 = WALK|REP, Ceiling_CrushStop (tag) 75 = WALK|REP, Door_Close (tag, D_SLOW) From f26e65e550afa6c2fcb14f7d817c486fadd8c3c4 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 2 Oct 2010 16:49:44 +0000 Subject: [PATCH 088/815] - Fixed Linux compilation errors. (I'm not too sure what I was supposed to do with PrepTimidity() though.) SVN r2882 (trunk) --- src/sound/music_midi_timidity.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index 28a77eada..c0efbf74e 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -429,7 +429,7 @@ bool TimidityPPMIDIDevice::LaunchTimidity () } return false; #else - if (WavePipe[0] != -1 && WavePipe[1] == -1 && m_Stream != NULL) + if (WavePipe[0] != -1 && WavePipe[1] == -1 && Stream != NULL) { // Timidity was previously launched, so the write end of the pipe // is closed, and the read end is still open. Close the pipe @@ -437,9 +437,9 @@ bool TimidityPPMIDIDevice::LaunchTimidity () close (WavePipe[0]); WavePipe[0] = -1; - delete m_Stream; - m_Stream = NULL; - PrepTimidity (); + delete Stream; + Stream = NULL; + Open (NULL, NULL); } int forkres; @@ -597,7 +597,7 @@ bool TimidityPPMIDIDevice::IsOpen() const { if (waitpid (ChildProcess, NULL, WNOHANG) == ChildProcess) { - ChildProcess = -1; + const_cast(this)->ChildProcess = -1; #endif return false; } From 828adec7e63a07d5940c9f2de53124e45fdd33e8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 3 Oct 2010 20:22:42 +0000 Subject: [PATCH 089/815] - moved the Messages menu from Display Option to HUD options. SVN r2890 (trunk) --- wadsrc/static/menudef.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 5c6873d14..db8d4bd45 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -644,7 +644,6 @@ OptionMenu "VideoOptions" { Title "DISPLAY OPTIONS" - Submenu "Message Options", "MessageOptions" Submenu "Scoreboard Options", "ScoreboardOptions" StaticText " " Slider "Screen size", "screenblocks", 3.0, 12.0, 1.0, 0 @@ -725,6 +724,7 @@ OptionMenu "HUDOptions" { Title "HUD Options" Submenu "Alternative HUD", "AltHudOptions" + Submenu "Message Options", "MessageOptions" StaticText " " Option "Default Crosshair", "crosshair", "Crosshairs" Option "Force default crosshair", "crosshairforce", "OnOff" From 00f6f459e5233a712d911f4af988c1c63545b0e9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 5 Oct 2010 19:17:22 +0000 Subject: [PATCH 090/815] - fixed: Strife defined the wrong graphic for the pause sign. SVN r2900 (trunk) --- wadsrc/static/mapinfo/strife.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index e65b7a9a7..a1aa90fbe 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -53,7 +53,7 @@ gameinfo menufontcolor_selection = "GOLD" menubackbutton = "M_BACK_S" PlayerClasses = "StrifePlayer" - pausesign = "PAUSED" + pausesign = "M_PAUSE" gibfactor = 0.5 cursorpic = "strfcurs" } From 5ff3b3f32915f624358b9acabd082e385fca6e3b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 5 Oct 2010 22:31:22 +0000 Subject: [PATCH 091/815] - added Gez's VOC loader. SVN r2901 (trunk) --- src/s_sound.cpp | 7 +- src/sound/fmodsound.cpp | 6 +- src/sound/fmodsound.h | 2 +- src/sound/i_sound.cpp | 157 +++++++++++++++++++++++++++++++++++++++- src/sound/i_sound.h | 3 +- 5 files changed, 169 insertions(+), 6 deletions(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index b2565b950..593fc4eaf 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -1307,10 +1307,15 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx) wlump.Read(sfxdata, size); SDWORD len = LittleLong(((SDWORD *)sfxdata)[1]); + // If the sound is voc, use the custom loader. + if (strncmp ((const char *)sfxstart, "Creative Voice File", 19) == 0) + { + sfx->data = GSnd->LoadSoundVoc(sfxstart, len); + } // If the sound is raw, just load it as such. // Otherwise, try the sound as DMX format. // If that fails, let FMOD try and figure it out. - if (sfx->bLoadRAW || + else if (sfx->bLoadRAW || (((BYTE *)sfxdata)[0] == 3 && ((BYTE *)sfxdata)[1] == 0 && len <= size - 8)) { int frequency; diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 23e374a7b..399322061 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -2316,7 +2316,7 @@ void FMODSoundRenderer::UpdateSounds() // //========================================================================== -SoundHandle FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart) +SoundHandle FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) { FMOD_CREATESOUNDEXINFO exinfo; SoundHandle retval = { NULL }; @@ -2372,7 +2372,9 @@ SoundHandle FMODSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequ if (loopstart >= 0) { - sample->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, numsamples - 1, FMOD_TIMEUNIT_PCM); + if (loopend == -1) + loopend = numsamples - 1; + sample->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM); } retval.data = sample; diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h index 1460b697b..8e28d0743 100644 --- a/src/sound/fmodsound.h +++ b/src/sound/fmodsound.h @@ -14,7 +14,7 @@ public: void SetSfxVolume (float volume); void SetMusicVolume (float volume); SoundHandle LoadSound(BYTE *sfxdata, int length); - SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart); + SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1); void UnloadSound (SoundHandle sfx); unsigned int GetMSLength(SoundHandle sfx); unsigned int GetSampleLength(SoundHandle sfx); diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 0a946b3c1..8f62d3e69 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -124,7 +124,7 @@ public: SoundHandle retval = { NULL }; return retval; } - SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart) + SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) { SoundHandle retval = { NULL }; return retval; @@ -344,3 +344,158 @@ FString SoundStream::GetStats() { return "No stream stats available."; } + +//========================================================================== +// +// SoundRenderer :: LoadSoundVoc +// +//========================================================================== + +SoundHandle SoundRenderer::LoadSoundVoc(BYTE *sfxdata, int length) +{ + BYTE * data = NULL; + int len, frequency, channels, bits, loopstart, loopend; + len = frequency = channels = bits = 0; + loopstart = loopend = -1; + do if (length > 26) + { + // First pass to parse data and validate the file + if (strncmp ((const char *)sfxdata, "Creative Voice File", 19)) + break; + int i = 26, blocktype = 0, blocksize = 0, codec = -1; + bool noextra = true, okay = true; + while (i < length) + { + // Read block header + blocktype = sfxdata[i]; + if (blocktype == 0) + break; + blocksize = sfxdata[i+1] + (sfxdata[i+2]<<8) + (sfxdata[i+3]<<16); + i += 4; + if (i + blocksize > length) + { + okay = false; + break; + } + + // Read block data + switch (blocktype) + { + case 1: // Sound data + if (noextra && (codec == -1 || codec == sfxdata[i+1])) + { + frequency = 1000000/(256 - sfxdata[i]); + channels = 1; + codec = sfxdata[i+1]; + if (codec == 0) + bits = 8; + else if (codec == 4) + bits = -16; + else okay = false; + len += blocksize - 2; + } + break; + case 2: // Sound data continuation + if (codec == -1) + okay = false; + len += blocksize; + break; + case 3: // Silence + if (frequency == 1000000/(256 - sfxdata[i+2])) + { + int silength = 1 + sfxdata[i] + (sfxdata[i+1]<<8); + if (codec == 0) // 8-bit unsigned PCM + len += silength; + else if (codec == 4) // 16-bit signed PCM + len += silength<<1; + else okay = false; + } else okay = false; + break; + case 4: // Mark (ignored) + case 5: // Text (ignored) + break; + case 6: // Repeat start + loopstart = len; + break; + case 7: // Repeat end + loopend = len; + if (loopend < loopstart) + okay = false; + break; + case 8: // Extra info + noextra = false; + if (codec == -1) + { + codec = sfxdata[i+2]; + channels = 1+sfxdata[i+3]; + frequency = 256000000/(channels * (65536 - (sfxdata[i]+(sfxdata[i+1]<<8)))); + } else okay = false; + break; + case 9: // Sound data in new format + if (codec == -1) + { + frequency = sfxdata[i] + (sfxdata[i+1]<<8) + (sfxdata[i+2]<<16) + (sfxdata[i+3]<<24); + bits = sfxdata[i+4]; + channels = sfxdata[i+5]; + codec = sfxdata[i+6] + (sfxdata[i+7]<<8); + if (codec == 0) + bits = 8; + else if (codec == 4) + bits = -16; + else okay = false; + len += blocksize - 12; + } else okay = false; + break; + default: // Unknown block type + okay = false; + DPrintf ("Unknown VOC block type %i\n", blocktype); + break; + } + // Move to next block + i += blocksize; + } + + // Second pass to write the data + if (okay) + { + data = new BYTE[len]; + i = 26; + int j = 0; + while (i < length) + { + // Read block header again + blocktype = sfxdata[i]; + if (blocktype == 0) break; + blocksize = sfxdata[i+1] + (sfxdata[i+2]<<8) + (sfxdata[i+3]<<16); + i += 4; + switch (blocktype) + { + case 1: memcpy(data+j, sfxdata+i+2, blocksize-2 ); j += blocksize-2; break; + case 2: memcpy(data+j, sfxdata+i, blocksize ); j += blocksize; break; + case 9: memcpy(data+j, sfxdata+i+12, blocksize-12); j += blocksize-12; break; + case 3: + { + int silength = 1 + sfxdata[i] + (sfxdata[i+1]<<8); + if (bits == 8) + { + memset(data+j, 128, silength); + j += silength; + } + else if (bits == -16) + { + memset(data+j, 0, silength<<1); + j += silength<<1; + } + } + break; + default: break; + } + } + } + + } while (false); + SoundHandle retval = LoadSoundRaw(data, len, frequency, channels, bits, loopstart, loopend); + if (data) delete[] data; + return retval; +} + diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index e5edcbb40..9aea1b525 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -92,7 +92,8 @@ public: virtual void SetSfxVolume (float volume) = 0; virtual void SetMusicVolume (float volume) = 0; virtual SoundHandle LoadSound(BYTE *sfxdata, int length) = 0; - virtual SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart) = 0; + SoundHandle LoadSoundVoc(BYTE *sfxdata, int length); + virtual SoundHandle LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1) = 0; virtual void UnloadSound (SoundHandle sfx) = 0; // unloads a sound from memory virtual unsigned int GetMSLength(SoundHandle sfx) = 0; // Gets the length of a sound at its default frequency virtual unsigned int GetSampleLength(SoundHandle sfx) = 0; // Gets the length of a sound at its default frequency From 0490c35347843bf2da1a805c86197040c1ccff97 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 5 Oct 2010 22:35:59 +0000 Subject: [PATCH 092/815] - added Edward-san's improvements for changemus command. SVN r2902 (trunk) --- src/s_sound.cpp | 96 ++++++++++++++++++++------------------ src/sound/i_music.h | 2 +- src/sound/i_musicinterns.h | 2 +- src/sound/i_sound.cpp | 3 +- src/sound/i_sound.h | 1 + 5 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 593fc4eaf..95130d309 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -827,7 +827,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO FVector3 pos, vel; FRolloffInfo *rolloff; - if (sound_id <= 0 || volume <= 0 || nosfx) + if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) return NULL; int type; @@ -2582,38 +2582,41 @@ CCMD (idmus) FString map; int l; - if (argv.argc() > 1) + if (!nomusic) { - if (gameinfo.flags & GI_MAPxx) + if (argv.argc() > 1) { - l = atoi (argv[1]); - if (l <= 99) + if (gameinfo.flags & GI_MAPxx) { - map = CalcMapName (0, l); + l = atoi (argv[1]); + if (l <= 99) + { + map = CalcMapName (0, l); + } + else + { + Printf ("%s\n", GStrings("STSTR_NOMUS")); + return; + } + } + else + { + map = CalcMapName (argv[1][0] - '0', argv[1][1] - '0'); + } + + if ( (info = FindLevelInfo (map)) ) + { + if (info->Music.IsNotEmpty()) + { + S_ChangeMusic (info->Music, info->musicorder); + Printf ("%s\n", GStrings("STSTR_MUS")); + } } else { Printf ("%s\n", GStrings("STSTR_NOMUS")); - return; } } - else - { - map = CalcMapName (argv[1][0] - '0', argv[1][1] - '0'); - } - - if ( (info = FindLevelInfo (map)) ) - { - if (info->Music.IsNotEmpty()) - { - S_ChangeMusic (info->Music, info->musicorder); - Printf ("%s\n", GStrings("STSTR_MUS")); - } - } - else - { - Printf ("%s\n", GStrings("STSTR_NOMUS")); - } } } @@ -2625,27 +2628,30 @@ CCMD (idmus) CCMD (changemus) { - if (argv.argc() > 1) - { - if (PlayList) - { - delete PlayList; - PlayList = NULL; - } - S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0); - } - else - { - const char *currentmus = mus_playing.name.GetChars(); - if(currentmus != NULL && *currentmus != 0) - { - Printf ("currently playing %s\n", currentmus); - } - else - { - Printf ("no music playing\n"); - } - } + if (!nomusic) + { + if (argv.argc() > 1) + { + if (PlayList) + { + delete PlayList; + PlayList = NULL; + } + S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0); + } + else + { + const char *currentmus = mus_playing.name.GetChars(); + if(currentmus != NULL && *currentmus != 0) + { + Printf ("currently playing %s\n", currentmus); + } + else + { + Printf ("no music playing\n"); + } + } + } } //========================================================================== diff --git a/src/sound/i_music.h b/src/sound/i_music.h index 17775ebe0..d587d3129 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -93,6 +93,6 @@ public: bool m_Looping; bool m_NotStartedYet; // Song has been created but not yet played }; - +extern int nomusic; #endif //__I_MUSIC_H__ diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 62a9f3710..29b655d11 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -593,6 +593,7 @@ protected: void SetupForHMI(int len); void SetupForHMP(int len); void CheckCaps(int tech); + void DoInitialSetup(); void DoRestart(); bool CheckDone(); @@ -749,6 +750,5 @@ MusInfo *GME_OpenSong(FILE *file, BYTE *musiccache, int len, const char *fmt); // -------------------------------------------------------------------------- extern MusInfo *currSong; -extern int nomusic; EXTERN_CVAR (Float, snd_musicvolume) diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 8f62d3e69..27c00e93f 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -81,6 +81,7 @@ CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, snd_pitched, false, CVAR_ARCHIVE) SoundRenderer *GSnd; +bool nosound; bool nosfx; void I_CloseSound (); @@ -235,7 +236,7 @@ public: void I_InitSound () { /* Get command line options: */ - bool nosound = !!Args->CheckParm ("-nosound"); + nosound = !!Args->CheckParm ("-nosound"); nosfx = !!Args->CheckParm ("-nosfx"); if (nosound) diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index 9aea1b525..660e47166 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -145,6 +145,7 @@ public: extern SoundRenderer *GSnd; extern bool nosfx; +extern bool nosound; void I_InitSound (); void I_ShutdownSound (); From d9970ab9b6805eba857164a40689d828a3d1908a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 6 Oct 2010 10:44:03 +0000 Subject: [PATCH 093/815] - merged finale branch back into trunk. SVN r2911 (trunk) --- src/d_iwad.cpp | 2 +- src/d_main.cpp | 20 +- src/dobjgc.cpp | 2 + src/doomdef.h | 4 +- src/f_finale.cpp | 1374 ----- src/f_finale.h | 53 - src/g_game.cpp | 4 +- src/g_level.cpp | 116 +- src/g_level.h | 46 +- src/g_mapinfo.cpp | 196 +- src/g_strife/a_programmer.cpp | 1 - src/gi.cpp | 3 + src/gi.h | 3 + src/intermission/intermission.cpp | 875 ++++ src/intermission/intermission.h | 320 ++ src/intermission/intermission_parse.cpp | 848 ++++ src/menu/menu.cpp | 15 +- src/menu/menu.h | 1 + src/namedef.h | 10 + src/p_lnspec.cpp | 3 +- src/p_user.cpp | 6 +- src/statistics.cpp | 4 +- wadsrc/static/mapinfo/chex.txt | 4 + wadsrc/static/mapinfo/common.txt | 308 ++ wadsrc/static/mapinfo/doomcommon.txt | 5 + wadsrc/static/mapinfo/heretic.txt | 4 + wadsrc/static/mapinfo/hexdd.txt | 8 + wadsrc/static/mapinfo/hexen.txt | 6 +- wadsrc/static/mapinfo/strife.txt | 174 +- zdoom.vcproj | 6074 ++++++++++++----------- 30 files changed, 5732 insertions(+), 4757 deletions(-) delete mode 100644 src/f_finale.cpp delete mode 100644 src/f_finale.h create mode 100644 src/intermission/intermission.cpp create mode 100644 src/intermission/intermission.h create mode 100644 src/intermission/intermission_parse.cpp create mode 100644 wadsrc/static/mapinfo/common.txt create mode 100644 wadsrc/static/mapinfo/hexdd.txt diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 119d85b74..3453d9d5f 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -61,7 +61,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx | GI_COMPATSHORTTEX | GI_COMPATSTAIRS }, { "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 }, - { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 }, + { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexdd.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 }, { "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE }, { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE }, diff --git a/src/d_main.cpp b/src/d_main.cpp index fc282fa5f..10d58fd2b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -57,7 +57,7 @@ #include "w_wad.h" #include "s_sound.h" #include "v_video.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "f_wipe.h" #include "m_argv.h" #include "m_misc.h" @@ -677,13 +677,23 @@ void D_Display () else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL) { // save the current screen if about to wipe BorderNeedRefresh = screen->GetPageCount (); - if (wipegamestate != GS_FORCEWIPEFADE) + switch (wipegamestate) { + default: wipe = screen->WipeStartScreen (wipetype); - } - else - { + break; + + case GS_FORCEWIPEFADE: wipe = screen->WipeStartScreen (wipe_Fade); + break; + + case GS_FORCEWIPEBURN: + wipe = screen->WipeStartScreen (wipe_Burn); + break; + + case GS_FORCEWIPEMELT: + wipe = screen->WipeStartScreen (wipe_Melt); + break; } wipegamestate = gamestate; } diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 4314d0488..642097073 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -73,6 +73,7 @@ #include "m_argv.h" #include "po_man.h" #include "menu/menu.h" +#include "intermission/intermission.h" // MACROS ------------------------------------------------------------------ @@ -300,6 +301,7 @@ static void MarkRoot() Mark(screen); Mark(StatusBar); Mark(DMenu::CurrentMenu); + Mark(DIntermissionController::CurrentIntermission); DThinker::MarkRoots(); FCanvasTextureInfo::Mark(); Mark(DACSThinker::ActiveThinker); diff --git a/src/doomdef.h b/src/doomdef.h index 1108e770f..9ad019db4 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -76,7 +76,9 @@ typedef enum GS_TITLELEVEL, // [RH] A combination of GS_LEVEL and GS_DEMOSCREEN GS_FORCEWIPE = -1, - GS_FORCEWIPEFADE = -2 + GS_FORCEWIPEFADE = -2, + GS_FORCEWIPEBURN = -3, + GS_FORCEWIPEMELT = -4 } gamestate_t; extern gamestate_t gamestate; diff --git a/src/f_finale.cpp b/src/f_finale.cpp deleted file mode 100644 index a851fdc31..000000000 --- a/src/f_finale.cpp +++ /dev/null @@ -1,1374 +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: -// Game completion, final screen animation. -// -//----------------------------------------------------------------------------- - - - -#include -#include -#include - -#include "i_system.h" -#include "m_swap.h" -#include "v_video.h" -#include "i_video.h" -#include "v_text.h" -#include "w_wad.h" -#include "s_sound.h" -#include "gstrings.h" -#include "doomstat.h" -#include "r_state.h" -#include "r_draw.h" -#include "hu_stuff.h" -#include "cmdlib.h" -#include "gi.h" -#include "p_conversation.h" -#include "a_strifeglobal.h" -#include "templates.h" -#include "c_bind.h" -#include "r_translate.h" -#include "g_level.h" -#include "d_event.h" -#include "v_palette.h" -#include "d_main.h" - -static void FadePic (); -static void GetFinaleText (const char *msgLumpName); - -// Stage of animation: -// 0 = text -// 1 = art screen -// 2 = underwater screen -// 3 = character cast -// 4 = Heretic title -// 5 = Strife slideshow -static unsigned int FinaleStage; - -static size_t FinaleCount, FinaleEndCount; -static int FinalePart; - -static int TEXTSPEED; -#define TEXTWAIT 250 - -static int FinaleSequence; -static SBYTE FadeDir; -static bool FinaleHasPic; - -static FString FinaleText; -static size_t FinaleTextLen; -static const char *FinaleFlat; -static bool FinaleEnding; - -void F_StartCast (void); -void F_CastTicker (void); -bool F_CastResponder (event_t *ev); -void F_CastDrawer (void); -void F_AdvanceSlideshow (); - -// -// F_StartFinale -// -void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, - const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, - bool ending, int endsequence) -{ - bool loopmusic = ending ? !gameinfo.noloopfinalemusic : true; - gameaction = ga_nothing; - gamestate = GS_FINALE; - viewactive = false; - automapactive = false; - - // Okay - IWAD dependend stuff. - // This has been changed severely, and some stuff might have changed in the process. - // - // [RH] More flexible now (even more severe changes) - // FinaleFlat, FinaleText, and music are now determined in G_WorldDone() based on - // data in a level_info_t and a cluster_info_t. - - if (cdtrack == 0 || !S_ChangeCDMusic (cdtrack, cdid)) - { - if (music == NULL) - { - S_ChangeMusic (gameinfo.finaleMusic, 0, loopmusic); - } - else - { - S_ChangeMusic (music, musicorder, loopmusic); - } - } - - FinaleFlat = (flat != NULL && *flat != 0) ? flat : gameinfo.finaleFlat; - if (FinaleFlat != NULL && FinaleFlat[0] == '$') - { - FinaleFlat = GStrings(FinaleFlat + 1); - } - - if (textInLump) - { - GetFinaleText (text); - } - else - { - const char *from = (text != NULL) ? text : "Empty message"; - FinaleText = from; - FinaleTextLen = FinaleText.Len() + 1; - } - if (lookupText) - { - const char *str = GStrings[FinaleText.GetChars()]; - if (str != NULL) - { - FinaleText = str; - FinaleTextLen = FinaleText.Len() + 1; - } - } - - FinaleStage = 0; - V_SetBlend (0,0,0,0); - TEXTSPEED = 2; - - FinaleHasPic = !!finalePic; - FinaleCount = 0; - FinaleEndCount = 70; - FadeDir = -1; - FinaleEnding = ending; - FinaleSequence = endsequence; - - S_StopAllChannels (); - - if (ending) - { - if (EndSequences[FinaleSequence].EndType == END_Chess) - { - TEXTSPEED = 3; // Slow the text to its original rate to match the music. - S_ChangeMusic ("hall", 0, loopmusic); - FinaleStage = 10; - GetFinaleText ("win1msg"); - V_SetBlend (0,0,0,256); - } - else if (EndSequences[FinaleSequence].EndType == END_Strife) - { - if (players[0].mo->FindInventory (QuestItemClasses[24]) || - players[0].mo->FindInventory (QuestItemClasses[27])) - { - FinalePart = 10; - } - else - { - FinalePart = 17; - } - FinaleStage = 5; - FinaleEndCount = 0; - } - } -} - -void F_EndFinale () -{ - FinaleText = NULL; - FinaleTextLen = 0; -} - -bool F_Responder (event_t *event) -{ - if (FinaleStage == 3) - { - return F_CastResponder (event); - } - else if (FinaleStage == 2 && event->type == EV_KeyDown) - { // We're showing the water pic; make any key kick to demo mode - FinaleStage = 4; - V_ForceBlend (0, 0, 0, 0); - return true; - } - - return false; -} - - -// -// F_Ticker -// -void F_Ticker () -{ - int i; - bool interrupt = false; - - // check for skipping - for (i = 0; i < MAXPLAYERS; i++) - { - // Only for buttons going down - if (!interrupt) - { - for (size_t j = 0; j < sizeof(players[i].cmd.ucmd.buttons)*8; ++j) - { - if (((players[i].cmd.ucmd.buttons >> j) & 1) && - !((players[i].oldbuttons >> j) & 1)) - { - interrupt = true; - break; - } - } - } - players[i].oldbuttons = players[i].cmd.ucmd.buttons; - } - - // [RH] Non-commercial can be skipped now, too - if (FinaleStage == 0) - { - if (interrupt || - ((!(gameinfo.flags & GI_MAPxx) || gameinfo.flags & GI_SHAREWARE) - && FinaleCount > FinaleTextLen*TEXTSPEED+TEXTWAIT)) - { - if (FinaleCount < FinaleTextLen*TEXTSPEED+10) - { - FinaleCount = FinaleTextLen*TEXTSPEED+10; - } - else - { - if (FinaleEnding) - { - // [RH] Don't automatically advance end-of-game messages - if (interrupt) - { - if (EndSequences[FinaleSequence].EndType == END_Cast) - { - F_StartCast (); - } - else - { - FinaleCount = 0; - FinaleStage = 1; - wipegamestate = GS_FORCEWIPE; - if (EndSequences[FinaleSequence].EndType == END_Bunny) - { - if (!EndSequences[FinaleSequence].Advanced) - S_StartMusic ("$music_bunny"); - } - } - if (EndSequences[FinaleSequence].Advanced && - !EndSequences[FinaleSequence].Music.IsEmpty()) - { - S_ChangeMusic(EndSequences[FinaleSequence].Music, 0, EndSequences[FinaleSequence].MusicLooping); - } - } - } - else - { - gameaction = ga_worlddone; - } - } - } - } - else if (FinaleStage >= 10) - { - // Hexen chess ending with three pages of text. - // [RH] This can be interrupted to speed it up. - if (interrupt) - { - if (FinaleStage == 11 || FinaleStage == 12 || FinaleStage == 15) - { // Stages that display text - if (FinaleCount < FinaleEndCount-TEXTWAIT) - { - FinaleCount = FinaleEndCount-TEXTWAIT; - } - else - { - FinaleCount = FinaleEndCount; - } - } - else if (FinaleCount < 69) - { // Stages that fade pictures - FinaleCount = 69; - } - } - if (FinaleStage < 15 && FinaleCount >= FinaleEndCount) - { - FinaleCount = 0; - FinaleStage++; - switch (FinaleStage) - { - case 11: // Text 1 - FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT; - break; - - case 12: // Pic 2, Text 2 - GetFinaleText ("win2msg"); - FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT; - S_ChangeMusic ("orb", 0, !gameinfo.noloopfinalemusic); - break; - - case 13: // Pic 2 -- Fade out - FinaleEndCount = 70; - FadeDir = 1; - break; - - case 14: // Pic 3 -- Fade in - FinaleEndCount = 71; - FadeDir = -1; - S_ChangeMusic ("chess", 0, !gameinfo.noloopfinalemusic); - break; - - case 15: // Pic 3, Text 3 - GetFinaleText ("win3msg"); - FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT; - break; - } - return; - } - if (FinaleStage == 10 || FinaleStage == 13 || FinaleStage == 14) - { - FadePic (); - } - } - else if (FinaleStage >= 5) - { // Strife slideshow - if (interrupt) - { - FinaleCount = FinaleEndCount; - } - } - - // advance animation - FinaleCount++; - - if (FinaleStage == 3) - { - F_CastTicker (); - return; - } - else if (FinaleStage == 5 && FinaleCount > FinaleEndCount) - { - S_StopSound (CHAN_VOICE); - F_AdvanceSlideshow (); - FinaleCount = 0; - } -} - -//=========================================================================== -// -// FadePic -// -//=========================================================================== - -static void FadePic () -{ - int blend = int(256*FinaleCount/70); - - if (FadeDir < 0) - { - blend = 256 - blend; - } - V_SetBlend (0,0,0,blend); -} - -// -// F_TextWrite -// -void F_TextWrite (void) -{ - FTexture *pic; - int w; - size_t count; - const char *ch; - int c; - int cx; - int cy; - const FRemapTable *range; - int leftmargin; - int rowheight; - bool scale; - - if (FinaleCount < 11) - return; - - // draw some of the text onto the screen - leftmargin = (gameinfo.gametype & (GAME_DoomStrifeChex|GAME_Hexen) ? 10 : 20) - 160; - rowheight = SmallFont->GetHeight () + - (gameinfo.gametype & (GAME_DoomStrifeChex) ? 3 : -1); - scale = (CleanXfac != 1 || CleanYfac != 1); - - cx = leftmargin; - if (FinaleStage == 15) - { - cy = 135 - 100; - } - else - { - cy = (gameinfo.gametype & (GAME_DoomStrifeChex) ? 10 : 5) - 100; - } - ch = FinaleText.GetChars(); - - count = (FinaleCount - 10)/TEXTSPEED; - range = SmallFont->GetColorTranslation (CR_UNTRANSLATED); - - for ( ; count ; count-- ) - { - c = *ch++; - if (!c) - break; - if (c == '\n') - { - cx = leftmargin; - cy += rowheight; - continue; - } - - pic = SmallFont->GetChar (c, &w); - if (cx+w > SCREENWIDTH) - continue; - if (pic != NULL) - { - if (scale) - { - screen->DrawTexture (pic, - cx + 320 / 2, - cy + 200 / 2, - DTA_Translation, range, - DTA_Clean, true, - TAG_DONE); - } - else - { - screen->DrawTexture (pic, - cx + 320 / 2, - cy + 200 / 2, - DTA_Translation, range, - TAG_DONE); - } - } - cx += w; - } - -} - -// -// Final DOOM 2 animation -// Casting by id Software. -// in order of appearance -// -struct castinfo_t -{ - const char *name; - const char *type; - const AActor *info; - const PClass *Class; -}; - -castinfo_t castorder[] = -{ - {"CC_ZOMBIE", "ZombieMan"}, - {"CC_SHOTGUN", "ShotgunGuy"}, - {"CC_HEAVY", "ChaingunGuy"}, - {"CC_IMP", "DoomImp"}, - {"CC_DEMON", "Demon"}, - {"CC_LOST", "LostSoul"}, - {"CC_CACO", "Cacodemon"}, - {"CC_HELL", "HellKnight"}, - {"CC_BARON", "BaronOfHell"}, - {"CC_ARACH", "Arachnotron"}, - {"CC_PAIN", "PainElemental"}, - {"CC_REVEN", "Revenant"}, - {"CC_MANCU", "Fatso"}, - {"CC_ARCH", "Archvile"}, - {"CC_SPIDER", "SpiderMastermind"}, - {"CC_CYBER", "Cyberdemon"}, - {"CC_HERO", "DoomPlayer"}, - - {0, NULL} -}; - -static struct -{ - const char *type; - BYTE melee; - BYTE ofs; - const char *sound; - FState *match; -} atkstates[] = -{ - { "DoomPlayer", 0, 0, "weapons/sshotf" }, - { "ZombieMan", 0, 1, "grunt/attack" }, - { "ShotgunGuy", 0, 1, "shotguy/attack" }, - { "Archvile", 0, 1, "vile/start" }, - { "Revenant", 1, 1, "skeleton/swing" }, - { "Revenant", 1, 3, "skeleton/melee" }, - { "Revenant", 0, 1, "skeleton/attack" }, - { "Fatso", 0, 1, "fatso/attack" }, - { "Fatso", 0, 4, "fatso/attack" }, - { "Fatso", 0, 7, "fatso/attack" }, - { "ChaingunGuy", 0, 1, "chainguy/attack" }, - { "ChaingunGuy", 0, 2, "chainguy/attack" }, - { "ChaingunGuy", 0, 3, "chainguy/attack" }, - { "DoomImp", 0, 2, "imp/attack" }, - { "Demon", 1, 1, "demon/melee" }, - { "BaronOfHell", 0, 1, "baron/attack" }, - { "HellKnight", 0, 1, "baron/attack" }, - { "Cacodemon", 0, 1, "caco/attack" }, - { "LostSoul", 0, 1, "skull/melee" }, - { "SpiderMastermind", 0, 1, "spider/attack" }, - { "SpiderMastermind", 0, 2, "spider/attack" }, - { "Arachnotron", 0, 1, "baby/attack" }, - { "Cyberdemon", 0, 1, "weapons/rocklf" }, - { "Cyberdemon", 0, 3, "weapons/rocklf" }, - { "Cyberdemon", 0, 5, "weapons/rocklf" }, - { "PainElemental", 0, 2, "skull/melee" }, - { NULL } -}; - -int castnum; -int casttics; -int castsprite; // [RH] For overriding the player sprite with a skin -const FRemapTable *casttranslation; // [RH] Draw "our hero" with their chosen suit color -FState* caststate; -bool castdeath; -int castframes; -int castonmelee; -bool castattacking; - -static FState *advplayerstate; - -// -// F_StartCast -// -extern gamestate_t wipegamestate; - - -void F_StartCast (void) -{ - const PClass *type; - int i; - - // [RH] Set the names and defaults for the cast - for (i = 0; castorder[i].type; i++) - { - type = PClass::FindClass (castorder[i].type); - if (type == NULL) - { - castorder[i].info = GetDefault(); - castorder[i].Class= RUNTIME_CLASS(AActor); - } - else - { - castorder[i].info = GetDefaultByType (type); - castorder[i].Class= type; - } - } - - for (i = 0; atkstates[i].type; i++) - { - type = PClass::FindClass (atkstates[i].type); - if (type != NULL) - { - if (atkstates[i].melee) - atkstates[i].match = ((AActor *)(type->Defaults))->MeleeState + atkstates[i].ofs; - else - atkstates[i].match = ((AActor *)(type->Defaults))->MissileState + atkstates[i].ofs; - } - else - { - atkstates[i].match = NULL; - } - } - - type = PClass::FindClass (NAME_DoomPlayer); - if (type != NULL) - advplayerstate = ((AActor *)(type->Defaults))->MissileState; - - wipegamestate = GS_FORCEWIPE; - castnum = 0; - caststate = castorder[castnum].info->SeeState; - castsprite = caststate->sprite; - casttranslation = NULL; - casttics = caststate->GetTics (); - castdeath = false; - FinaleStage = 3; - castframes = 0; - castonmelee = 0; - castattacking = false; - if (!EndSequences[FinaleSequence].Advanced) - S_ChangeMusic ("$music_evil"); -} - - -// -// F_CastTicker -// -void F_CastTicker (void) -{ - if (--casttics > 0 && caststate != NULL) - return; // not time to change state yet - - if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL) - { - // switch from deathstate to next monster - do - { - castnum++; - castdeath = false; - if (castorder[castnum].name == 0) - castnum = 0; - if (castorder[castnum].info->SeeSound) - { - S_Sound (CHAN_VOICE | CHAN_UI, castorder[castnum].info->SeeSound, 1, ATTN_NONE); - } - caststate = castorder[castnum].info->SeeState; - // [RH] Skip monsters that have been hacked to no longer have attack states - if (castorder[castnum].info->MissileState == NULL && - castorder[castnum].info->MeleeState == NULL) - { - caststate = NULL; - } - } - while (caststate == NULL); - if (castnum == 16) - { - castsprite = skins[players[consoleplayer].userinfo.skin].sprite; - casttranslation = translationtables[TRANSLATION_Players][consoleplayer]; - } - else - { - castsprite = caststate->sprite; - casttranslation = NULL; - } - castframes = 0; - } - else - { - // sound hacks.... - if (caststate != NULL) - { - int i; - - for (i = 0; atkstates[i].type; i++) - { - if (atkstates[i].match == caststate) - { - S_StopAllChannels (); - S_Sound (CHAN_WEAPON | CHAN_UI, atkstates[i].sound, 1, ATTN_NONE); - break; - } - } - } - - // just advance to next state in animation - if (caststate == advplayerstate) - goto stopattack; // Oh, gross hack! - - caststate = caststate->GetNextState(); - castframes++; - } - - if (castframes == 12) - { - // go into attack frame - castattacking = true; - if (castonmelee) - caststate = castorder[castnum].info->MeleeState; - else - caststate = castorder[castnum].info->MissileState; - castonmelee ^= 1; - if (caststate == NULL) - { - if (castonmelee) - caststate = castorder[castnum].info->MeleeState; - else - caststate = castorder[castnum].info->MissileState; - } - } - - if (castattacking) - { - if (castframes == 24 - || caststate == castorder[castnum].info->SeeState ) - { - stopattack: - castattacking = false; - castframes = 0; - caststate = castorder[castnum].info->SeeState; - } - } - - casttics = caststate->GetTics(); - if (casttics == -1) - casttics = 15; -} - - -// -// F_CastResponder -// - -bool F_CastResponder (event_t* ev) -{ - if (ev->type != EV_KeyDown) - return false; - - const char *cmd = Bindings.GetBind (ev->data1); - - if (cmd != NULL && !stricmp (cmd, "toggleconsole")) - return false; - - if (castdeath) - return true; // already in dying frames - - // go into death frame - castdeath = true; - caststate = castorder[castnum].Class->ActorInfo->FindState(NAME_Death); - if (caststate != NULL) - { - casttics = caststate->GetTics(); - castframes = 0; - castattacking = false; - if (castnum == 16) - { - int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); - if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); - } - else if (castorder[castnum].info->DeathSound) - { - S_Sound (CHAN_VOICE | CHAN_UI, castorder[castnum].info->DeathSound, 1, ATTN_NONE); - } - } - - return true; -} - -// -// F_CastDrawer -// -void F_CastDrawer (void) -{ - spriteframe_t* sprframe; - FTexture* pic; - - // erase the entire screen to a background - screen->DrawTexture (TexMan[GStrings("bgcastcall")], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), - TAG_DONE); - - screen->DrawText (SmallFont, CR_UNTRANSLATED, - (SCREENWIDTH - SmallFont->StringWidth (GStrings(castorder[castnum].name)) * CleanXfac)/2, - (SCREENHEIGHT * 180) / 200, - GStrings(castorder[castnum].name), - DTA_CleanNoMove, true, TAG_DONE); - - // draw the current frame in the middle of the screen - if (caststate != NULL) - { - sprframe = &SpriteFrames[sprites[castsprite].spriteframes + caststate->GetFrame()]; - pic = TexMan(sprframe->Texture[0]); - - screen->DrawTexture (pic, 160, 170, - DTA_320x200, true, - DTA_FlipX, sprframe->Flip & 1, - DTA_Translation, casttranslation, - TAG_DONE); - } -} - - -/* -================== -= -= F_DemonScroll -= -================== -*/ - -void F_DemonScroll () -{ - const char *tex1, *tex2; - if (EndSequences[FinaleSequence].Advanced) - { - tex1 = EndSequences[FinaleSequence].PicName; - tex2 = EndSequences[FinaleSequence].PicName2; - } - else - { - tex1 = "FINAL1"; - tex2 = "FINAL2"; - } - - int yval; - FTexture *final1 = TexMan(tex1); - FTexture *final2 = TexMan(tex2); - int fwidth = final1->GetScaledWidth(); - int fheight = final1->GetScaledHeight(); - - if (FinaleCount < 70) - { - screen->DrawTexture (final1, 0, 0, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - screen->FillBorder (NULL); - return; - } - yval = int(FinaleCount) - 70; - if (yval < 600) - { - yval = Scale (yval, fheight, 600); - screen->DrawTexture (final1, 0, yval, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - screen->DrawTexture (final2, 0, yval - fheight, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - } - else - { //else, we'll just sit here and wait, for now - screen->DrawTexture (final2, 0, 0, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - } - screen->FillBorder (NULL); -} - -/* -================== -= -= F_DrawUnderwater -= -================== -*/ -extern int NoWipe; - -void F_DrawUnderwater(void) -{ - extern EMenuState menuactive; - FTexture *pic; - - switch (FinaleStage) - { - case 1: - { - PalEntry *palette; - const BYTE *orgpal; - FMemLump lump; - int i; - - lump = Wads.ReadLump ("E2PAL"); - orgpal = (BYTE *)lump.GetMem(); - palette = screen->GetPalette (); - for (i = 256; i > 0; i--, orgpal += 3) - { - *palette++ = PalEntry (orgpal[0], orgpal[1], orgpal[2]); - } - screen->UpdatePalette (); - FinaleStage = 2; - } - // intentional fall-through - case 2: - pic = TexMan("E2END"); - screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); - screen->FillBorder (NULL); - paused = false; - menuactive = MENU_Off; - NoWipe = -1; - break; - - case 4: - { - PalEntry *palette; - int i; - - palette = screen->GetPalette (); - for (i = 0; i < 256; ++i) - { - palette[i] = GPalette.BaseColors[i]; - } - screen->UpdatePalette (); - - pic = TexMan("TITLE"); - screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); - screen->FillBorder (NULL); - NoWipe = 0; - break; - } - } -} - -/* -================== -= -= F_BunnyScroll -= -================== -*/ -void F_BunnyScroll (void) -{ - static size_t laststage; - - bool bunny = false; - int scrolled; - char name[10]; - size_t stage; - FTexture *tex; - int fwidth; - int fheight; - const char *tex1; - const char *tex2; - - if (EndSequences[FinaleSequence].Advanced) - { - tex1 = EndSequences[FinaleSequence].PicName; - tex2 = EndSequences[FinaleSequence].PicName2; - bunny = EndSequences[FinaleSequence].PlayTheEnd; - } - else if (EndSequences[FinaleSequence].EndType == END_BuyStrife) - { - tex1 = "CREDIT"; - tex2 = "VELLOGO"; - } - else - { - tex1 = "PFUB1"; - tex2 = "PFUB2"; - bunny = true; - } - - V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); - - tex = TexMan(tex1); - fwidth = tex->GetScaledWidth(); - fheight = tex->GetScaledHeight(); - - scrolled = clamp (((signed)FinaleCount-230)*fwidth/640, 0, fwidth); - - screen->DrawTexture (tex, scrolled, 0, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - - tex = TexMan(tex2); - screen->DrawTexture (tex, scrolled - fwidth, 0, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - - screen->FillBorder (NULL); - - if (bunny) - { - if (FinaleCount < 1130) - { - return; - } - if (FinaleCount < 1180) - { - screen->DrawTexture (TexMan("END0"), (320-13*8)/2, (200-8*8)/2, DTA_320x200, true, TAG_DONE); - laststage = 0; - return; - } - - stage = (FinaleCount-1180) / 5; - if (stage > 6) - stage = 6; - if (stage > laststage) - { - S_Sound (CHAN_WEAPON | CHAN_UI, "weapons/pistol", 1, ATTN_NONE); - laststage = stage; - } - - mysnprintf (name, countof(name), "END%d", (int)stage); - screen->DrawTexture (TexMan(name), (320-13*8)/2, (200-8*8)/2, DTA_320x200, true, TAG_DONE); - } -} - -//============================================================================ -// -// F_StartSlideshow -// -// Starts running the slideshow previously set up. -// -//============================================================================ - -void F_StartSlideshow () -{ - gameaction = ga_nothing; - gamestate = GS_FINALE; - wipegamestate = GS_FINALE; - viewactive = false; - automapactive = false; - - S_StopAllChannels (); - S_ChangeMusic ("D_DARK", 0, true); - V_SetBlend (0,0,0,0); - - // The slideshow is determined solely by the map you're on. - if (!multiplayer && level.flags2 & LEVEL2_DEATHSLIDESHOW) - { - FinalePart = 14; - } - else switch (level.levelnum) - { - case 3: - FinalePart = 1; - break; - - case 10: - FinalePart = 5; - break; - - default: - FinalePart = -99; - break; - } - - FinaleCount = 0; - FinaleStage = 5; - FinaleEndCount = 0; -} - -//============================================================================ -// -// F_AdvanceSlideshow -// -//============================================================================ - -void F_AdvanceSlideshow () -{ - switch (FinalePart) - { - case -99: - if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) - S_ChangeMusic (level.Music, level.musicorder); - gamestate = GS_LEVEL; - wipegamestate = GS_LEVEL; - P_ResumeConversation (); - viewactive = true; - break; - - case -1: - wipegamestate = GS_FORCEWIPEFADE; - FinaleStage = 6; - S_StartMusic ("D_FAST"); - break; - - // Macil's speech on map 3 about the Programmer. - case 1: - FinaleFlat = "SS2F1"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac10", 1, ATTN_NORM); - FinalePart = 2; - FinaleEndCount = 9 * TICRATE; - break; - - case 2: - FinaleFlat = "SS2F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac11", 1, ATTN_NORM); - FinalePart = 3; - FinaleEndCount = 10 * TICRATE; - break; - - case 3: - FinaleFlat = "SS2F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac12", 1, ATTN_NORM); - FinalePart = 4; - FinaleEndCount = 12 * TICRATE; - break; - - case 4: - FinaleFlat = "SS2F4"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac13", 1, ATTN_NORM); - FinalePart = -99; - FinaleEndCount = 17 * TICRATE; - break; - - // Macil's speech on map 10 about the Sigil. - case 5: - FinaleFlat = "SS3F1"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac16", 1, ATTN_NORM); - FinalePart = 6; - FinaleEndCount = 10 * TICRATE; - break; - - case 6: - FinaleFlat = "SS3F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac17", 1, ATTN_NORM); - FinalePart = 7; - FinaleEndCount = 12 * TICRATE; - break; - - case 7: - FinaleFlat = "SS3F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac18", 1, ATTN_NORM); - FinalePart = 8; - FinaleEndCount = 12 * TICRATE; - break; - - case 8: - FinaleFlat = "SS3F4"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac19", 1, ATTN_NORM); - FinaleEndCount = 11 * TICRATE; - FinalePart = -99; - break; - - // You won! You are a hero! - case 10: - FinaleFlat = "SS4F1"; - S_StartMusic ("D_HAPPY"); - S_Sound (CHAN_VOICE | CHAN_UI, "svox/rie01", 1, ATTN_NORM); - FinaleEndCount = 13 * TICRATE; - FinalePart = 11; - break; - - case 11: - FinaleFlat = "SS4F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/bbx01", 1, ATTN_NORM); - FinaleEndCount = 11 * TICRATE; - FinalePart = 12; - break; - - case 12: - FinaleFlat = "SS4F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/bbx02", 1, ATTN_NORM); - FinaleEndCount = 14 * TICRATE; - FinalePart = 13; - break; - - case 13: - FinaleFlat = "SS4F4"; - FinaleEndCount = 28 * TICRATE; - FinalePart = -1; - break; - - // You are dead! All hope is lost! - case 14: - S_StartMusic ("D_SAD"); - FinaleFlat = "SS5F1"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss501b", 1, ATTN_NORM); - FinalePart = 15; - FinaleEndCount = 11 * TICRATE; - break; - - case 15: - FinaleFlat = "SS5F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss502b", 1, ATTN_NORM); - FinalePart = 16; - FinaleEndCount = 10 * TICRATE; - break; - - case 16: - FinaleFlat = "SS5F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss503b", 1, ATTN_NORM); - FinalePart = -1; - FinaleEndCount = 11 * TICRATE; - break; - - // You won, but at what cost? - case 17: - S_StartMusic ("D_END"); - FinaleFlat = "SS6F1"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss601a", 1, ATTN_NORM); - FinaleEndCount = 8 * TICRATE; - FinalePart = 18; - break; - - case 18: - FinaleFlat = "SS6F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss602a", 1, ATTN_NORM); - FinalePart = 19; - FinaleEndCount = 8 * TICRATE; - break; - - case 19: - FinaleFlat = "SS6F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss603a", 1, ATTN_NORM); - FinalePart = -1; - FinaleEndCount = 9 * TICRATE; - break; - } -} - -// -// F_Drawer -// -void F_Drawer (void) -{ - const char *picname = NULL; - - switch (FinaleStage) - { - case 0: // Intermission or end-of-episode text - // erase the entire screen to a tiled background (or picture) - if (!FinaleHasPic) - { - FTextureID picnum = TexMan.CheckForTexture (FinaleFlat, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); - if (picnum.isValid()) - { - screen->FlatFill (0,0, SCREENWIDTH, SCREENHEIGHT, TexMan[picnum]); - } - else - { - screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0); - } - } - else - { - picname = FinaleFlat; - } - V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); - break; - - case 1: - case 2: - case 4: - switch (EndSequences[FinaleSequence].EndType) - { - default: - case END_Pic1: - picname = gameinfo.GetFinalePage(1); - screen->DrawTexture (TexMan[picname], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), TAG_DONE); - break; - case END_Pic2: - picname = gameinfo.GetFinalePage(2); - screen->DrawTexture (TexMan[picname], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), TAG_DONE); - break; - case END_Pic3: - picname = gameinfo.GetFinalePage(3); - screen->DrawTexture (TexMan[picname], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), TAG_DONE); - break; - case END_Pic: - picname = EndSequences[FinaleSequence].PicName; - break; - case END_Bunny: - case END_BuyStrife: - F_BunnyScroll (); - break; - case END_Underwater: - F_DrawUnderwater (); - break; - case END_Demon: - F_DemonScroll (); - break; - case END_TitleScreen: - D_StartTitle (); - break; - } - break; - - case 3: - F_CastDrawer (); - break; - - case 5: - picname = FinaleFlat; - break; - - case 6: - picname = "CREDIT"; - break; - - case 10: - case 11: - picname = "FINALE1"; - break; - - case 12: - case 13: - picname = "FINALE2"; - break; - - case 14: - case 15: - picname = "FINALE3"; - break; - } - if (picname != NULL) - { - FTexture *pic = TexMan[picname]; - screen->DrawTexture (pic, 0, 0, DTA_Fullscreen, true, TAG_DONE); - screen->FillBorder (NULL); - if (FinaleStage >= 14) - { // Chess pic, draw the correct character graphic - double w = pic->GetScaledWidthDouble(); - double h = pic->GetScaledHeightDouble(); - if (multiplayer) - { - screen->DrawTexture (TexMan["CHESSALL"], 20, 0, - DTA_VirtualWidthF, w, - DTA_VirtualHeightF, h, TAG_DONE); - } - else if (players[consoleplayer].CurrentPlayerClass > 0) - { - picname = players[consoleplayer].CurrentPlayerClass == 1 ? "CHESSC" : "CHESSM"; - screen->DrawTexture (TexMan[picname], 60, 0, - DTA_VirtualWidthF, w, - DTA_VirtualHeightF, h, TAG_DONE); - } - } - } - switch (FinaleStage) - { - case 0: - case 11: - case 12: - case 15: - F_TextWrite (); - break; - } -} - -//========================================================================== -// -// GetFinaleText -// -//========================================================================== - -static void GetFinaleText (const char *msgLumpName) -{ - int msgLump; - - msgLump = Wads.CheckNumForFullName(msgLumpName, true); - if (msgLump != -1) - { - char *textbuf; - FinaleTextLen = Wads.LumpLength(msgLump); - textbuf = (char *)alloca (FinaleTextLen + 1); - Wads.ReadLump (msgLump, textbuf); - textbuf[FinaleTextLen] = '\0'; - FinaleText = textbuf; - } - else - { - FinaleText = "Unknown message "; - FinaleText += msgLumpName; - FinaleTextLen = FinaleText.Len(); - } -} diff --git a/src/f_finale.h b/src/f_finale.h deleted file mode 100644 index 7eb2884d0..000000000 --- a/src/f_finale.h +++ /dev/null @@ -1,53 +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: -// -// -//----------------------------------------------------------------------------- - - -#ifndef __F_FINALE__ -#define __F_FINALE__ - -#include "basictypes.h" - -struct event_t; - - -// -// FINALE -// - -// Called by main loop. -bool F_Responder (event_t* ev); - -// Called by main loop. -void F_Ticker (); - -// Called by main loop. -void F_Drawer (); - - -void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, - const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, - bool ending, int endsequence = 0); - -void F_StartSlideshow (); - -void F_EndFinale (); - -#endif diff --git a/src/g_game.cpp b/src/g_game.cpp index 35e740f40..1153a9e7c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -37,7 +37,7 @@ #include "doomstat.h" #include "d_protocol.h" #include "d_netinf.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "m_argv.h" #include "m_misc.h" #include "menu/menu.h" @@ -1031,7 +1031,7 @@ void G_Ticker () G_DoCompleted (); break; case ga_slideshow: - F_StartSlideshow (); + if (gamestate == GS_LEVEL) F_StartIntermission(level.info->slideshow, FSTATE_InLevel); break; case ga_worlddone: G_DoWorldDone (); diff --git a/src/g_level.cpp b/src/g_level.cpp index 62eb809aa..7008572a7 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -51,7 +51,7 @@ #include "p_local.h" #include "r_sky.h" #include "c_console.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "gstrings.h" #include "v_video.h" #include "st_stuff.h" @@ -78,6 +78,7 @@ #include "d_netinf.h" #include "v_palette.h" #include "menu/menu.h" +#include "a_strifeglobal.h" #include "gi.h" @@ -99,22 +100,11 @@ EXTERN_CVAR (String, playerclass) #define RCLS_ID MAKE_ID('r','c','L','s') #define PCLS_ID MAKE_ID('p','c','L','s') -static void SetEndSequence (char *nextmap, int type); void G_VerifySkill(); static FRandom pr_classchoice ("RandomPlayerClassChoice"); -TArray EndSequences; - -EndSequence::EndSequence() -{ - EndType = END_Pic; - Advanced = false; - MusicLooping = false; - PlayTheEnd = false; -} - extern level_info_t TheDefaultLevelInfo; extern bool timingdemo; @@ -135,73 +125,6 @@ void *statcopy; // for statistics driver FLevelLocals level; // info about current level -//========================================================================== -// -// -//========================================================================== - -int FindEndSequence (int type, const char *picname) -{ - unsigned int i, num; - - num = EndSequences.Size (); - for (i = 0; i < num; i++) - { - if (EndSequences[i].EndType == type && !EndSequences[i].Advanced && - (type != END_Pic || stricmp (EndSequences[i].PicName, picname) == 0)) - { - return (int)i; - } - } - return -1; -} - -//========================================================================== -// -// -//========================================================================== - -static void SetEndSequence (char *nextmap, int type) -{ - int seqnum; - - seqnum = FindEndSequence (type, NULL); - if (seqnum == -1) - { - EndSequence newseq; - newseq.EndType = type; - seqnum = (int)EndSequences.Push (newseq); - } - mysnprintf(nextmap, 11, "enDSeQ%04x", (WORD)seqnum); -} - -//========================================================================== -// -// -//========================================================================== - -void G_SetForEndGame (char *nextmap) -{ - if (!strncmp(nextmap, "enDSeQ",6)) return; // If there is already an end sequence please leave it alone!!! - - if (gameinfo.gametype == GAME_Strife) - { - SetEndSequence (nextmap, gameinfo.flags & GI_SHAREWARE ? END_BuyStrife : END_Strife); - } - else if (gameinfo.gametype == GAME_Hexen) - { - SetEndSequence (nextmap, END_Chess); - } - else if (gameinfo.gametype == GAME_Doom && (gameinfo.flags & GI_MAPxx)) - { - SetEndSequence (nextmap, END_Cast); - } - else - { // The ExMx games actually have different ends based on the episode, - // but I want to keep this simple. - SetEndSequence (nextmap, END_Pic1); - } -} //========================================================================== // @@ -541,7 +464,6 @@ static bool unloading; // //========================================================================== - void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill) { level_info_t *nextinfo = NULL; @@ -552,7 +474,20 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill return; } - if (strncmp(levelname, "enDSeQ", 6) != 0) + if (levelname == NULL || *levelname == 0) + { + // end the game + levelname = NULL; + if (!strncmp(level.nextmap, "enDSeQ",6)) + { + levelname = level.nextmap; // If there is already an end sequence please leave it alone! + } + else + { + nextlevel.Format("enDSeQ%04x", int(gameinfo.DefaultEndSequence)); + } + } + else if (strncmp(levelname, "enDSeQ", 6) != 0) { nextinfo = FindLevelInfo (levelname); if (nextinfo != NULL) @@ -566,7 +501,7 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill } } - nextlevel = levelname; + if (levelname != NULL) nextlevel = levelname; if (nextSkill != -1) NextSkill = nextSkill; @@ -1026,13 +961,28 @@ void G_WorldDone (void) if (strncmp (nextlevel, "enDSeQ", 6) == 0) { + FName endsequence = ENamedName(strtol(nextlevel.GetChars()+6, NULL, 16)); + // Strife needs a special case here to choose between good and sad ending. Bad is handled elsewherw. + if (endsequence == NAME_Inter_Strife) + { + if (players[0].mo->FindInventory (QuestItemClasses[24]) || + players[0].mo->FindInventory (QuestItemClasses[27])) + { + endsequence = NAME_Inter_Strife_Good; + } + else + { + endsequence = NAME_Inter_Strife_Sad; + } + } + F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder, thiscluster->cdtrack, thiscluster->cdid, thiscluster->FinaleFlat, thiscluster->ExitText, thiscluster->flags & CLUSTER_EXITTEXTINLUMP, thiscluster->flags & CLUSTER_FINALEPIC, thiscluster->flags & CLUSTER_LOOKUPEXITTEXT, - true, strtol(nextlevel.GetChars()+6, NULL, 16)); + true, endsequence); } else { diff --git a/src/g_level.h b/src/g_level.h index f04e222d5..d7add35b5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -54,6 +54,8 @@ class FScanner; #define GCC_YSEG __attribute__((section(SECTION_YREG))) #endif +struct FIntermissionDescriptor; +struct FIntermissionAction; struct FMapInfoParser { @@ -98,6 +100,11 @@ struct FMapInfoParser bool CheckFloat(); void SkipToNext(); void CheckEndOfFile(const char *block); + + void ParseIntermissionAction(FIntermissionDescriptor *Desc); + void ParseIntermission(); + FName CheckEndSequence(); + FName ParseEndGame(); }; #define DEFINE_MAP_OPTION(name, old) \ @@ -163,7 +170,7 @@ enum ELevelFlags LEVEL_VISITED = 0x80000000, // Used for intermission map // The flags QWORD is now split into 2 DWORDs - LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death + //LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed @@ -251,7 +258,7 @@ struct level_info_t char mapname[9]; char pname[9]; - char nextmap[11]; // The endsequence string is 10 chars so we need more space here + char nextmap[11]; char secretmap[11]; char skypic1[9]; char skypic2[9]; @@ -286,6 +293,9 @@ struct level_info_t DWORD compatmask; FString Translator; // for converting Doom-format linedef and sector types. int DefaultEnvironment; // Default sound environment for the map. + FName Intermission; + FName deathsequence; + FName slideshow; // Redirection: If any player is carrying the specified item, then // you go to the RedirectMap instead of this one. @@ -415,36 +425,6 @@ struct FLevelLocals bool IsFreelookAllowed() const; }; -enum EndTypes -{ - END_Pic, - END_Pic1, - END_Pic2, - END_Pic3, - END_Bunny, - END_Cast, - END_Demon, - END_Underwater, - END_Chess, - END_Strife, - END_BuyStrife, - END_TitleScreen -}; - -struct EndSequence -{ - BYTE EndType; - bool Advanced; - bool MusicLooping; - bool PlayTheEnd; - FString PicName; - FString PicName2; - FString Music; - - EndSequence(); -}; - -extern TArray EndSequences; struct cluster_info_t { @@ -509,8 +489,6 @@ enum void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill=-1); -void G_SetForEndGame (char *nextmap); - void G_StartTravel (); void G_FinishTravel (); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 42beba873..e15de22a2 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -53,9 +53,6 @@ #include "version.h" #include "v_text.h" -int FindEndSequence (int type, const char *picname); - - TArray wadclusterinfos; TArray wadlevelinfos; @@ -757,9 +754,6 @@ void FMapInfoParser::ParseCluster() void FMapInfoParser::ParseNextMap(char *mapname) { - EndSequence newSeq; - bool useseq = false; - if (sc.CheckNumber()) { if (HexenHack) @@ -773,163 +767,14 @@ void FMapInfoParser::ParseNextMap(char *mapname) } else { - + *mapname = 0; sc.MustGetString(); - if (sc.Compare("endgame")) + strncpy (mapname, sc.String, 8); + mapname[8] = 0; + FName seq = CheckEndSequence(); + if (seq != NAME_None) { - if (!sc.CheckString("{")) - { - // Make Demon Eclipse work again - sc.UnGet(); - goto standard_endgame; - } - newSeq.Advanced = true; - newSeq.EndType = END_Pic1; - newSeq.PlayTheEnd = false; - newSeq.MusicLooping = true; - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("pic")) - { - ParseAssign(); - sc.MustGetString(); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - } - else if (sc.Compare("hscroll")) - { - ParseAssign(); - newSeq.EndType = END_Bunny; - sc.MustGetString(); - newSeq.PicName = sc.String; - ParseComma(); - sc.MustGetString(); - newSeq.PicName2 = sc.String; - if (CheckNumber()) - newSeq.PlayTheEnd = !!sc.Number; - } - else if (sc.Compare("vscroll")) - { - ParseAssign(); - newSeq.EndType = END_Demon; - sc.MustGetString(); - newSeq.PicName = sc.String; - ParseComma(); - sc.MustGetString(); - newSeq.PicName2 = sc.String; - } - else if (sc.Compare("cast")) - { - newSeq.EndType = END_Cast; - } - else if (sc.Compare("music")) - { - ParseAssign(); - sc.MustGetString(); - newSeq.Music = sc.String; - if (CheckNumber()) - { - newSeq.MusicLooping = !!sc.Number; - } - } - else - { - if (format_type == FMT_New) - { - // Unknown - sc.ScriptMessage("Unknown property '%s' found in endgame definition\n", sc.String); - SkipToNext(); - } - else - { - sc.ScriptError("Unknown property '%s' found in endgame definition\n", sc.String); - } - - } - } - useseq = true; - } - else if (strnicmp (sc.String, "EndGame", 7) == 0) - { - int type; - switch (sc.String[7]) - { - case '1': type = END_Pic1; break; - case '2': type = END_Pic2; break; - case '3': type = END_Bunny; break; - case 'C': type = END_Cast; break; - case 'W': type = END_Underwater; break; - case 'S': type = END_Strife; break; - standard_endgame: - default: type = END_Pic3; break; - } - newSeq.EndType = type; - useseq = true; - } - else if (sc.Compare("endpic")) - { - ParseComma(); - sc.MustGetString (); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - useseq = true; - } - else if (sc.Compare("endbunny")) - { - newSeq.EndType = END_Bunny; - useseq = true; - } - else if (sc.Compare("endcast")) - { - newSeq.EndType = END_Cast; - useseq = true; - } - else if (sc.Compare("enddemon")) - { - newSeq.EndType = END_Demon; - useseq = true; - } - else if (sc.Compare("endchess")) - { - newSeq.EndType = END_Chess; - useseq = true; - } - else if (sc.Compare("endunderwater")) - { - newSeq.EndType = END_Underwater; - useseq = true; - } - else if (sc.Compare("endbuystrife")) - { - newSeq.EndType = END_BuyStrife; - useseq = true; - } - else if (sc.Compare("endtitle")) - { - newSeq.EndType = END_TitleScreen; - useseq = true; - } - else - { - strncpy (mapname, sc.String, 8); - mapname[8] = 0; - } - if (useseq) - { - int seqnum = -1; - - if (!newSeq.Advanced) - { - seqnum = FindEndSequence (newSeq.EndType, newSeq.PicName); - } - - if (seqnum == -1) - { - seqnum = (int)EndSequences.Push (newSeq); - } - // mapname can point to nextmap and secretmap which are both 12 characters long - mysnprintf(mapname, 11, "enDSeQ%04x", (WORD)seqnum); + mysnprintf(mapname, 11, "enDSeQ%04x", int(seq)); } } } @@ -1229,6 +1074,20 @@ DEFINE_MAP_OPTION(translator, true) info->Translator = parse.sc.String; } +DEFINE_MAP_OPTION(deathsequence, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->deathsequence = parse.sc.String; +} + +DEFINE_MAP_OPTION(slideshow, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->slideshow = parse.sc.String; +} + DEFINE_MAP_OPTION(bordertexture, true) { parse.ParseAssign(); @@ -1351,7 +1210,7 @@ MapFlagHandlers[] = { "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, - { "deathslideshow", MITYPE_SETFLAG2, LEVEL2_DEATHSLIDESHOW, 0 }, + { "deathslideshow", MITYPE_SETFLAG2, 0, 0 }, { "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, @@ -1908,6 +1767,18 @@ void FMapInfoParser::ParseMapInfo (int lump, level_info_t &gamedefaults, level_i sc.ScriptError("gameinfo definitions not supported with old MAPINFO syntax"); } } + else if (sc.Compare("intermission")) + { + if (format_type != FMT_Old) + { + format_type = FMT_New; + ParseIntermission(); + } + else + { + sc.ScriptError("intermission definitions not supported with old MAPINFO syntax"); + } + } else { sc.ScriptError("%s: Unknown top level keyword", sc.String); @@ -1965,7 +1836,6 @@ void G_ParseMapInfo (const char *basemapinfo) level_info_t defaultinfo; parse.ParseMapInfo(lump, gamedefaults, defaultinfo); } - EndSequences.ShrinkToFit (); if (AllEpisodes.Size() == 0) { diff --git a/src/g_strife/a_programmer.cpp b/src/g_strife/a_programmer.cpp index 196fae584..6a65ef9e3 100644 --- a/src/g_strife/a_programmer.cpp +++ b/src/g_strife/a_programmer.cpp @@ -6,7 +6,6 @@ #include "p_enemy.h" #include "s_sound.h" #include "a_strifeglobal.h" -#include "f_finale.h" #include "thingdef/thingdef.h" #include "g_level.h" #include "doomstat.h" diff --git a/src/gi.cpp b/src/gi.cpp index c8ca25b89..18f8a6674 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -297,6 +297,9 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRING(mFontColorHighlight, "menufontcolor_highlight") GAMEINFOKEY_STRING(mFontColorSelection, "menufontcolor_selection") GAMEINFOKEY_CSTRING(mBackButton, "menubackbutton", 8) + GAMEINFOKEY_INT(TextScreenX, "textscreenx") + GAMEINFOKEY_INT(TextScreenY, "textscreeny") + GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence") else { diff --git a/src/gi.h b/src/gi.h index 40ddfb9ad..e71b0f783 100644 --- a/src/gi.h +++ b/src/gi.h @@ -125,6 +125,9 @@ struct gameinfo_t FName mFontColorSelection; char mBackButton[9]; fixed_t gibfactor; + int TextScreenX; + int TextScreenY; + FName DefaultEndSequence; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp new file mode 100644 index 000000000..461495f63 --- /dev/null +++ b/src/intermission/intermission.cpp @@ -0,0 +1,875 @@ +/* +** intermission.cpp +** Framework for intermissions (text screens, slideshows, etc) +** +**--------------------------------------------------------------------------- +** 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_event.h" +#include "w_wad.h" +#include "gi.h" +#include "v_video.h" +#include "v_palette.h" +#include "d_main.h" +#include "gstrings.h" +#include "intermission/intermission.h" +#include "actor.h" +#include "d_player.h" +#include "r_state.h" +#include "r_translate.h" +#include "c_bind.h" +#include "g_level.h" +#include "p_conversation.h" +#include "menu/menu.h" + +FIntermissionDescriptorList IntermissionDescriptors; + +IMPLEMENT_CLASS(DIntermissionScreen) +IMPLEMENT_CLASS(DIntermissionScreenFader) +IMPLEMENT_CLASS(DIntermissionScreenText) +IMPLEMENT_CLASS(DIntermissionScreenCast) +IMPLEMENT_CLASS(DIntermissionScreenScroller) +IMPLEMENT_POINTY_CLASS(DIntermissionController) + DECLARE_POINTER(mScreen) +END_POINTERS + +extern int NoWipe; + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreen::Init(FIntermissionAction *desc, bool first) +{ + int lumpnum; + + if (desc->mCdTrack == 0 || !S_ChangeCDMusic (desc->mCdTrack, desc->mCdId)) + { + if (desc->mMusic.IsEmpty()) + { + // only start the default music if this is the first action in an intermission + if (first) S_ChangeMusic (gameinfo.finaleMusic, 0, desc->mMusicLooping); + } + else + { + S_ChangeMusic (desc->mMusic, desc->mMusicOrder, desc->mMusicLooping); + } + } + mDuration = desc->mDuration; + + const char *texname = desc->mBackground; + if (*texname == '@') + { + char *pp; + unsigned int v = strtoul(texname+1, &pp, 10) - 1; + if (*pp == 0 && v < gameinfo.finalePages.Size()) + { + texname = gameinfo.finalePages[v].GetChars(); + } + else if (gameinfo.finalePages.Size() > 0) + { + texname = gameinfo.finalePages[0].GetChars(); + } + else + { + texname = gameinfo.titlePage; + } + } + else if (*texname == '$') + { + texname = GStrings[texname+1]; + } + FTextureID tex = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); + if (tex.isValid()) + { + mBackground = tex; + mFlatfill = desc->mFlatfill; + } + S_Sound (CHAN_VOICE | CHAN_UI, desc->mSound, 1.0f, ATTN_NONE); + if (desc->mPalette.IsNotEmpty() && (lumpnum = Wads.CheckNumForFullName(desc->mPalette, true)) > 0) + { + PalEntry *palette; + const BYTE *orgpal; + FMemLump lump; + int i; + + lump = Wads.ReadLump (lumpnum); + orgpal = (BYTE *)lump.GetMem(); + palette = screen->GetPalette (); + for (i = 256; i > 0; i--, orgpal += 3) + { + *palette++ = PalEntry (orgpal[0], orgpal[1], orgpal[2]); + } + screen->UpdatePalette (); + mPaletteChanged = true; + NoWipe = 1; + M_EnableMenu(false); + } + mOverlays.Resize(desc->mOverlays.Size()); + for (unsigned i=0; i < mOverlays.Size(); i++) + { + mOverlays[i].x = desc->mOverlays[i].x; + mOverlays[i].y = desc->mOverlays[i].y; + mOverlays[i].mCondition = desc->mOverlays[i].mCondition; + mOverlays[i].mPic = TexMan.CheckForTexture(desc->mOverlays[i].mName, FTexture::TEX_MiscPatch); + } + mTicker = 0; +} + + +int DIntermissionScreen::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + return -1; + } + return 0; +} + +int DIntermissionScreen::Ticker () +{ + if (++mTicker >= mDuration && mDuration > 0) return -1; + return 0; +} + +bool DIntermissionScreen::CheckOverlay(int i) +{ + if (mOverlays[i].mCondition == NAME_Multiplayer && !multiplayer) return false; + else if (mOverlays[i].mCondition != NAME_None) + { + if (multiplayer || players[0].mo == NULL) return false; + const PClass *cls = PClass::FindClass(mOverlays[i].mCondition); + if (cls == NULL) return false; + if (!players[0].mo->IsKindOf(cls)) return false; + } + return true; +} + +void DIntermissionScreen::Drawer () +{ + if (mBackground.isValid()) + { + if (!mFlatfill) + { + screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, TAG_DONE); + } + else + { + screen->FlatFill (0,0, SCREENWIDTH, SCREENHEIGHT, TexMan[mBackground]); + } + } + else + { + screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0); + } + for (unsigned i=0; i < mOverlays.Size(); i++) + { + if (CheckOverlay(i)) + screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, TAG_DONE); + } + if (!mFlatfill) screen->FillBorder (NULL); +} + +void DIntermissionScreen::Destroy() +{ + if (mPaletteChanged) + { + PalEntry *palette; + int i; + + palette = screen->GetPalette (); + for (i = 0; i < 256; ++i) + { + palette[i] = GPalette.BaseColors[i]; + } + screen->UpdatePalette (); + NoWipe = 5; + mPaletteChanged = false; + M_EnableMenu(true); + } + S_StopSound(CHAN_VOICE); + Super::Destroy(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenFader::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mType = static_cast(desc)->mFadeType; +} + +//=========================================================================== +// +// FadePic +// +//=========================================================================== + +int DIntermissionScreenFader::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + V_SetBlend(0,0,0,0); + return -1; + } + return Super::Responder(ev); +} + +int DIntermissionScreenFader::Ticker () +{ + if (mFlatfill || !mBackground.isValid()) return -1; + return Super::Ticker(); +} + +void DIntermissionScreenFader::Drawer () +{ + if (!mFlatfill && mBackground.isValid()) + { + double factor = clamp(double(mTicker) / mDuration, 0., 1.); + if (mType == FADE_In) factor = 1.0 - factor; + int color = MAKEARGB(xs_RoundToInt(factor*255), 0,0,0); + + if (screen->Begin2D(false)) + { + screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, DTA_ColorOverlay, color, TAG_DONE); + for (unsigned i=0; i < mOverlays.Size(); i++) + { + if (CheckOverlay(i)) + screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, DTA_ColorOverlay, color, TAG_DONE); + } + screen->FillBorder (NULL); + } + else + { + V_SetBlend (0,0,0,int(256*factor)); + Super::Drawer(); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenText::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mText = static_cast(desc)->mText; + if (mText[0] == '$') mText = GStrings[&mText[1]]; + mTextSpeed = static_cast(desc)->mTextSpeed; + mTextX = static_cast(desc)->mTextX; + if (mTextX < 0) mTextX =gameinfo.TextScreenX; + mTextY = static_cast(desc)->mTextY; + if (mTextY < 0) mTextY =gameinfo.TextScreenY; + mTextLen = (int)strlen(mText); + mTextDelay = static_cast(desc)->mTextDelay; + mTextColor = static_cast(desc)->mTextColor; + // For text screens, the duration only counts when the text is complete. + if (mDuration > 0) mDuration += mTextDelay + mTextSpeed * mTextLen; +} + +int DIntermissionScreenText::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + if (mTicker < mTextDelay + (mTextLen * mTextSpeed)) + { + mTicker = mTextDelay + (mTextLen * mTextSpeed); + return 1; + } + } + return Super::Responder(ev); +} + +void DIntermissionScreenText::Drawer () +{ + Super::Drawer(); + if (mTicker >= mTextDelay) + { + FTexture *pic; + int w; + size_t count; + int c; + const FRemapTable *range; + + // draw some of the text onto the screen + int rowheight = SmallFont->GetHeight () + (gameinfo.gametype & (GAME_DoomStrifeChex) ? 3 : -1); + bool scale = (CleanXfac != 1 || CleanYfac != 1); + + int cx = mTextX; + int cy = mTextY; + const char *ch = mText; + + count = (mTicker - mTextDelay) / mTextSpeed; + range = SmallFont->GetColorTranslation (mTextColor); + + for ( ; count > 0 ; count-- ) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = mTextX; + cy += rowheight; + continue; + } + + pic = SmallFont->GetChar (c, &w); + if (cx+w > SCREENWIDTH) + continue; + if (pic != NULL) + { + if (scale) + { + screen->DrawTexture (pic, + cx,// + 320 / 2, + cy,// + 200 / 2, + DTA_Translation, range, + DTA_Clean, true, + TAG_DONE); + } + else + { + screen->DrawTexture (pic, + cx,// + 320 / 2, + cy,// + 200 / 2, + DTA_Translation, range, + TAG_DONE); + } + } + cx += w; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenCast::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mName = static_cast(desc)->mName; + mClass = PClass::FindClass(static_cast(desc)->mCastClass); + if (mClass != NULL) mDefaults = GetDefaultByType(mClass); + else mDefaults = NULL; + + mCastSounds.Resize(static_cast(desc)->mCastSounds.Size()); + for (unsigned i=0; i < mCastSounds.Size(); i++) + { + mCastSounds[i].mSequence = static_cast(desc)->mCastSounds[i].mSequence; + mCastSounds[i].mIndex = static_cast(desc)->mCastSounds[i].mIndex; + mCastSounds[i].mSound = static_cast(desc)->mCastSounds[i].mSound; + } + caststate = mDefaults->SeeState; + if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + { + advplayerstate = mDefaults->MissileState; + castsprite = skins[players[consoleplayer].userinfo.skin].sprite; + casttranslation = translationtables[TRANSLATION_Players][consoleplayer]; + } + else + { + advplayerstate = NULL; + if (caststate != NULL) castsprite = caststate->sprite; + else castsprite = -1; + casttranslation = NULL; + } + castdeath = false; + castframes = 0; + castonmelee = 0; + castattacking = false; + if (mDefaults->SeeSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->SeeSound, 1, ATTN_NONE); + } +} + +int DIntermissionScreenCast::Responder (event_t *ev) +{ + if (ev->type != EV_KeyDown) return 0; + + if (castdeath) + return 1; // already in dying frames + + castdeath = true; + caststate = mClass->ActorInfo->FindState(NAME_Death); + if (caststate == NULL) return -1; + + casttics = caststate->GetTics(); + castframes = 0; + castattacking = false; + + if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + { + int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); + if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); + } + else if (mDefaults->DeathSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->DeathSound, 1, ATTN_NONE); + } + return true; +} + +int DIntermissionScreenCast::Ticker () +{ + Super::Ticker(); + + if (--casttics > 0 && caststate != NULL) + return 0; // not time to change state yet + + if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL) + { + return -1; + } + else + { + // sound hacks.... + if (caststate != NULL && castattacking) + { + for (unsigned i = 0; i < mCastSounds.Size(); i++) + { + if ((!!mCastSounds[i].mSequence) == (basestate != mDefaults->MissileState) && + (caststate == basestate + mCastSounds[i].mIndex - 1)) + { + S_StopAllChannels (); + S_Sound (CHAN_WEAPON | CHAN_UI, mCastSounds[i].mSound, 1, ATTN_NONE); + break; + } + } + } + + // just advance to next state in animation + if (caststate == advplayerstate) + goto stopattack; // Oh, gross hack! + + caststate = caststate->GetNextState(); + castframes++; + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + basestate = caststate = mDefaults->MeleeState; + else + basestate = caststate = mDefaults->MissileState; + castonmelee ^= 1; + if (caststate == NULL) + { + if (castonmelee) + basestate = caststate = mDefaults->MeleeState; + else + basestate = caststate = mDefaults->MissileState; + } + } + + if (castattacking) + { + if (castframes == 24 || caststate == mDefaults->SeeState ) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = mDefaults->SeeState; + } + } + + casttics = caststate->GetTics(); + if (casttics == -1) + casttics = 15; + return 0; +} + +void DIntermissionScreenCast::Drawer () +{ + spriteframe_t* sprframe; + FTexture* pic; + + Super::Drawer(); + + const char *name = mName; + if (name != NULL) + { + if (*name == '$') name = GStrings(name+1); + screen->DrawText (SmallFont, CR_UNTRANSLATED, + (SCREENWIDTH - SmallFont->StringWidth (name) * CleanXfac)/2, + (SCREENHEIGHT * 180) / 200, + name, + DTA_CleanNoMove, true, TAG_DONE); + } + + // draw the current frame in the middle of the screen + if (caststate != NULL) + { + sprframe = &SpriteFrames[sprites[castsprite].spriteframes + caststate->GetFrame()]; + pic = TexMan(sprframe->Texture[0]); + + screen->DrawTexture (pic, 160, 170, + DTA_320x200, true, + DTA_FlipX, sprframe->Flip & 1, + DTA_Translation, casttranslation, + TAG_DONE); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenScroller::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mFirstPic = mBackground; + mSecondPic = TexMan.CheckForTexture(static_cast(desc)->mSecondPic, FTexture::TEX_MiscPatch); + mScrollDelay = static_cast(desc)->mScrollDelay; + mScrollTime = static_cast(desc)->mScrollTime; + mScrollDir = static_cast(desc)->mScrollDir; +} + +void DIntermissionScreenScroller::Drawer () +{ + FTexture *tex = TexMan[mFirstPic]; + FTexture *tex2 = TexMan[mSecondPic]; + if (mTicker >= mScrollDelay && mTicker < mScrollDelay + mScrollTime && tex != NULL && tex2 != NULL) + { + + int fwidth = tex->GetScaledWidth(); + int fheight = tex->GetScaledHeight(); + + double xpos1 = 0, ypos1 = 0, xpos2 = 0, ypos2 = 0; + + switch (mScrollDir) + { + case SCROLL_Up: + ypos1 = double(mTicker - mScrollDelay) * fheight / mScrollTime; + ypos2 = ypos1 - fheight; + break; + + case SCROLL_Down: + ypos1 = -double(mTicker - mScrollDelay) * fheight / mScrollTime; + ypos2 = ypos1 + fheight; + break; + + case SCROLL_Left: + default: + xpos1 = double(mTicker - mScrollDelay) * fwidth / mScrollTime; + xpos2 = xpos1 - fwidth; + break; + + case SCROLL_Right: + xpos1 = -double(mTicker - mScrollDelay) * fwidth / mScrollTime; + xpos2 = xpos1 + fwidth; + break; + } + + screen->DrawTexture (tex, xpos1, ypos1, + DTA_VirtualWidth, fwidth, + DTA_VirtualHeight, fheight, + DTA_Masked, false, + TAG_DONE); + screen->DrawTexture (tex2, xpos2, ypos2, + DTA_VirtualWidth, fwidth, + DTA_VirtualHeight, fheight, + DTA_Masked, false, + TAG_DONE); + + screen->FillBorder (NULL); + mBackground = mSecondPic; + } + else + { + Super::Drawer(); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +DIntermissionController *DIntermissionController::CurrentIntermission; + +DIntermissionController::DIntermissionController(FIntermissionDescriptor *Desc, bool DeleteDesc, BYTE state) +{ + mDesc = Desc; + mDeleteDesc = DeleteDesc; + mIndex = 0; + mAdvance = false; + mScreen = NULL; + mFirst = true; + mGameState = state; + NextPage(); +} + +bool DIntermissionController::NextPage () +{ + FTextureID bg; + bool fill = false; + + if (mIndex == (int)mDesc->mActions.Size() && mDesc->mLink == NAME_None) + { + // last page + return false; + } + + if (mScreen != NULL) + { + bg = mScreen->GetBackground(&fill); + mScreen->Destroy(); + } +again: + while ((unsigned)mIndex < mDesc->mActions.Size()) + { + FIntermissionAction *action = mDesc->mActions[mIndex++]; + if (action->mClass == WIPER_ID) + { + wipegamestate = static_cast(action)->mWipeType; + } + else if (action->mClass == TITLE_ID) + { + Destroy(); + D_StartTitle (); + return false; + } + else + { + // create page here + mScreen = (DIntermissionScreen*)action->mClass->CreateNew(); + mScreen->SetBackground(bg, fill); // copy last screen's background before initializing + mScreen->Init(action, mFirst); + mFirst = false; + return true; + } + } + if (mDesc->mLink != NAME_None) + { + FIntermissionDescriptor **pDesc = IntermissionDescriptors.CheckKey(mDesc->mLink); + if (pDesc != NULL) + { + if (mDeleteDesc) delete mDesc; + mDeleteDesc = false; + mIndex = 0; + mDesc = *pDesc; + goto again; + } + } + return false; +} + +bool DIntermissionController::Responder (event_t *ev) +{ + if (mScreen != NULL) + { + if (!mScreen->mPaletteChanged && ev->type == EV_KeyDown) + { + const char *cmd = Bindings.GetBind (ev->data1); + + if (cmd != NULL && !stricmp (cmd, "toggleconsole")) + return false; + } + + if (mScreen->mTicker < 2) return false; // prevent some leftover events from auto-advancing + int res = mScreen->Responder(ev); + mAdvance = (res == -1); + return !!res; + } + return false; +} + +void DIntermissionController::Ticker () +{ + if (mScreen != NULL) + { + mAdvance |= (mScreen->Ticker() == -1); + } + if (mAdvance) + { + mAdvance = false; + if (!NextPage()) + { + switch (mGameState) + { + case FSTATE_InLevel: + if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) + S_ChangeMusic (level.Music, level.musicorder); + gamestate = GS_LEVEL; + wipegamestate = GS_LEVEL; + P_ResumeConversation (); + viewactive = true; + Destroy(); + break; + + case FSTATE_ChangingLevel: + gameaction = ga_worlddone; + Destroy(); + break; + + default: + break; + } + } + } +} + +void DIntermissionController::Drawer () +{ + if (mScreen != NULL) + { + mScreen->Drawer(); + } +} + +void DIntermissionController::Destroy () +{ + Super::Destroy(); + if (mScreen != NULL) mScreen->Destroy(); + if (mDeleteDesc) delete mDesc; + mDesc = NULL; + if (CurrentIntermission == this) CurrentIntermission = NULL; +} + + +//========================================================================== +// +// starts a new intermission +// +//========================================================================== + +void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE state) +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Destroy(); + } + V_SetBlend (0,0,0,0); + S_StopAllChannels (); + gameaction = ga_nothing; + gamestate = GS_FINALE; + if (state == FSTATE_InLevel) wipegamestate = GS_FINALE; // don't wipe when within a level. + viewactive = false; + automapactive = false; + DIntermissionController::CurrentIntermission = new DIntermissionController(desc, deleteme, state); +} + + +//========================================================================== +// +// starts a new intermission +// +//========================================================================== + +void F_StartIntermission(FName seq, BYTE state) +{ + FIntermissionDescriptor **pdesc = IntermissionDescriptors.CheckKey(seq); + if (pdesc != NULL) + { + F_StartIntermission(*pdesc, false, state); + } +} + + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +bool F_Responder (event_t* ev) +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + return DIntermissionController::CurrentIntermission->Responder(ev); + } + return false; +} + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_Ticker () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Ticker(); + } +} + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_Drawer () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Drawer(); + } +} + + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_EndFinale () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Destroy(); + DIntermissionController::CurrentIntermission = NULL; + } +} diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h new file mode 100644 index 000000000..d777d46d3 --- /dev/null +++ b/src/intermission/intermission.h @@ -0,0 +1,320 @@ +#ifndef __INTERMISSION_H +#define __INTERMISSION_H + +#include "doomdef.h" +#include "dobject.h" +#include "m_fixed.h" +#include "textures/textures.h" +#include "s_sound.h" +#include "v_font.h" + +struct event_t; + +#define DECLARE_SUPER_CLASS(cls,parent) \ +private: \ + typedef parent Super; \ + typedef cls ThisClass; + +struct FIntermissionPatch +{ + FName mCondition; + FString mName; + double x, y; +}; + +struct FIIntermissionPatch +{ + FName mCondition; + FTextureID mPic; + double x, y; +}; + +struct FCastSound +{ + BYTE mSequence; + BYTE mIndex; + FString mSound; +}; + +struct FICastSound +{ + BYTE mSequence; + BYTE mIndex; + FSoundID mSound; +}; + +enum EFadeType +{ + FADE_In, + FADE_Out, +}; + +enum EScrollDir +{ + SCROLL_Left, + SCROLL_Right, + SCROLL_Up, + SCROLL_Down, +}; + +// actions that don't create objects +#define WIPER_ID ((const PClass*)intptr_t(-1)) +#define TITLE_ID ((const PClass*)intptr_t(-2)) + +//========================================================================== + +struct FIntermissionAction +{ + int mSize; + const PClass *mClass; + FString mMusic; + int mMusicOrder; + int mCdTrack; + int mCdId; + int mDuration; + FString mBackground; + FString mPalette; + FString mSound; + bool mFlatfill; + bool mMusicLooping; + TArray mOverlays; + + FIntermissionAction(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionFader : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + EFadeType mFadeType; + + FIntermissionActionFader(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionWiper : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + gamestate_t mWipeType; + + FIntermissionActionWiper(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionTextscreen : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mText; + int mTextDelay; + int mTextSpeed; + int mTextX, mTextY; + EColorRange mTextColor; + + FIntermissionActionTextscreen(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionCast : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mName; + FName mCastClass; + TArray mCastSounds; + + FIntermissionActionCast(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionScroller : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mSecondPic; + int mScrollDelay; + int mScrollTime; + int mScrollDir; + + FIntermissionActionScroller(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionDescriptor +{ + FName mLink; + TArray mActions; +}; + +typedef TMap FIntermissionDescriptorList; + +extern FIntermissionDescriptorList IntermissionDescriptors; + +//========================================================================== + +class DIntermissionScreen : public DObject +{ + DECLARE_CLASS (DIntermissionScreen, DObject) + +protected: + int mDuration; + FTextureID mBackground; + bool mFlatfill; + TArray mOverlays; + + bool CheckOverlay(int i); + +public: + int mTicker; + bool mPaletteChanged; + + DIntermissionScreen() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); + void Destroy(); + FTextureID GetBackground(bool *fill) + { + *fill = mFlatfill; + return mBackground; + } + void SetBackground(FTextureID tex, bool fill) + { + mBackground = tex; + mFlatfill = fill; + } +}; + +class DIntermissionScreenFader : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenFader, DIntermissionScreen) + + EFadeType mType; + +public: + + DIntermissionScreenFader() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); +}; + +class DIntermissionScreenText : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenText, DIntermissionScreen) + + const char *mText; + int mTextSpeed; + int mTextX, mTextY; + int mTextCounter; + int mTextDelay; + int mTextLen; + EColorRange mTextColor; + +public: + + DIntermissionScreenText() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual void Drawer (); +}; + +class DIntermissionScreenCast : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenCast, DIntermissionScreen) + + const char *mName; + const PClass *mClass; + AActor *mDefaults; + TArray mCastSounds; + + int casttics; + int castsprite; // [RH] For overriding the player sprite with a skin + const FRemapTable *casttranslation; // [RH] Draw "our hero" with their chosen suit color + FState* caststate; + FState* basestate; + FState* advplayerstate; + bool castdeath; + bool castattacking; + int castframes; + int castonmelee; + +public: + + DIntermissionScreenCast() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); +}; + +class DIntermissionScreenScroller : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenScroller, DIntermissionScreen) + + FTextureID mFirstPic; + FTextureID mSecondPic; + int mScrollDelay; + int mScrollTime; + int mScrollDir; + +public: + + DIntermissionScreenScroller() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual void Drawer (); +}; + +enum +{ + FSTATE_EndingGame = 0, + FSTATE_ChangingLevel = 1, + FSTATE_InLevel = 2 +}; + +class DIntermissionController : public DObject +{ + DECLARE_CLASS (DIntermissionController, DObject) + HAS_OBJECT_POINTERS + + FIntermissionDescriptor *mDesc; + TObjPtr mScreen; + bool mDeleteDesc; + bool mFirst; + bool mAdvance; + BYTE mGameState; + int mIndex; + + bool NextPage(); + +public: + static DIntermissionController *CurrentIntermission; + + DIntermissionController(FIntermissionDescriptor *mDesc = NULL, bool mDeleteDesc = false, BYTE state = FSTATE_ChangingLevel); + bool Responder (event_t *ev); + void Ticker (); + void Drawer (); + void Destroy(); +}; + + +// Interface for main loop +bool F_Responder (event_t* ev); +void F_Ticker (); +void F_Drawer (); +void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE state); +void F_StartIntermission(FName desc, BYTE state); +void F_EndFinale (); + +// Create an intermission from old cluster data +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, + bool ending, FName endsequence = NAME_None); + + + +#endif diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp new file mode 100644 index 000000000..91641d6a9 --- /dev/null +++ b/src/intermission/intermission_parse.cpp @@ -0,0 +1,848 @@ +/* +** intermission_parser.cpp +** Parser for intermission definitions in MAPINFO +** (both new style and old style 'ENDGAME' blocks) +** +**--------------------------------------------------------------------------- +** 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 "intermission/intermission.h" +#include "g_level.h" +#include "w_wad.h" +#include "gi.h" + + +static void ReplaceIntermission(FName intname,FIntermissionDescriptor *desc) +{ + FIntermissionDescriptor ** pDesc = IntermissionDescriptors.CheckKey(intname); + if (pDesc != NULL && *pDesc != NULL) delete *pDesc; + IntermissionDescriptors[intname] = desc; +} + +//========================================================================== +// +// FIntermissionAction +// +//========================================================================== + +FIntermissionAction::FIntermissionAction() +{ + mSize = sizeof(FIntermissionAction); + mClass = RUNTIME_CLASS(DIntermissionScreen); + mMusicOrder = + mCdId = + mCdTrack = + mDuration = 0; + mFlatfill = false; + mMusicLooping = true; +} + +bool FIntermissionAction::ParseKey(FScanner &sc) +{ + if (sc.Compare("music")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mMusic = sc.String; + mMusicOrder = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mMusicOrder = sc.Number; + } + return true; + } + else if (sc.Compare("cdmusic")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mCdTrack = sc.Number; + mCdId = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mCdId = sc.Number; + } + return true; + } + else if (sc.Compare("Time")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mDuration = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mDuration = sc.Number; + } + return true; + } + else if (sc.Compare("Background")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mBackground = sc.String; + mFlatfill = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mFlatfill = !!sc.Number; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_StringConst); + mPalette = sc.String; + } + } + return true; + } + else if (sc.Compare("Sound")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mSound = sc.String; + return true; + } + else if (sc.Compare("Draw")) + { + FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + pat->mName = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->x = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->y = sc.Number; + pat->mCondition = NAME_None; + return true; + } + else if (sc.Compare("DrawConditional")) + { + FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + pat->mCondition = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + pat->mName = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->x = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->y = sc.Number; + return true; + } + else return false; +} + +//========================================================================== +// +// FIntermissionActionFader +// +//========================================================================== + +FIntermissionActionFader::FIntermissionActionFader() +{ + mSize = sizeof(FIntermissionActionFader); + mClass = RUNTIME_CLASS(DIntermissionScreenFader); + mFadeType = FADE_In; +} + +bool FIntermissionActionFader::ParseKey(FScanner &sc) +{ + struct FadeType + { + const char *Name; + EFadeType Type; + } + const FT[] = { + { "FadeIn", FADE_In }, + { "FadeOut", FADE_Out }, + { NULL, FADE_In } + }; + + if (sc.Compare("FadeType")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&FT[0].Name, sizeof(FT[0])); + if (v != -1) mFadeType = FT[v].Type; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionWiper +// +//========================================================================== + +FIntermissionActionWiper::FIntermissionActionWiper() +{ + mSize = sizeof(FIntermissionActionWiper); + mClass = WIPER_ID; + mWipeType = GS_FORCEWIPE; +} + +bool FIntermissionActionWiper::ParseKey(FScanner &sc) +{ + struct WipeType + { + const char *Name; + gamestate_t Type; + } + const FT[] = { + { "Crossfade", GS_FORCEWIPEFADE }, + { "Melt", GS_FORCEWIPEMELT }, + { "Burn", GS_FORCEWIPEBURN }, + { "Default", GS_FORCEWIPE }, + { NULL, GS_FORCEWIPE } + }; + + if (sc.Compare("WipeType")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&FT[0].Name, sizeof(FT[0])); + if (v != -1) mWipeType = FT[v].Type; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionFader +// +//========================================================================== + +FIntermissionActionTextscreen::FIntermissionActionTextscreen() +{ + mSize = sizeof(FIntermissionActionTextscreen); + mClass = RUNTIME_CLASS(DIntermissionScreenText); + mTextSpeed = 2; + mTextX = -1; // use gameinfo defaults + mTextY = -1; + mTextColor = CR_UNTRANSLATED; + mTextDelay = 10; +} + +bool FIntermissionActionTextscreen::ParseKey(FScanner &sc) +{ + if (sc.Compare("Position")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mTextX = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + mTextY = sc.Number; + return true; + } + else if (sc.Compare("TextLump")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + int lump = Wads.CheckNumForFullName(sc.String, true); + if (lump > 0) + { + mText = Wads.ReadLump(lump).GetString(); + } + else + { + // only print an error if coming from a PWAD + if (Wads.GetLumpFile(sc.LumpNum) > 1) + sc.ScriptMessage("Unknown text lump '%s'", sc.String); + mText.Format("Unknown text lump '%s'", sc.String); + } + return true; + } + else if (sc.Compare("Text")) + { + sc.MustGetToken('='); + do + { + sc.MustGetToken(TK_StringConst); + mText << sc.String << '\n'; + } + while (sc.CheckToken(',')); + return true; + } + else if (sc.Compare("TextColor")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mTextColor = V_FindFontColor(sc.String); + return true; + } + else if (sc.Compare("TextDelay")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mTextDelay = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mTextDelay = sc.Number; + } + return true; + } + else if (sc.Compare("textspeed")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mTextSpeed = sc.Number; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionAction +// +//========================================================================== + +FIntermissionActionCast::FIntermissionActionCast() +{ + mSize = sizeof(FIntermissionActionCast); + mClass = RUNTIME_CLASS(DIntermissionScreenCast); +} + +bool FIntermissionActionCast::ParseKey(FScanner &sc) +{ + if (sc.Compare("CastName")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mName = sc.String; + return true; + } + else if (sc.Compare("CastClass")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mCastClass = sc.String; + return true; + } + else if (sc.Compare("AttackSound")) + { + static const char *const seqs[] = {"Missile", "Melee", NULL}; + FCastSound *cs = &mCastSounds[mCastSounds.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + cs->mSequence = (BYTE)sc.MatchString(seqs); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + cs->mIndex = (BYTE)sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + cs->mSound = sc.String; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionScroller +// +//========================================================================== + +FIntermissionActionScroller::FIntermissionActionScroller() +{ + mSize = sizeof(FIntermissionActionScroller); + mClass = RUNTIME_CLASS(DIntermissionScreenScroller); + mScrollDelay = 0; + mScrollTime = 640; + mScrollDir = SCROLL_Right; +} + +bool FIntermissionActionScroller::ParseKey(FScanner &sc) +{ + struct ScrollType + { + const char *Name; + EScrollDir Type; + } + const ST[] = { + { "Left", SCROLL_Left }, + { "Right", SCROLL_Right }, + { "Up", SCROLL_Up }, + { "Down", SCROLL_Down }, + { NULL, SCROLL_Left } + }; + + if (sc.Compare("ScrollDirection")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&ST[0].Name, sizeof(ST[0])); + if (v != -1) mScrollDir = ST[v].Type; + return true; + } + else if (sc.Compare("InitialDelay")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mScrollDelay = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mScrollDelay = sc.Number; + } + return true; + } + else if (sc.Compare("ScrollTime")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mScrollTime = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mScrollTime = sc.Number; + } + return true; + } + else if (sc.Compare("Background2")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mSecondPic = sc.String; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// ParseIntermission +// +//========================================================================== + +void FMapInfoParser::ParseIntermissionAction(FIntermissionDescriptor *desc) +{ + FIntermissionAction *ac = NULL; + + sc.MustGetToken(TK_Identifier); + if (sc.Compare("image")) + { + ac = new FIntermissionAction; + } + else if (sc.Compare("scroller")) + { + ac = new FIntermissionActionScroller; + } + else if (sc.Compare("cast")) + { + ac = new FIntermissionActionCast; + } + else if (sc.Compare("Fader")) + { + ac = new FIntermissionActionFader; + } + else if (sc.Compare("Wiper")) + { + ac = new FIntermissionActionWiper; + } + else if (sc.Compare("TextScreen")) + { + ac = new FIntermissionActionTextscreen; + } + else if (sc.Compare("GotoTitle")) + { + ac = new FIntermissionAction; + ac->mClass = TITLE_ID; + } + else if (sc.Compare("Link")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + desc->mLink = sc.String; + return; + } + else + { + sc.ScriptMessage("Unknown intermission type '%s'", sc.String); + } + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + bool success = false; + if (!sc.CheckToken(TK_Sound)) + { + sc.MustGetToken(TK_Identifier); + } + if (ac != NULL) + { + success = ac->ParseKey(sc); + if (!success) + { + sc.ScriptMessage("Unknown key name '%s'\n", sc.String); + } + } + if (!success) SkipToNext(); + } + if (ac != NULL) desc->mActions.Push(ac); +} + +//========================================================================== +// +// ParseIntermission +// +//========================================================================== + +void FMapInfoParser::ParseIntermission() +{ + sc.MustGetString(); + FName intname = sc.String; + FIntermissionDescriptor *desc = new FIntermissionDescriptor(); + + ReplaceIntermission(intname, desc); + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + ParseIntermissionAction(desc); + } +} + +//========================================================================== +// +// Parse old style endsequence +// +//========================================================================== + +struct EndSequence +{ + SBYTE EndType; + bool MusicLooping; + bool PlayTheEnd; + FString PicName; + FString PicName2; + FString Music; +}; + +enum EndTypes +{ + END_Pic, + END_Bunny, + END_Cast, + END_Demon +}; + +FName FMapInfoParser::ParseEndGame() +{ + EndSequence newSeq; + static int generated = 0; + + newSeq.EndType = -1; + newSeq.PlayTheEnd = false; + newSeq.MusicLooping = true; + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("pic")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.EndType = END_Pic; + newSeq.PicName = sc.String; + } + else if (sc.Compare("hscroll")) + { + ParseAssign(); + newSeq.EndType = END_Bunny; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + if (CheckNumber()) + newSeq.PlayTheEnd = !!sc.Number; + } + else if (sc.Compare("vscroll")) + { + ParseAssign(); + newSeq.EndType = END_Demon; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + } + else if (sc.Compare("cast")) + { + newSeq.EndType = END_Cast; + } + else if (sc.Compare("music")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.Music = sc.String; + if (CheckNumber()) + { + newSeq.MusicLooping = !!sc.Number; + } + } + else + { + if (format_type == FMT_New) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in endgame definition\n", sc.String); + SkipToNext(); + } + else + { + sc.ScriptError("Unknown property '%s' found in endgame definition\n", sc.String); + } + + } + } + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + FIntermissionAction *action; + + switch (newSeq.EndType) + { + case END_Pic: + action = new FIntermissionAction; + break; + + case END_Bunny: + { + FIntermissionActionScroller *bunny = new FIntermissionActionScroller; + bunny->mSecondPic = newSeq.PicName2; + bunny->mScrollDir = SCROLL_Left; + bunny->mScrollDelay = 230; + bunny->mScrollTime = 640; + bunny->mDuration = 1130; + action = bunny; + if (newSeq.PlayTheEnd) desc->mLink = "TheEnd"; + break; + } + + case END_Demon: + { + FIntermissionActionScroller *demon = new FIntermissionActionScroller; + demon->mSecondPic = newSeq.PicName2; + demon->mScrollDir = SCROLL_Up; + demon->mScrollDelay = 70; + demon->mScrollTime = 600; + action = demon; + break; + } + + case END_Cast: + action = new FIntermissionAction; + action->mDuration = 1; + desc->mLink = "Doom2Cast"; + break; + } + + action->mBackground = newSeq.PicName; + action->mMusic = newSeq.Music; + action->mMusicLooping = newSeq.MusicLooping; + + FString seq; + seq.Format("@EndSequence_%d_", generated++); + ReplaceIntermission(seq, desc); + return FName(seq); +} + +//========================================================================== +// +// Checks map name for end sequence +// +//========================================================================== + +FName FMapInfoParser::CheckEndSequence() +{ + const char *seqname = NULL; + + if (sc.Compare("endgame")) + { + if (!sc.CheckString("{")) + { + // Make Demon Eclipse work again + sc.UnGet(); + goto standard_endgame; + } + return ParseEndGame(); + } + else if (strnicmp (sc.String, "EndGame", 7) == 0) + { + switch (sc.String[7]) + { + case '1': seqname = "Inter_Pic1"; break; + case '2': seqname = "Inter_Pic2"; break; + case '3': seqname = "Inter_Bunny"; break; + case 'C': seqname = "Inter_Cast"; break; + case 'W': seqname = "Inter_Underwater"; break; + case 'S': seqname = "Inter_Strife"; break; + standard_endgame: + default: seqname = "Inter_Pic3"; break; + } + } + else if (sc.Compare("endpic")) + { + ParseComma(); + sc.MustGetString (); + FString seqname; + seqname << "@EndPic_" << sc.String; + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + FIntermissionAction *action = new FIntermissionAction; + action->mBackground = sc.String; + desc->mActions.Push(action); + ReplaceIntermission(seqname, desc); + return FName(seqname); + } + else if (sc.Compare("endbunny")) + { + seqname = "Inter_Bunny"; + } + else if (sc.Compare("endcast")) + { + seqname = "Inter_Cast"; + } + else if (sc.Compare("enddemon")) + { + seqname = "Inter_Demonscroll"; + } + else if (sc.Compare("endchess")) + { + seqname = "Inter_Chess"; + } + else if (sc.Compare("endunderwater")) + { + seqname = "Inter_Underwater"; + } + else if (sc.Compare("endbuystrife")) + { + seqname = "Inter_BuyStrife"; + } + else if (sc.Compare("endtitle")) + { + seqname = "Inter_Titlescreen"; + } + + if (seqname != NULL) + { + return FName(seqname); + } + return NAME_None; +} + + +//========================================================================== +// +// Creates an intermission from the cluster's finale info +// +//========================================================================== + +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, + bool ending, FName endsequence) +{ + if (text != NULL && *text != 0) + { + FIntermissionActionTextscreen *textscreen = new FIntermissionActionTextscreen; + if (textInLump) + { + int lump = Wads.CheckNumForFullName(text, true); + if (lump > 0) + { + textscreen->mText = Wads.ReadLump(lump).GetString(); + } + else + { + textscreen->mText.Format("Unknown text lump '%s'", text); + } + } + else if (!lookupText) + { + textscreen->mText = text; + } + else + { + textscreen->mText << '$' << text; + } + textscreen->mTextDelay = 10; + textscreen->mBackground = flat; + textscreen->mFlatfill = !finalePic; + + if (music != NULL && *music != 0) + { + textscreen->mMusic = music; + textscreen->mMusicOrder = musicorder; + } + if (cdtrack > 0) + { + textscreen->mCdTrack = cdtrack; + textscreen->mCdId = cdid; + } + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + desc->mActions.Push(textscreen); + + if (ending) + { + desc->mLink = endsequence; + FIntermissionActionWiper *wiper = new FIntermissionActionWiper; + desc->mActions.Push(wiper); + } + + F_StartIntermission(desc, true, ending? FSTATE_EndingGame : FSTATE_ChangingLevel); + } + else if (ending) + { + FIntermissionDescriptor **pdesc = IntermissionDescriptors.CheckKey(endsequence); + if (pdesc != NULL) + { + F_StartIntermission(*pdesc, false, ending? FSTATE_EndingGame : FSTATE_ChangingLevel); + } + } +} diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index c0ef72a31..3a7e0794f 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -79,6 +79,7 @@ int MenuButtonTickers[NUM_MKEYS]; bool MenuButtonOrigin[NUM_MKEYS]; int BackbuttonTime; fixed_t BackbuttonAlpha; +static bool MenuEnabled = true; #define KEY_REPEAT_DELAY (TICRATE*5/12) @@ -633,7 +634,7 @@ bool M_Responder (event_t *ev) } return DMenu::CurrentMenu->Responder(ev) || !keyup; } - else + else if (MenuEnabled) { if (ev->type == EV_KeyDown) { @@ -761,6 +762,18 @@ void M_Init (void) } +//============================================================================= +// +// +// +//============================================================================= + +void M_EnableMenu (bool on) +{ + MenuEnabled = on; +} + + //============================================================================= // // [RH] Most menus can now be accessed directly diff --git a/src/menu/menu.h b/src/menu/menu.h index f13300bc0..e8632a0b2 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -637,6 +637,7 @@ public: struct event_t; +void M_EnableMenu (bool on) ; bool M_Responder (event_t *ev); void M_Ticker (void); void M_Drawer (void); diff --git a/src/namedef.h b/src/namedef.h index ecbe97865..c9168fed3 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -528,3 +528,13 @@ xx(res_7) xx(res_8) xx(res_9) xx(AlwaysRun) + +// end sequences +xx(Inter_Strife) +xx(Inter_Strife_Good) +xx(Inter_Strife_Sad) +xx(Inter_Strife_Bad) +xx(Inter_Strife_Lose) +xx(Inter_Strife_MAP03) +xx(Inter_Strife_MAP10) +xx(Multiplayer) \ No newline at end of file diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index b5a973cbd..d71bfbf73 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -895,8 +895,7 @@ FUNC(LS_Teleport_EndGame) { if (!backSide && CheckIfExitIsGood (it, NULL)) { - G_SetForEndGame (level.nextmap); - G_ExitLevel (0, false); + G_ChangeLevel(NULL, 0, 0); return true; } return false; diff --git a/src/p_user.cpp b/src/p_user.cpp index 37cb230b5..e844d2680 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -44,7 +44,7 @@ #include "w_wad.h" #include "cmdlib.h" #include "sbar.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "c_console.h" #include "doomdef.h" #include "c_dispatch.h" @@ -1215,9 +1215,9 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor) } } } - if (!multiplayer && (level.flags2 & LEVEL2_DEATHSLIDESHOW)) + if (!multiplayer && level.info->deathsequence != NAME_None) { - F_StartSlideshow (); + F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame); } } } diff --git a/src/statistics.cpp b/src/statistics.cpp index f5778af44..276090ebd 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -453,7 +453,6 @@ void STAT_ChangeLevel(const char *newl) level_info_t *l = FindLevelInfo (newl); nextinfo = l->CheckLevelRedirect (); if (nextinfo == NULL) nextinfo = l; - } if (savestatistics == 1) @@ -602,8 +601,7 @@ CCMD(printstats) CCMD(finishgame) { // This CCMD simulates an end-of-game action and exists to end mods that never exit their last level. - G_SetForEndGame (level.nextmap); - G_ExitLevel (0, false); + G_ChangeLevel(NULL, 0, 0); } ADD_STAT(statistics) diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index f75838b7f..7ff6e2d67 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -1,4 +1,5 @@ // MAPINFO for Chex Quest +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,9 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 1 cursorpic = "chexcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Pic1" } skill baby diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt new file mode 100644 index 000000000..d7d6b3d6c --- /dev/null +++ b/wadsrc/static/mapinfo/common.txt @@ -0,0 +1,308 @@ + +Intermission Inter_Titlescreen +{ + GotoTitle + { + } +} + + +Intermission Inter_Pic1 +{ + Image + { + Background = "@1" // index into finalepic FadeIn gameinfo block + } +} + +Intermission Inter_Pic2 +{ + Image + { + Background = "@2" + } +} + +Intermission Inter_Pic3 +{ + Image + { + Background = "@3" + } +} + +Intermission Inter_Bunny +{ + Scroller + { + ScrollDirection = Left + Background = "PFUB1" + Background2 = "PFUB2" + Music = "$MUSIC_BUNNY" + InitialDelay = -230 + Scrolltime = -640 + Time = -1130 + } + Link = TheEnd +} + +Intermission TheEnd +{ + // no backgrounds are set here so this will reuse the previous one. + Image + { + Draw = "END0", 108, 68 + Time = -50 + } + Image + { + Draw = "END1", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END2", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END3", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END4", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END5", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END6", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } +} + +Intermission Inter_Underwater +{ + Image + { + Background = "E2END", 0, "E2PAL" + } + GotoTitle + { + } +} + +Intermission Inter_Demonscroll +{ + Scroller + { + ScrollDirection = Up + Background = "FINAL1" + Background2 = "FINAL2" + InitialDelay = 2 + Scrolltime = -600 + } +} + +Intermission Inter_BuyStrife +{ + Scroller + { + ScrollDirection = Left + Background = "CREDIT" + Background2 = "VELLOGO" + InitialDelay = -230 + Scrolltime = -640 + } +} + +Intermission Inter_Cast +{ + Image + { + // This is only here to initialize the background and the music + Background = "$bgcastcall" + Time = -1 + Music = "$MUSIC_EVIL" + } + Link = Doom2Cast +} + +Intermission Doom2Cast +{ + Cast + { + CastClass = "Zombieman" + CastName = "$CC_ZOMBIE" + AttackSound = "Missile", 1, "grunt/attack" + } + Cast + { + CastClass = "ShotgunGuy" + CastName = "$CC_SHOTGUN" + AttackSound = "Missile", 1, "shotguy/attack" + } + Cast + { + CastClass = "ChaingunGuy" + CastName = "$CC_HEAVY" + AttackSound = "Missile", 1, "chainguy/attack" + AttackSound = "Missile", 2, "chainguy/attack" + AttackSound = "Missile", 3, "chainguy/attack" + } + Cast + { + CastClass = "DoomImp" + CastName = "$CC_IMP" + AttackSound = "Missile", 2, "imp/attack" + } + Cast + { + CastClass = "Demon" + CastName = "$CC_DEMON" + AttackSound = "Melee", 1, "demon/melee" + } + Cast + { + CastClass = "LostSoul" + CastName = "$CC_LOST" + AttackSound = "Missile", 1, "grunt/attack" + } + Cast + { + CastClass = "Cacodemon" + CastName = "$CC_CACO" + AttackSound = "Missile", 1, "caco/attack" + } + Cast + { + CastClass = "HellKnight" + CastName = "$CC_HELL" + AttackSound = "Missile", 1, "baron/attack" + } + Cast + { + CastClass = "BaronOfHell" + CastName = "$CC_BARON" + AttackSound = "Missile", 1, "baron/attack" + } + Cast + { + CastClass = "Arachnotron" + CastName = "$CC_ARACH" + AttackSound = "Missile", 1, "baby/attack" + } + Cast + { + CastClass = "PainElemental" + CastName = "$CC_PAIN" + AttackSound = "Missile", 2, "skull/melee" + } + Cast + { + CastClass = "Revenant" + CastName = "$CC_REVEN" + AttackSound = "Missile", 1, "skeleton/attack" + AttackSound = "Melee", 1, "skeleton/swing" + AttackSound = "Melee", 3, "skeleton/melee" + } + Cast + { + CastClass = "Fatso" + CastName = "$CC_MANCU" + AttackSound = "Missile", 1, "fatso/attack" + AttackSound = "Missile", 4, "fatso/attack" + AttackSound = "Missile", 7, "fatso/attack" + } + Cast + { + CastClass = "Archvile" + CastName = "$CC_ARCH" + AttackSound = "Missile", 1, "vile/start" + } + Cast + { + CastClass = "SpiderMastermind" + CastName = "$CC_SPIDER" + AttackSound = "Missile", 1, "spider/attack" + AttackSound = "Missile", 2, "spider/attack" + } + Cast + { + CastClass = "Cyberdemon" + CastName = "$CC_CYBER" + AttackSound = "Missile", 1, "weapons/rocklf" + AttackSound = "Missile", 3, "weapons/rocklf" + AttackSound = "Missile", 5, "weapons/rocklf" + } + Cast + { + CastClass = "DoomPlayer" + CastName = "$CC_HERO" + AttackSound = "Missile", 0, "weapons/sshotf" + } + Link = Doom2Cast // restart cast call +} + +Intermission Inter_Chess +{ + Fader + { + Music = "Hall" + Background = "FINALE1" + Time = 2 + FadeType = FadeIn + } + TextScreen + { + Background = "FINALE1" + TextSpeed = 3 + TextLump = "win1msg" + Time = -250 + } + TextScreen + { + Music = "Orb" + Background = "FINALE2" + TextSpeed = 3 + TextLump = "win2msg" + Time = -250 + } + Fader + { + Background = "FINALE2" + Time = 2 + FadeType = FadeOut + } + Fader + { + Music = "Chess" + Background = "FINALE3" + DrawConditional = "Multiplayer", "CHESSALL", 20, 0 + DrawConditional = "ClericPlayer", "CHESSC", 60, 0 + DrawConditional = "MagePlayer", "CHESSM", 60, 0 + Time = 2 + FadeType = FadeIn + } + TextScreen + { + Background = "FINALE3" + DrawConditional = "Multiplayer", "CHESSALL", 20, 0 + DrawConditional = "ClericPlayer", "CHESSC", 60, 0 + DrawConditional = "MagePlayer", "CHESSM", 60, 0 + TextSpeed = 3 + TextLump = "win3msg" + Position = 5, 135 + } +} diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 393dbc2ea..0ad964ca5 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -1,3 +1,5 @@ +include "mapinfo/common.txt" + gameinfo { titlepage = "TITLEPIC" @@ -56,6 +58,9 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 1 cursorpic = "doomcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Cast" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 17cd942f2..0e269c8f7 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -1,4 +1,5 @@ // MAPINFO for Heretic (Shareware and Retail) +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,9 @@ gameinfo pausesign = "PAUSED" gibfactor = 0.5 cursorpic = "herecurs" + textscreenx = 20 + textscreeny = 5 + defaultendsequence = "Inter_Pic1" } skill baby diff --git a/wadsrc/static/mapinfo/hexdd.txt b/wadsrc/static/mapinfo/hexdd.txt new file mode 100644 index 000000000..77231d46d --- /dev/null +++ b/wadsrc/static/mapinfo/hexdd.txt @@ -0,0 +1,8 @@ +include "mapinfo/hexen.txt" + +cluster 3 +{ + hub + music = "hub" + pic = "interpic" +} diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 29dddd8d7..6022c541e 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -1,4 +1,5 @@ // A bare-bones MAPINFO for Hexen. +include "mapinfo/common.txt" // Most of the MAPINFO is still in hexen.wad. @@ -54,6 +55,9 @@ gameinfo pausesign = "PAUSED" gibfactor = 0.5 cursorpic = "hexncurs" + textscreenx = 10 + textscreeny = 5 + defaultendsequence = "Inter_Chess" } skill baby @@ -117,7 +121,7 @@ skill nightmare clearepisodes episode "&wt@01" { - name = "Hexen" + name = "Hexen - Beyond Heretic" key = "h" } diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index a1aa90fbe..aed50b223 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -1,4 +1,5 @@ // MAPINFO for Strife (full version and teaser) +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,175 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 0.5 cursorpic = "strfcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Strife" +} + +Intermission Inter_Strife_Good +{ + Image + { + Music = "D_HAPPY" + Background = "SS4F1" + Sound = "svox/rie01" + Time = 13 + } + Image + { + Background = "SS4F2" + Sound = "svox/bbx01" + Time = 11 + } + Image + { + Background = "SS4F3" + Sound = "svox/bbx02" + Time = 14 + } + Image + { + Background = "SS4F4" + Time = 28 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + + +Intermission Inter_Strife_Sad +{ + Image + { + Music = "D_SAD" + Background = "SS6F1" + Sound = "svox/ss601a" + Time = 8 + } + Image + { + Background = "SS6F2" + Sound = "svox/ss602a" + Time = 8 + } + Image + { + Background = "SS6F3" + Sound = "svox/ss603a" + Time = 9 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + +Intermission Inter_Strife_Lose +{ + Image + { + Music = "D_SAD" + Background = "SS5F1" + Sound = "svox/ss501b" + Time = 11 + } + Image + { + Background = "SS5F2" + Sound = "svox/ss502b" + Time = 10 + } + Image + { + Background = "SS5F3" + Sound = "svox/ss503b" + Time = 11 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + +/* later +Intermission Inter_Strife_Intro +{ +} +*/ + +Intermission Inter_Strife_MAP03 +{ + Image + { + Music = "D_DARK" + Background = "SS2F1" + Sound = "svox/mac10" + Time = 9 + } + Image + { + Background = "SS2F2" + Sound = "svox/mac11" + Time = 10 + } + Image + { + Background = "SS2F3" + Sound = "svox/mac12" + Time = 12 + } + Image + { + Background = "SS2F4" + Sound = "svox/mac13" + Time = 17 + } +} + +Intermission Inter_Strife_MAP10 +{ + Image + { + Music = "D_DARK" + Background = "SS3F1" + Sound = "svox/mac16" + Time = 10 + } + Image + { + Background = "SS3F2" + Sound = "svox/mac17" + Time = 12 + } + Image + { + Background = "SS3F3" + Sound = "svox/mac18" + Time = 12 + } + Image + { + Background = "SS3F4" + Sound = "svox/mac19" + Time = 11 + } } skill baby @@ -140,6 +310,7 @@ map MAP03 "AREA 3: front base" cluster = 1 noallies redirect = "Sigil", "map30" + slideshow = "Inter_Strife_MAP03" } map MAP04 "AREA 4: power station" @@ -212,6 +383,7 @@ map MAP10 "AREA 10: New Front Base" sky1 = "SKYMNT01" music = "D_MARCH" cluster = 1 + slideshow = "Inter_Strife_MAP10" } map MAP11 "AREA 11: Borderlands" @@ -364,7 +536,7 @@ map MAP29 "AREA 29: Entity's Lair" sky1 = "SKYMNT01" music = "D_INSTRY" cluster = 1 - deathslideshow + deathsequence = "Inter_Strife_Lose" } map MAP30 "AREA 30: Abandoned Front Base" diff --git a/zdoom.vcproj b/zdoom.vcproj index 05c7e012b..834715c60 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - @@ -962,7 +958,7 @@ /> - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -1884,14 +1836,6 @@ Outputs="$(IntDir)/$(InputName).obj" /> - - - @@ -2081,14 +2025,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -2099,6 +2035,14 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5382,14 +2576,6 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> - - - @@ -5399,6 +2585,14 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> + + + @@ -5697,7 +2891,7 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 62f846b0abe0b01cabf575c9d69be9a1d8db019c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 6 Oct 2010 16:01:51 +0000 Subject: [PATCH 094/815] - fixed: The Lost Soul used the wrong sound in the cast call. SVN r2914 (trunk) --- wadsrc/static/mapinfo/common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index d7d6b3d6c..8e7886987 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -177,7 +177,7 @@ Intermission Doom2Cast { CastClass = "LostSoul" CastName = "$CC_LOST" - AttackSound = "Missile", 1, "grunt/attack" + AttackSound = "Missile", 1, "skull/melee" } Cast { From ec3e4bb7c71618b78d642abd853789127485127a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 6 Oct 2010 17:57:56 +0000 Subject: [PATCH 095/815] - fixed: The Player played no sounds in the Doom2 cast. - fixed: The player's melee state is not a proper melee state so it should not be entered when used in the cast. - fixed: Sounds were played before changing states. That missed situations where the state was entered from anywhere else but the previous state. SVN r2915 (trunk) --- src/intermission/intermission.cpp | 57 +++++++++++++++++++------------ src/intermission/intermission.h | 2 ++ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 461495f63..acb9fb62c 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -452,6 +452,25 @@ int DIntermissionScreenCast::Responder (event_t *ev) return true; } +void DIntermissionScreenCast::PlayAttackSound() +{ + // sound hacks.... + if (caststate != NULL && castattacking) + { + for (unsigned i = 0; i < mCastSounds.Size(); i++) + { + if ((!!mCastSounds[i].mSequence) == (basestate != mDefaults->MissileState) && + (caststate == basestate + mCastSounds[i].mIndex)) + { + S_StopAllChannels (); + S_Sound (CHAN_WEAPON | CHAN_UI, mCastSounds[i].mSound, 1, ATTN_NONE); + return; + } + } + } + +} + int DIntermissionScreenCast::Ticker () { Super::Ticker(); @@ -465,26 +484,13 @@ int DIntermissionScreenCast::Ticker () } else { - // sound hacks.... - if (caststate != NULL && castattacking) - { - for (unsigned i = 0; i < mCastSounds.Size(); i++) - { - if ((!!mCastSounds[i].mSequence) == (basestate != mDefaults->MissileState) && - (caststate == basestate + mCastSounds[i].mIndex - 1)) - { - S_StopAllChannels (); - S_Sound (CHAN_WEAPON | CHAN_UI, mCastSounds[i].mSound, 1, ATTN_NONE); - break; - } - } - } - // just advance to next state in animation if (caststate == advplayerstate) goto stopattack; // Oh, gross hack! caststate = caststate->GetNextState(); + + PlayAttackSound(); castframes++; } @@ -492,18 +498,27 @@ int DIntermissionScreenCast::Ticker () { // go into attack frame castattacking = true; - if (castonmelee) - basestate = caststate = mDefaults->MeleeState; - else - basestate = caststate = mDefaults->MissileState; - castonmelee ^= 1; - if (caststate == NULL) + if (!mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) { if (castonmelee) basestate = caststate = mDefaults->MeleeState; else basestate = caststate = mDefaults->MissileState; + castonmelee ^= 1; + if (caststate == NULL) + { + if (castonmelee) + basestate = caststate = mDefaults->MeleeState; + else + basestate = caststate = mDefaults->MissileState; + } } + else + { + // The players use the melee state differently so it can't be used here + basestate = caststate = mDefaults->MissileState; + } + PlayAttackSound(); } if (castattacking) diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h index d777d46d3..72dd5a2e1 100644 --- a/src/intermission/intermission.h +++ b/src/intermission/intermission.h @@ -243,6 +243,8 @@ class DIntermissionScreenCast : public DIntermissionScreen int castframes; int castonmelee; + void PlayAttackSound(); + public: DIntermissionScreenCast() {} From 0595724d6859d1d94d90b04155f03a8e0937ce35 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 7 Oct 2010 21:34:21 +0000 Subject: [PATCH 096/815] - updated CMakeLists.txt for intermission changes. SVN r2921 (trunk) --- src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 298fe16a6..85f04333a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -635,7 +635,6 @@ add_executable( zdoom WIN32 doomstat.cpp dsectoreffect.cpp dthinker.cpp - f_finale.cpp f_wipe.cpp farchive.cpp files.cpp @@ -789,6 +788,8 @@ add_executable( zdoom WIN32 g_shared/sbar_mugshot.cpp g_shared/shared_hud.cpp g_shared/shared_sbar.cpp + intermission/intermission.cpp + intermission/intermission_parse.cpp menu/colorpickermenu.cpp menu/joystickmenu.cpp menu/listmenu.cpp From a84a015f0cf684fba5abef3a5a25b52a737605af Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 7 Oct 2010 22:57:09 +0000 Subject: [PATCH 097/815] - fixed: The intermission actions for custom end sequences were never appened to the intermission descriptor. SVN r2922 (trunk) --- src/intermission/intermission_parse.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index 91641d6a9..fdd2bf60f 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -618,6 +618,7 @@ FName FMapInfoParser::ParseEndGame() else if (sc.Compare("cast")) { newSeq.EndType = END_Cast; + if (newSeq.PicName.IsEmpty()) newSeq.PicName = "$bgcastcall"; } else if (sc.Compare("music")) { @@ -687,6 +688,7 @@ FName FMapInfoParser::ParseEndGame() action->mBackground = newSeq.PicName; action->mMusic = newSeq.Music; action->mMusicLooping = newSeq.MusicLooping; + desc->mActions.Push(action); FString seq; seq.Format("@EndSequence_%d_", generated++); From 5f8c2f6310af2b01b48b28f87a88f015e8d4cdde Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Fri, 8 Oct 2010 23:09:27 +0000 Subject: [PATCH 098/815] - Added: keys, accuracy, stamina, and savepercent to drawnumber. (Savepercent is also available for drawbar.) SVN r2924 (trunk) --- src/g_shared/sbarinfo_commands.cpp | 56 ++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 8c55206c3..b0b9fe9d1 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -893,8 +893,16 @@ class CommandDrawNumber : public CommandDrawString value = TOTALSECRETS; else if(sc.Compare("armorclass")) value = ARMORCLASS; + else if(sc.Compare("savepercent")) + value = SAVEPERCENT; else if(sc.Compare("airtime")) value = AIRTIME; + else if(sc.Compare("accuracy")) + value = ACCURACY; + else if(sc.Compare("stamina")) + value = STAMINA; + else if(sc.Compare("keys")) + value = KEYS; else if(sc.Compare("globalvar")) { value = GLOBALVAR; @@ -1090,6 +1098,7 @@ class CommandDrawNumber : public CommandDrawString num = level.total_secrets; break; case ARMORCLASS: + case SAVEPERCENT: { AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); if(harmor != NULL) @@ -1100,9 +1109,12 @@ class CommandDrawNumber : public CommandDrawString //Hexen counts basic armor also so we should too. if(statusBar->armor != NULL) { - num += statusBar->armor->SavePercent; + num += FixedMul(statusBar->armor->SavePercent, 100*FRACUNIT); } - num /= (5*FRACUNIT); + if(value == ARMORCLASS) + num /= (5*FRACUNIT); + else + num >>= FRACBITS; break; } case GLOBALVAR: @@ -1141,6 +1153,20 @@ class CommandDrawNumber : public CommandDrawString if(statusBar->CPlayer->mo->InvSel != NULL) num = statusBar->CPlayer->mo->InvSel->Amount; break; + case ACCURACY: + num = statusBar->CPlayer->accuracy; + break; + case STAMINA: + num = statusBar->CPlayer->stamina; + break; + case KEYS: + num = 0; + for(AInventory *item = statusBar->CPlayer->mo->Inventory;item != NULL;item = item->Inventory) + { + if(item->IsKindOf(RUNTIME_CLASS(AKey))) + num++; + } + break; default: break; } if(interpolationSpeed != 0 && (!hudChanged || level.time == 1)) @@ -1210,6 +1236,10 @@ class CommandDrawNumber : public CommandDrawString AIRTIME, SELECTEDINVENTORY, SCORE, + SAVEPERCENT, + ACCURACY, + STAMINA, + KEYS, CONSTANT }; @@ -2183,6 +2213,8 @@ class CommandDrawBar : public SBarInfoCommand type = SECRETS; else if(sc.Compare("airtime")) type = AIRTIME; + else if(sc.Compare("savepercent")) + type = SAVEPERCENT; else if(sc.Compare("poweruptime")) { type = POWERUPTIME; @@ -2363,6 +2395,23 @@ class CommandDrawBar : public SBarInfoCommand } break; } + case SAVEPERCENT: + { + AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); + if(harmor != NULL) + { + value = harmor->Slots[0] + harmor->Slots[1] + + harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; + } + //Hexen counts basic armor also so we should too. + if(statusBar->armor != NULL) + { + value += FixedMul(statusBar->armor->SavePercent, 100*FRACUNIT); + } + value >>= FRACBITS; + max = 100; + break; + } default: return; } @@ -2399,7 +2448,8 @@ class CommandDrawBar : public SBarInfoCommand SECRETS, ARMORCLASS, POWERUPTIME, - AIRTIME + AIRTIME, + SAVEPERCENT }; unsigned int border; From b2548bf02a69a1e1714bb9e7cc619072aa8a3621 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 9 Oct 2010 00:10:54 +0000 Subject: [PATCH 099/815] - Added detection code for Hacx 1.2 IWAD. SVN r2925 (trunk) --- src/d_iwad.cpp | 9 +++++++++ src/d_main.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 3453d9d5f..429a354f8 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -82,6 +82,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "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/urbanbrawl.txt", GI_MAPxx }, { "Harmony", "Harmony", MAKERGB(110,180,230), MAKERGB(69,79,126), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, + { "Hacx: Twitch n' Kill", "Hacx", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, //{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) }, }; @@ -113,6 +114,7 @@ static const char *IWADNames[] = "chex3.wad", "action2.wad", "harm1.wad", + "hacx.wad", #ifdef unix "DOOM2.WAD", // Also look for all-uppercase names "PLUTONIA.WAD", @@ -137,6 +139,7 @@ static const char *IWADNames[] = "CHEX3.WAD", "ACTION2.WAD", "HARM1.WAD", + "HACX.WAD", #endif NULL }; @@ -175,6 +178,7 @@ static EIWADType ScanIWAD (const char *iwad) "0HAWK01", "0CARA3", "0NOSE1", + "HACX-R", { 'G','A','M','E','I','N','F','O' }, "E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", @@ -208,6 +212,7 @@ static EIWADType ScanIWAD (const char *iwad) Check_Hawk, Check_Car, Check_Nose, + Check_Hacx, Check_Gameinfo, Check_e2m1 }; @@ -300,6 +305,10 @@ static EIWADType ScanIWAD (const char *iwad) { return IWAD_Harmony; } + else if (lumpsfound[Check_Hacx]) + { + return IWAD_Hacx; + } else if (lumpsfound[Check_FreeDoom]) { // Is there a 100% reliable way to tell FreeDoom and FreeDM diff --git a/src/d_main.h b/src/d_main.h index f72f7a69b..eb49840f1 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -82,6 +82,7 @@ enum EIWADType IWAD_ChexQuest3, IWAD_ActionDoom2, IWAD_Harmony, + IWAD_Hacx, IWAD_Custom, NUM_IWAD_TYPES From 394a37421a0fe5c5729097db32e751d690dd7902 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 9 Oct 2010 04:37:31 +0000 Subject: [PATCH 100/815] - NULL pointer checks in music_pseudo_mididevice.cpp to prevent crashes on startup if fmod fails to play a midi. SVN r2927 (trunk) --- src/sound/music_pseudo_mididevice.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sound/music_pseudo_mididevice.cpp b/src/sound/music_pseudo_mididevice.cpp index 129be8f4c..5dc8e8863 100644 --- a/src/sound/music_pseudo_mididevice.cpp +++ b/src/sound/music_pseudo_mididevice.cpp @@ -129,7 +129,7 @@ int PseudoMIDIDevice::Resume() { if (!Started) { - if (Stream->Play(bLooping, 1)) + if (Stream && Stream->Play(bLooping, 1)) { Started = true; return 0; @@ -149,7 +149,8 @@ void PseudoMIDIDevice::Stop() { if (Started) { - Stream->Stop(); + if (Stream) + Stream->Stop(); Started = false; } } From 45f3ef91d34222615f85f7f251b30d74016e64bd Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 9 Oct 2010 15:57:04 +0000 Subject: [PATCH 101/815] - Fixed: If a log popup is defined, it should not draw the default log message. - Fixed: The counters in draw(selected)inventory(bar) should not have been limited to 3 characters. - Added: drawshadow() to drawinventorybar. - Added: itemflash to drawselectedinventory to use Strife's cursor fade. - Added: time and logtext to drawstring. In addition a linebreaks(size) flag was added. SVN r2928 (trunk) --- src/g_shared/sbarinfo.cpp | 5 ++ src/g_shared/sbarinfo_commands.cpp | 88 +++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 8b97c7cf3..f31fb4912 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1080,6 +1080,11 @@ public: } } + bool MustDrawLog (EHudState state) + { + return script->huds[STBAR_POPUPLOG]->NumCommands() == 0; + } + void SetMugShotState (const char *state_name, bool wait_till_done, bool reset) { script->MugShot.SetState(state_name, wait_till_done, reset); diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b0b9fe9d1..3b612368d 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -520,15 +520,25 @@ class CommandDrawString : public SBarInfoCommand { public: CommandDrawString(SBarInfo *script) : SBarInfoCommand(script), - shadow(false), shadowX(2), shadowY(2), spacing(0), font(NULL), - translation(CR_UNTRANSLATED), cache(-1), strValue(CONSTANT), - valueArgument(0), alignment (ALIGN_RIGHT) + lineBreaks(false), breakWidth(320), shadow(false), shadowX(2), + shadowY(2), spacing(0), font(NULL), translation(CR_UNTRANSLATED), + cache(-1), strValue(CONSTANT), valueArgument(0), alignment(ALIGN_RIGHT) { } void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) { - statusBar->DrawString(font, str.GetChars(), x, y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), translation, spacing, shadow, shadowX, shadowY); + if(lineBreaks) + { + FBrokenLines *lines = V_BreakLines(font, breakWidth, str.GetChars()); + for(int i = 0;lines[i].Width >= 0;i++) + { + statusBar->DrawString(font, lines[i].Text, x, y+i*(font->GetHeight()+4), block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), translation, spacing, shadow, shadowX, shadowY); + } + V_FreeBrokenLines(lines); + } + else + statusBar->DrawString(font, str.GetChars(), x, y, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets(), translation, spacing, shadow, shadowX, shadowY); } void Parse(FScanner &sc, bool fullScreenOffsets) { @@ -562,6 +572,10 @@ class CommandDrawString : public SBarInfoCommand strValue = WEAPONTAG; else if(sc.Compare("inventorytag")) strValue = INVENTORYTAG; + else if(sc.Compare("time")) + strValue = TIME; + else if(sc.Compare("logtext")) + strValue = LOGTEXT; else if(sc.Compare("globalvar")) { strValue = GLOBALVAR; @@ -627,6 +641,14 @@ class CommandDrawString : public SBarInfoCommand } shadow = true; } + else if(sc.Compare("linebreaks")) + { + sc.MustGetToken('('); + sc.MustGetToken(TK_IntConst); + breakWidth = sc.Number; + sc.MustGetToken(')'); + lineBreaks = true; + } else sc.ScriptError("Unknown flag '%s'.", sc.String); if(!sc.CheckToken('|') && !sc.CheckToken(',')) break; @@ -722,6 +744,12 @@ class CommandDrawString : public SBarInfoCommand RealignString(); } break; + case TIME: + str.Format("%02d:%02d:%02d", (level.time/TICRATE)/3600, ((level.time/TICRATE)%3600)/60, (level.time/TICRATE)%60); + break; + case LOGTEXT: + str = statusBar->CPlayer->LogText; + break; default: break; } @@ -769,10 +797,14 @@ class CommandDrawString : public SBarInfoCommand INVENTORYTAG, GLOBALVAR, GLOBALARRAY, + TIME, + LOGTEXT, CONSTANT }; + bool lineBreaks; + int breakWidth; bool shadow; int shadowX; int shadowY; @@ -1338,6 +1370,7 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private CommandDrawImage(script), CommandDrawNumber(script), alternateOnEmpty(false), artiflash(false), alwaysShowCounter(false) { + length = INT_MAX; // Counter size } void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar) @@ -1353,7 +1386,15 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private translatable, false, offset); } else + { + if(itemflash && itemflashFade) + { + fixed_t flashAlpha = fixed_t(((double) block->Alpha() / (double) FRACUNIT) * ((double) itemflashFade / (double) OPAQUE) * FRACUNIT); + statusBar->DrawGraphic(statusBar->Images[statusBar->invBarOffset + imgCURSOR], imgx-4, imgy+2, block->XOffset(), block->YOffset(), flashAlpha, block->FullScreenOffsets(), + translatable, false, offset); + } CommandDrawImage::Draw(block, statusBar); + } if(alwaysShowCounter || statusBar->CPlayer->mo->InvSel->Amount != 1) CommandDrawNumber::Draw(block, statusBar); } @@ -1371,6 +1412,8 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private artiflash = true; else if(sc.Compare("alwaysshowcounter")) alwaysShowCounter = true; + else if(sc.Compare("itemflash")) + itemflash = true; else if(sc.Compare("center")) offset = CENTER; else if(sc.Compare("centerbottom")) @@ -1430,6 +1473,12 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private if(artiflashTick > 0) artiflashTick--; + if(itemflashFade > 0) + { + itemflashFade -= FRACUNIT/14; + if(itemflashFade < 0) + itemflashFade = 0; + } SetTruth(statusBar->CPlayer->mo->InvSel == NULL || (level.flags & LEVEL_NOINVENTORYBAR), block, statusBar); @@ -1437,15 +1486,18 @@ class CommandDrawSelectedInventory : public SBarInfoCommandFlowControl, private CommandDrawNumber::Tick(block, statusBar, hudChanged); } - static void Flash() { artiflashTick = 4; } + static void Flash() { artiflashTick = 4; itemflashFade = FRACUNIT*3/4; } protected: bool alternateOnEmpty; bool artiflash; bool alwaysShowCounter; + bool itemflash; - static int artiflashTick; + static int artiflashTick; + static fixed_t itemflashFade; }; int CommandDrawSelectedInventory::artiflashTick = 4; +int CommandDrawSelectedInventory::itemflashFade = FRACUNIT*3/4; void DSBarInfo::FlashItem(const PClass *itemtype) { @@ -1754,7 +1806,8 @@ class CommandDrawInventoryBar : public SBarInfoCommand CommandDrawInventoryBar(SBarInfo *script) : SBarInfoCommand(script), style(STYLE_Doom), size(7), alwaysShow(false), noArtibox(false), noArrows(false), alwaysShowCounter(false), translucent(false), - vertical(false), counters(NULL), font(NULL), translation(CR_GOLD), + vertical(false), shadow(false), shadowX(2), shadowY(2), + counters(NULL), font(NULL), translation(CR_GOLD), fontSpacing(0) { } @@ -1858,6 +1911,19 @@ class CommandDrawInventoryBar : public SBarInfoCommand { if(sc.Compare("alwaysshow")) alwaysShow = true; + else if(sc.Compare("drawshadow")) + { + if(sc.CheckToken('(')) + { + sc.MustGetToken(TK_IntConst); + shadowX = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + shadowY = sc.Number; + sc.MustGetToken(')'); + } + shadow = true; + } else if(sc.Compare("noartibox")) noArtibox = true; else if(sc.Compare("noarrows")) @@ -1926,7 +1992,10 @@ class CommandDrawInventoryBar : public SBarInfoCommand counters[i]->spacing = fontSpacing; counters[i]->whenNotZero = !alwaysShowCounter; counters[i]->drawValue = counters[i]->value = CommandDrawNumber::CONSTANT; - counters[i]->length = 3; + counters[i]->length = INT_MAX; + counters[i]->shadow = shadow; + counters[i]->shadowX = shadowX; + counters[i]->shadowY = shadowY; } } @@ -1951,6 +2020,9 @@ class CommandDrawInventoryBar : public SBarInfoCommand bool alwaysShowCounter; bool translucent; bool vertical; + bool shadow; + int shadowX; + int shadowY; SBarInfoCoordinate x; SBarInfoCoordinate y; CommandDrawNumber* *counters; From d969b141b84b0642889179473e98954527bfbf91 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 10 Oct 2010 06:21:23 +0000 Subject: [PATCH 102/815] - added option to set custom end sequences: Use 'next = endsequence, "sequencename" in MAPINFO. SVN r2929 (trunk) --- src/intermission/intermission_parse.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index fdd2bf60f..b79519863 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -771,6 +771,12 @@ FName FMapInfoParser::CheckEndSequence() { seqname = "Inter_Titlescreen"; } + else if (sc.Compare("endsequence")) + { + ParseComma(); + sc.MustGetString(); + seqname = sc.String; + } if (seqname != NULL) { From 2eac96143e1e268776e726c727a057d05315eeb5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Oct 2010 19:16:09 +0000 Subject: [PATCH 103/815] - added conditionals to LANGUAGE parser so that the special messages for Chex Quest can be handled in the definition lumps instead of the code. SVN r2930 (trunk) --- src/m_cheat.cpp | 31 ++++++------------------------- src/menu/messagebox.cpp | 27 +++++---------------------- src/stringtable.cpp | 17 ++++++++++++++++- wadsrc/static/language.enu | 20 ++++++++++---------- wadsrc/static/language.fr | 32 ++++++++++++++++---------------- wadsrc/static/language.ita | 22 ++++++++++------------ 6 files changed, 63 insertions(+), 86 deletions(-) diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 95ddf7725..e2a41ddfd 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -83,20 +83,10 @@ void cht_DoCheat (player_t *player, int cheat) // fall through to CHT_GOD case CHT_GOD: player->cheats ^= CF_GODMODE; - if (gameinfo.gametype != GAME_Chex) - { - if (player->cheats & CF_GODMODE) - msg = GStrings("STSTR_DQDON"); - else - msg = GStrings("STSTR_DQDOFF"); - } + if (player->cheats & CF_GODMODE) + msg = GStrings("STSTR_DQDON"); else - { - if (player->cheats & CF_GODMODE) - msg = GStrings("STSTR_CDQDON"); - else - msg = GStrings("STSTR_CDQDOFF"); - } + msg = GStrings("STSTR_DQDOFF"); SB_state = screen->GetPageCount (); break; @@ -180,10 +170,7 @@ void cht_DoCheat (player_t *player, int cheat) { player->mo->GiveInventoryType (type); } - if(gameinfo.gametype != GAME_Chex) - msg = GStrings("STSTR_CHOPPERS"); - else - msg = GStrings("STSTR_CCHOPPERS"); + msg = GStrings("STSTR_CHOPPERS"); } // [RH] The original cheat also set powers[pw_invulnerability] to true. // Since this is a timer and not a boolean, it effectively turned off @@ -214,10 +201,7 @@ void cht_DoCheat (player_t *player, int cheat) cht_Give (player, "ammo"); cht_Give (player, "keys"); cht_Give (player, "armor"); - if(gameinfo.gametype != GAME_Chex) - msg = GStrings("STSTR_KFAADDED"); - else - msg = GStrings("STSTR_CKFAADDED"); + msg = GStrings("STSTR_KFAADDED"); break; case CHT_IDFA: @@ -225,10 +209,7 @@ void cht_DoCheat (player_t *player, int cheat) cht_Give (player, "weapons"); cht_Give (player, "ammo"); cht_Give (player, "armor"); - if(gameinfo.gametype != GAME_Chex) - msg = GStrings("STSTR_FAADDED"); - else - msg = GStrings("STSTR_CFAADDED"); + msg = GStrings("STSTR_FAADDED"); break; case CHT_BEHOLDV: diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index bde298894..eecd997e2 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -471,17 +471,11 @@ DEndGameMenu::DEndGameMenu(bool playsound) if (netgame) { - if(gameinfo.gametype == GAME_Chex) - EndString = GStrings("CNETEND"); - else - EndString = GStrings("NETEND"); + EndString = GStrings("NETEND"); return; } - if(gameinfo.gametype == GAME_Chex) - EndString = GStrings("CENDGAME"); - else - EndString = GStrings("ENDGAME"); + EndString = GStrings("ENDGAME"); Init(NULL, EndString, 0, playsound); @@ -561,11 +555,7 @@ DQuickSaveMenu::DQuickSaveMenu(bool playsound) { FString tempstring; - if(gameinfo.gametype == GAME_Chex) - tempstring.Format(GStrings("CQSPROMPT"), quickSaveSlot->Title); - else - tempstring.Format(GStrings("QSPROMPT"), quickSaveSlot->Title); - + tempstring.Format(GStrings("QSPROMPT"), quickSaveSlot->Title); Init(NULL, tempstring, 0, playsound); } @@ -652,11 +642,7 @@ DQuickLoadMenu::DQuickLoadMenu(bool playsound) { FString tempstring; - if(gameinfo.gametype == GAME_Chex) - tempstring.Format(GStrings("CQLPROMPT"), quickSaveSlot->Title); - else - tempstring.Format(GStrings("QLPROMPT"), quickSaveSlot->Title); - + tempstring.Format(GStrings("QLPROMPT"), quickSaveSlot->Title); Init(NULL, tempstring, 0, playsound); } @@ -693,10 +679,7 @@ CCMD (quickload) if (netgame) { - if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CQLOADNET"), NULL); - else - M_StartMessage (GStrings("QLOADNET"), NULL); + M_StartMessage (GStrings("QLOADNET"), NULL); return; } diff --git a/src/stringtable.cpp b/src/stringtable.cpp index f0e5b326f..450ab45c7 100644 --- a/src/stringtable.cpp +++ b/src/stringtable.cpp @@ -43,6 +43,7 @@ #include "sc_man.h" #include "c_dispatch.h" #include "v_text.h" +#include "gi.h" // PassNum identifies which language pass this string is from. // PassNum 0 is for DeHacked. @@ -224,6 +225,18 @@ void FStringTable::LoadLanguage (int lumpnum, DWORD code, bool exactMatch, int p sc.ScriptError ("Found a string without a language specified."); } + bool savedskip = skip; + if (sc.Compare("$")) + { + sc.MustGetStringName("ifgame"); + sc.MustGetStringName("("); + sc.MustGetString(); + skip |= !sc.Compare(GameNames[gameinfo.gametype]); + sc.MustGetStringName(")"); + sc.MustGetString(); + + } + if (skip) { // We're not interested in this language, so skip the string. sc.MustGetStringName ("="); @@ -231,7 +244,9 @@ void FStringTable::LoadLanguage (int lumpnum, DWORD code, bool exactMatch, int p do { sc.MustGetString (); - } while (!sc.Compare (";")); + } + while (!sc.Compare (";")); + skip = savedskip; continue; } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index da6de9524..57b8fcce7 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1628,17 +1628,17 @@ CE3TEXT = "Wonderful Job!"; CE4TEXT = "Fantastic"; CLOADNET = "you can't do load while in a net quest!\n\npress a key."; -CQSPROMPT = "quicksave over your quest named\n\n'%s'?\n\npress y or n."; -CQLOADNET = "you can't quickload during a netquest!\n\npress a key."; -CQLPROMPT = "do you want to quickload the quest named\n\n'%s'?\n\npress y or n."; +$ifgame(chex) QSPROMPT = "quicksave over your quest named\n\n'%s'?\n\npress y or n."; +$ifgame(chex) QLOADNET = "you can't quickload during a netquest!\n\npress a key."; +$ifgame(chex) QLPROMPT = "do you want to quickload the quest named\n\n'%s'?\n\npress y or n."; CNEWGAME = "you can't start a new quest\nwhile in a network quest.\n\npress a key."; CNIGHTMARE = "Careful, this will be tough.\nDo you wish to continue?\n\npress y or n."; CSWSTRING = "this is Chex(R) Quest. look for\n\nfuture levels at www.chexquest.com.\n\npress a key."; -CNETEND = "you can't end a netquest!\n\npress a key."; -CENDGAME = "are you sure you want to end the quest?\n\npress y or n."; +$ifgame(chex) NETEND = "you can't end a netquest!\n\npress a key."; +$ifgame(chex) ENDGAME = "are you sure you want to end the quest?\n\npress y or n."; GOTCHEXARMOR = "Picked up the Chex(R) Armor."; GOTSUPERCHEXARMOR = "Picked up the Super Chex(R) Armor!"; @@ -1673,11 +1673,11 @@ GOTLARGEZORCHER = "You got the Large Zorcher!"; GOTSUPERLARGEZORCHER = "You got the Mega Zorcher!"; GOTMINIZORCHER = "Picked up a Mini Zorcher."; -STSTR_CDQDON = "Invincible Mode ON"; -STSTR_CDQDOFF = "Invincible Mode OFF"; -STSTR_CFAADDED = "Zorch Added"; -STSTR_CKFAADDED = "Super Zorch Added"; -STSTR_CCHOPPERS = "... Eat Chex(R)!"; +$ifgame(chex) STSTR_DQDON = "Invincible Mode ON"; +$ifgame(chex) STSTR_DQDOFF = "Invincible Mode OFF"; +$ifgame(chex) STSTR_FAADDED = "Zorch Added"; +$ifgame(chex) STSTR_KFAADDED = "Super Zorch Added"; +$ifgame(chex) STSTR_CHOPPERS = "... Eat Chex(R)!"; OB_COMMONUS = "%o was slimed by a flemoid."; OB_BIPEDICUS = "%o was slimed by a bipedicus."; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index 1f06dd49b..b88551902 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -1462,17 +1462,17 @@ CC_FLEMOIDUSBIPEDICUSWITHARMOR = "FLEMOIDUS BIPEDICUS AVEC ARMURE"; CC_THEFLEMBRANE = "LA FLEMBRANE"; CLOADNET = "vous ne pouvez pas charger lors d'une quete reseau!\n\npressez un bouton."; -CQSPROMPT = "sauvergarde rapide par dessus la denome quete\n\n'%s'?\n\npressez y or n."; -CQLOADNET = "vous ne pouvez pas charger rapidement lors d'une quete reseau!!\n\npressez un bouton."; -CQLPROMPT = "voulez charger rapidement la denome quete\n\n'%s'?\n\npressez y or n."; +$ifgame(chex) QSPROMPT = "sauvergarde rapide par dessus la denome quete\n\n'%s'?\n\npressez y or n."; +$ifgame(chex) QLOADNET = "vous ne pouvez pas charger rapidement lors d'une quete reseau!!\n\npressez un bouton."; +$ifgame(chex) QLPROMPT = "voulez charger rapidement la denome quete\n\n'%s'?\n\npressez y or n."; CNEWGAME = "vous ne pouvez pas commencer une nouvelle quete\nlors d'une quete reseau.\n\pressez un bouton."; CNIGHTMARE = "Atention, ca va etre dur.\nVoulez vous continuer?\n\npressez y or n."; CSWSTRING = "Ceci est la quete de Chex(R). a la recherche de\n\nfutures niveau at www.chexquest.com.\n\npressez un bouton."; -CNETEND = "vous ne pouvez pas interompre une quete internet!\n\npressez un bouton."; -CENDGAME = "etes vous sure de vouloir interompre la quete?\n\npressez y or n."; +$ifgame(chex) NETEND = "vous ne pouvez pas interompre une quete internet!\n\npressez un bouton."; +$ifgame(chex) ENDGAME = "etes vous sure de vouloir interompre la quete?\n\npressez y or n."; GOTCHEXARMOR = "Vous avez pris l'armure Chex(R)."; GOTSUPERCHEXARMOR = "Vous avez pris la super armure Chex(R)!"; @@ -1497,18 +1497,18 @@ GOTLARGEZORCHERRECHARGE = "Vous avez pris une grande recharge zorcheur."; GOTLARGEZORCHERPACK = "Vous avez pris un grand paquet zorcheur."; GOTZORCHPACK = "Vous avez pris un zorchpak!"; -GOTLAZDEVICE = "You avez pris l'ustensil LAZ! woot!"; -GOTRAPIDZORCHER = "You avez pris le Zorcheur Rapide!"; -GOTSUPERBOOTSPORK = "You avez pris le Super Bootspork!"; -GOTZORCHPROPULSOR = "You avez pris le Propulseur Zorch!"; -GOTPHASINGZORCHER = "You avez pris le Zorcheur phasant!"; -GOTSUPERLARGEZORCHER = "You avez pris le mega Zorcheur!"; +GOTLAZDEVICE = "Vous avez pris l'ustensil LAZ! woot!"; +GOTRAPIDZORCHER = "Vous avez pris le Zorcheur Rapide!"; +GOTSUPERBOOTSPORK = "Vous avez pris le Super Bootspork!"; +GOTZORCHPROPULSOR = "Vous avez pris le Propulseur Zorch!"; +GOTPHASINGZORCHER = "Vous avez pris le Zorcheur phasant!"; +GOTSUPERLARGEZORCHER = "Vous avez pris le mega Zorcheur!"; -STSTR_CDQDON = "Mode invincible ALLUME"; -STSTR_CDQDOFF = "Mode invincible ETEINT"; -STSTR_CFAADDED = "Zorch Ajoute"; -STSTR_CKFAADDED = "Super Zorch Ajoute"; -STSTR_CCHOPPERS = "... Mangez du Chex(R)!"; +$ifgame(chex) STSTR_DQDON = "Mode invincible ALLUME"; +$ifgame(chex) STSTR_DQDOFF = "Mode invincible ETEINT"; +$ifgame(chex) STSTR_FAADDED = "Zorch Ajoute"; +$ifgame(chex) STSTR_KFAADDED = "Super Zorch Ajoute"; +$ifgame(chex) STSTR_CHOPPERS = "... Mangez du Chex(R)!"; OB_COMMONUS = "%o a ete gelifie par un flemoid."; OB_BIPEDICUS = "%o a ete gelifie par un bipedicus."; diff --git a/wadsrc/static/language.ita b/wadsrc/static/language.ita index a46237991..962d55a30 100644 --- a/wadsrc/static/language.ita +++ b/wadsrc/static/language.ita @@ -1070,17 +1070,15 @@ CE3TEXT = "Meraviglioso!"; CE4TEXT = "Fantastico"; CLOADNET = "non puoi caricare una quest durante una net quest!\n\npress a key."; -CQSPROMPT = "sovrascrivere il salvataggio\n\n'%s'?\n\npremi y oppure n."; -CQLOADNET = "non puoi fare un quickload durante una netquest!\n\npremi un tasto."; -CQLPROMPT = "vuoi fare un quickload della quest\n\n'%s'?\n\npremi y oppure n."; +$ifgame(chex) QSPROMPT = "sovrascrivere il salvataggio\n\n'%s'?\n\npremi y oppure n."; +$ifgame(chex) QLOADNET = "non puoi fare un quickload durante una netquest!\n\npremi un tasto."; +$ifgame(chex) QLPROMPT = "vuoi fare un quickload della quest\n\n'%s'?\n\npremi y oppure n."; CNEWGAME = "non puoi iniziare una nuova quest\ndurante una quest in rete.\n\npremi un tasto."; CNIGHTMARE = "Attento, sara' dura.\nVuoi continuare?\n\npremi y oppure n."; -CSWSTRING = "questa e' Chex(R) Quest. cerca\n\nfuturi livelli su www.chexquest.com.\n\npremi un tasto."; - -CNETEND = "non puoi terminare una netquest!\n\npremi un tasto"; -CENDGAME = "sei sicuro di voler terminare la quest?\n\npremi y oppure n."; +$ifgame(chex) NETEND = "non puoi terminare una netquest!\n\npremi un tasto"; +$ifgame(chex) ENDGAME = "sei sicuro di voler terminare la quest?\n\npremi y oppure n."; GOTCHEXARMOR = "Raccolta un'Armatura Chex(R)."; GOTSUPERCHEXARMOR = "Raccolta la Super Armatura Chex(R)!"; @@ -1115,11 +1113,11 @@ GOTLARGEZORCHER = "Hai trovato il zorcher grande!"; GOTSUPERLARGEZORCHER = "Hai trovato il mega zorcher!"; GOTMINIZORCHER = "Raccolto un mini zorcher."; -STSTR_CDQDON = "Modalita' invincibile attivata"; -STSTR_CDQDOFF = "Modalita' invincibile disattivata"; -STSTR_CFAADDED = "Zorch aggiunto"; -STSTR_CKFAADDED = "Super Zorch aggiunto"; -STSTR_CCHOPPERS = "... Mangia Chex(R)!"; +$ifgame(chex) STSTR_DQDON = "Modalita' invincibile attivata"; +$ifgame(chex) STSTR_DQDOFF = "Modalita' invincibile disattivata"; +$ifgame(chex) STSTR_FAADDED = "Zorch aggiunto"; +$ifgame(chex) STSTR_KFAADDED = "Super Zorch aggiunto"; +$ifgame(chex) STSTR_CHOPPERS = "... Mangia Chex(R)!"; OB_COMMONUS = "%o e' stato coperto di slime da un flemoid."; OB_BIPEDICUS = "%o e' stato coperto di slime da un bipedicus."; From dd17c35d8983e1dd0497091d5054d3d8358537bc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 11 Oct 2010 22:10:15 +0000 Subject: [PATCH 104/815] - removed gamemode check for shareware message. It also uses LANGUAGE conditionals now. SVN r2931 (trunk) --- src/gametype.h | 1 - src/menu/menu.cpp | 9 +-------- wadsrc/static/language.enu | 2 +- wadsrc/static/language.fr | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/gametype.h b/src/gametype.h index 69eeae545..56c1564f6 100644 --- a/src/gametype.h +++ b/src/gametype.h @@ -10,7 +10,6 @@ enum EGameType GAME_Chex = 16, //Chex is basically Doom, but we need to have a different set of actors. GAME_Raven = GAME_Heretic|GAME_Hexen, - GAME_DoomStrife = GAME_Doom|GAME_Strife, GAME_DoomChex = GAME_Doom|GAME_Chex, GAME_DoomStrifeChex = GAME_Doom|GAME_Strife|GAME_Chex }; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 3a7e0794f..686ed12f6 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -361,14 +361,7 @@ void M_SetMenu(FName menu, int param) 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); - } + M_StartMessage(GStrings("SWSTRING"), 1); return; } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 57b8fcce7..f3fb2bcef 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1559,7 +1559,7 @@ MNU_PLAYERSETUP = "PLAYER SETUP"; MNU_DELETESG = "Do you really want to delete the savegame\n"; -MNU_ONLYREGISTERED = "ONLY AVAILABLE IN THE REGISTERED VERSION"; +$ifgame(heretic) SWSTRING = "ONLY AVAILABLE IN THE REGISTERED VERSION"; MNU_EPISODE = "Select Episode"; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index b88551902..354ba2bee 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -1369,7 +1369,7 @@ MNU_PLAYERSETUP = "REGLAGE DU JOUEUR"; MNU_DELETESG = "Voulez vous vraiment effacer cette sauvegarde?\n"; -MNU_ONLYREGISTERED = "SEULEMENT DISPONIBLE DANS LA VERSION ENREGISTREE"; +$ifgame(heretic) SWSTRING = "SEULEMENT DISPONIBLE DANS LA VERSION ENREGISTREE"; // Bloodbath announcer From 352a926ddf85d733c4352ee189c7f49b476281b8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 12 Oct 2010 07:14:31 +0000 Subject: [PATCH 105/815] - made all references to the GameNames array an inline function call to allow easier modification later - changed all parsers that check for the current game to use the same function for the game check. - fixed: The TEAMINFO parser handled 'game Any' incorrectly. SVN r2934 (trunk) --- src/d_main.cpp | 8 ++++---- src/g_shared/a_keys.cpp | 23 +++-------------------- src/gi.h | 12 ++++++++++++ src/keysections.cpp | 4 ++-- src/m_misc.cpp | 6 +++--- src/menu/menu.cpp | 2 +- src/menu/menudef.cpp | 10 +++------- src/p_terrain.cpp | 23 +---------------------- src/p_udmf.cpp | 2 +- src/s_advsound.cpp | 23 +---------------------- src/stringtable.cpp | 2 +- src/teaminfo.cpp | 25 +++++-------------------- src/teaminfo.h | 1 - 13 files changed, 37 insertions(+), 104 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 10d58fd2b..9c4c6b13b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1877,7 +1877,7 @@ void D_DoomMain (void) gameinfo.gametype = iwad_info->gametype; gameinfo.flags = iwad_info->flags; - GameConfig->DoGameSetup (GameNames[gameinfo.gametype]); + GameConfig->DoGameSetup (GameName()); if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload")) { @@ -1910,7 +1910,7 @@ void D_DoomMain (void) D_AddConfigWads (allwads, "Global.Autoload"); // Add game-specific wads - file = GameNames[gameinfo.gametype]; + file = GameName(); file += ".Autoload"; D_AddConfigWads (allwads, file); @@ -1925,7 +1925,7 @@ void D_DoomMain (void) // Run automatically executed files execFiles = new DArgs; - GameConfig->AddAutoexec (execFiles, GameNames[gameinfo.gametype]); + GameConfig->AddAutoexec (execFiles, GameName()); D_MultiExec (execFiles, true); // Run .cfg files at the start of the command line. @@ -2145,7 +2145,7 @@ void D_DoomMain (void) StartScreen->Progress (); - Printf ("R_Init: Init %s refresh subsystem.\n", GameNames[gameinfo.gametype]); + Printf ("R_Init: Init %s refresh subsystem.\n", GameName()); StartScreen->LoadingStatus ("Loading graphics", 0x3f); R_Init (); diff --git a/src/g_shared/a_keys.cpp b/src/g_shared/a_keys.cpp index aaddb9084..e7f1b286b 100644 --- a/src/g_shared/a_keys.cpp +++ b/src/g_shared/a_keys.cpp @@ -199,27 +199,11 @@ static void ParseLock(FScanner &sc) keynum = sc.Number; sc.MustGetString(); - if (sc.Compare("DOOM")) + if (!sc.Compare("{")) { - if (gameinfo.gametype != GAME_Doom) keynum=-1; + if (!CheckGame(sc.String, false)) keynum = -1; + sc.MustGetStringName("{"); } - else if (sc.Compare("HERETIC")) - { - if (gameinfo.gametype != GAME_Heretic) keynum=-1; - } - else if (sc.Compare("HEXEN")) - { - if (gameinfo.gametype != GAME_Hexen) keynum=-1; - } - else if (sc.Compare("STRIFE")) - { - if (gameinfo.gametype != GAME_Strife) keynum=-1; - } - else if (sc.Compare("CHEX")) - { - if (gameinfo.gametype != GAME_Chex) keynum=-1; - } - else sc.UnGet(); ignorekey = true; if (keynum > 0 && keynum < 255) @@ -239,7 +223,6 @@ static void ParseLock(FScanner &sc) sc.ScriptError("Lock index %d out of range", keynum); } - sc.MustGetStringName("{"); while (!sc.CheckString("}")) { sc.MustGetString(); diff --git a/src/gi.h b/src/gi.h index e71b0f783..b65926881 100644 --- a/src/gi.h +++ b/src/gi.h @@ -135,4 +135,16 @@ struct gameinfo_t extern gameinfo_t gameinfo; +inline const char *GameName() +{ + return GameNames[gameinfo.gametype]; +} + +inline bool CheckGame(const char *string, bool chexisdoom) +{ + int test = gameinfo.gametype; + if (test == GAME_Chex && chexisdoom) test = GAME_Doom; + return !stricmp(string, GameNames[test]); +} + #endif //__GI_H__ diff --git a/src/keysections.cpp b/src/keysections.cpp index fc0e668ba..5f8f12ea7 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -48,10 +48,10 @@ static void LoadKeys (const char *modname, bool dbl) { char section[64]; - if (GameNames[gameinfo.gametype] == NULL) + if (GameName() == NULL) return; - mysnprintf (section, countof(section), "%s.%s%sBindings", GameNames[gameinfo.gametype], modname, + mysnprintf (section, countof(section), "%s.%s%sBindings", GameName(), modname, dbl ? ".Double" : "."); FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; diff --git a/src/m_misc.cpp b/src/m_misc.cpp index d070fdd52..3762691b1 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -375,9 +375,9 @@ bool M_SaveDefaults (const char *filename) GameConfig->ChangePathName (filename); } GameConfig->ArchiveGlobalData (); - if (GameNames[gameinfo.gametype] != NULL) + if (GameName() != NULL) { - GameConfig->ArchiveGameData (GameNames[gameinfo.gametype]); + GameConfig->ArchiveGameData (GameName()); } success = GameConfig->WriteConfigFile (); if (filename != NULL) @@ -619,7 +619,7 @@ static bool FindFreeName (FString &fullname, const char *extension) for (i = 0; i <= 9999; i++) { - const char *gamename = GameNames[gameinfo.gametype]; + const char *gamename = GameName(); time_t now; tm *tm; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 686ed12f6..1d6184437 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -960,6 +960,6 @@ CCMD(reset2defaults) CCMD(reset2saved) { GameConfig->DoGlobalSetup (); - GameConfig->DoGameSetup (GameNames[gameinfo.gametype]); + GameConfig->DoGameSetup (GameName()); R_SetViewSize (screenblocks); } diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index d30e35ee7..a751caf5c 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -112,20 +112,16 @@ static void SkipSubBlock(FScanner &sc) static bool CheckSkipGameBlock(FScanner &sc) { - int filter = 0; + bool filter = false; 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; + filter |= CheckGame(sc.String, false); } while (sc.CheckString(",")); sc.MustGetStringName(")"); - if (!(gameinfo.gametype & filter)) + if (!filter) { SkipSubBlock(sc); return true; diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index a2750fbea..14d2bb3df 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -332,31 +332,10 @@ static void ParseOuter (FScanner &sc) break; case OUT_IFDOOM: - if (!(gameinfo.gametype & GAME_DoomChex)) - { - ifskip = true; - } - break; - case OUT_IFHERETIC: - if (gameinfo.gametype != GAME_Heretic) - { - ifskip = true; - } - break; - case OUT_IFHEXEN: - if (gameinfo.gametype != GAME_Hexen) - { - ifskip = true; - } - break; - case OUT_IFSTRIFE: - if (gameinfo.gametype != GAME_Strife) - { - ifskip = true; - } + ifskip = !CheckGame(sc.String+2, true); break; case OUT_ENDIF: diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 2dd3c770e..263022501 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1449,7 +1449,7 @@ public: floordrop = true; break; default: - Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameNames[gameinfo.gametype]); + Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameName()); switch (gameinfo.gametype) { default: // Shh, GCC diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index e6e7ce2a8..240498f0c 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1301,31 +1301,10 @@ static void S_AddSNDINFO (int lump) break; case SI_IfDoom: //also Chex - if (!(gameinfo.gametype & GAME_DoomChex)) - { - skipToEndIf = true; - } - break; - case SI_IfStrife: - if (gameinfo.gametype != GAME_Strife) - { - skipToEndIf = true; - } - break; - case SI_IfHeretic: - if (gameinfo.gametype != GAME_Heretic) - { - skipToEndIf = true; - } - break; - case SI_IfHexen: - if (gameinfo.gametype != GAME_Hexen) - { - skipToEndIf = true; - } + skipToEndIf = !CheckGame(sc.String+3, true); break; } } diff --git a/src/stringtable.cpp b/src/stringtable.cpp index 450ab45c7..1746a29d8 100644 --- a/src/stringtable.cpp +++ b/src/stringtable.cpp @@ -231,7 +231,7 @@ void FStringTable::LoadLanguage (int lumpnum, DWORD code, bool exactMatch, int p sc.MustGetStringName("ifgame"); sc.MustGetStringName("("); sc.MustGetString(); - skip |= !sc.Compare(GameNames[gameinfo.gametype]); + skip |= !sc.Compare(GameName()); sc.MustGetStringName(")"); sc.MustGetString(); diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp index 90dfb2c93..c54b2f9d6 100644 --- a/src/teaminfo.cpp +++ b/src/teaminfo.cpp @@ -113,7 +113,6 @@ enum ETeamOptions FTeam::FTeam () { - m_GameFilter = 0; m_iPlayerColor = 0; m_iPlayerCount = 0; m_iScore = 0; @@ -162,6 +161,7 @@ void FTeam::ParseTeamInfo () void FTeam::ParseTeamDefinition (FScanner &Scan) { FTeam Team; + int valid = -1; Scan.MustGetString (); Team.m_Name = Scan.String; Scan.MustGetStringName ("{"); @@ -174,23 +174,9 @@ void FTeam::ParseTeamDefinition (FScanner &Scan) { case TEAMINFO_Game: Scan.MustGetString (); - - if (!stricmp (Scan.String, "Doom")) - Team.m_GameFilter |= GAME_Doom; - else if (!stricmp (Scan.String, "Heretic")) - Team.m_GameFilter |= GAME_Heretic; - else if (!stricmp (Scan.String, "Hexen")) - Team.m_GameFilter |= GAME_Hexen; - else if (!stricmp (Scan.String, "Raven")) - Team.m_GameFilter |= GAME_Raven; - else if (!stricmp (Scan.String, "Strife")) - Team.m_GameFilter |= GAME_Strife; - else if (!stricmp (Scan.String, "Chex")) - Team.m_GameFilter |= GAME_Chex; - else if (!stricmp (Scan.String, "Any")) - Team.m_GameFilter |= GAME_Any; - else - Scan.ScriptError ("ParseTeamDefinition: Unknown game type '%s'.\n", Scan.String); + if (Scan.Compare("Any")) valid = 1; + else if (CheckGame(Scan.String, false)) valid = 1; + else if (valid == -1) valid = 0; break; case TEAMINFO_PlayerColor: @@ -236,8 +222,7 @@ void FTeam::ParseTeamDefinition (FScanner &Scan) } } - if (Team.m_GameFilter == 0 || Team.m_GameFilter & gameinfo.gametype) - Teams.Push (Team); + if (valid) Teams.Push (Team); } //========================================================================== diff --git a/src/teaminfo.h b/src/teaminfo.h index f18d63d0d..06f19c7fa 100644 --- a/src/teaminfo.h +++ b/src/teaminfo.h @@ -64,7 +64,6 @@ private: void ClearTeams (); FString m_Name; - BYTE m_GameFilter; int m_iPlayerColor; FString m_TextColor; FString m_Logo; From 7e362819e42df79b9fc9793a169b0d7a44ce7003 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 12 Oct 2010 08:43:15 +0000 Subject: [PATCH 106/815] - made the different cursor characters for Raven's and the other small fonts a property of the font instead deciding based on the game. SVN r2935 (trunk) --- src/ct_chat.cpp | 2 +- src/menu/loadsavemenu.cpp | 3 ++- src/menu/playermenu.cpp | 2 +- src/v_font.cpp | 14 ++++++++++++-- src/v_font.h | 3 +++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index ecc3c4827..d05e44a67 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -252,7 +252,7 @@ void CT_Drawer (void) } // draw the prompt, text, and cursor - ChatQueue[len] = gameinfo.gametype & GAME_DoomChex ? '_' : '['; + ChatQueue[len] = SmallFont->GetCursor(); ChatQueue[len+1] = '\0'; if (con_scaletext < 2) { diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 9b7b0e683..97bda8494 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -637,10 +637,11 @@ void DLoadSaveMenu::Drawer () listboxLeft+1, listboxTop+rowHeight*i+CleanYfac, savegamestring, DTA_CleanNoMove, true, TAG_DONE); + char curs[2] = { SmallFont->GetCursor(), 0 }; screen->DrawText (SmallFont, CR_WHITE, listboxLeft+1+SmallFont->StringWidth (savegamestring)*CleanXfac, listboxTop+rowHeight*i+CleanYfac, - (gameinfo.gametype & (GAME_DoomStrifeChex)) ? "_" : "[", + curs, DTA_CleanNoMove, true, TAG_DONE); } } diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index e41d6094d..c4ad7b86b 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -161,7 +161,7 @@ void FPlayerNameBox::Drawer(bool selected) else { size_t l = strlen(mEditName); - mEditName[l] = (gameinfo.gametype & (GAME_DoomStrifeChex)) ? '_' : '['; + mEditName[l] = SmallFont->GetCursor(); mEditName[l+1] = 0; screen->DrawText (SmallFont, CR_UNTRANSLATED, x + mFrameSize, mYpos, mEditName, diff --git a/src/v_font.cpp b/src/v_font.cpp index 3815c8ae4..9acd2d4c2 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -345,6 +345,7 @@ FFont::FFont (const char *name, const char *nametemplate, int first, int count, Name = copystring (name); Next = FirstFont; FirstFont = this; + Cursor = '_'; maxyoffs = 0; @@ -1892,6 +1893,7 @@ void V_InitCustomFonts() int start; int first; int count; + char cursor = '_'; while ((llump = Wads.FindLump ("FONTDEFS", &lastlump)) != -1) { @@ -1938,6 +1940,11 @@ void V_InitCustomFonts() count = sc.Number; format = 1; } + else if (sc.Compare ("CURSOR")) + { + sc.MustGetString(); + cursor = sc.String[0]; + } else if (sc.Compare ("NOTRANSLATION")) { if (format == 1) goto wrong; @@ -1981,7 +1988,8 @@ void V_InitCustomFonts() } if (format == 1) { - new FFont (namebuffer, templatebuf, first, count, start); + FFont *fnt = new FFont (namebuffer, templatebuf, first, count, start); + fnt->SetCursor(cursor); } else if (format == 2) { @@ -2003,7 +2011,8 @@ void V_InitCustomFonts() } if (count > 0) { - new FSpecialFont (namebuffer, first, count, &lumplist[first], notranslate); + FFont *fnt = new FSpecialFont (namebuffer, first, count, &lumplist[first], notranslate); + fnt->SetCursor(cursor); } } else goto wrong; @@ -2377,6 +2386,7 @@ void V_InitFonts() else if (Wads.CheckNumForName ("FONTA_S") >= 0) { SmallFont = new FFont ("SmallFont", "FONTA%02u", HU_FONTSTART, HU_FONTSIZE, 1); + SmallFont->SetCursor('['); } else { diff --git a/src/v_font.h b/src/v_font.h index 4cdba31af..5499c58af 100644 --- a/src/v_font.h +++ b/src/v_font.h @@ -95,6 +95,8 @@ public: inline int StringWidth (const char *str) const { return StringWidth ((const BYTE *)str); } int GetCharCode(int code, bool needpic) const; + char GetCursor() const { return Cursor; } + void SetCursor(char c) { Cursor = c; } protected: FFont (); @@ -110,6 +112,7 @@ protected: int SpaceWidth; int FontHeight; int GlobalKerning; + char Cursor; struct CharData { FTexture *Pic; From 55585a36eb42b10a46cd80cc4b50edfb40bd8e33 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 12 Oct 2010 22:52:35 +0000 Subject: [PATCH 107/815] - Added "Heretic1" autoload section for files that should be loaded with real Heretic but not with Blasphemer. SVN r2938 (trunk) --- src/d_iwad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 429a354f8..8900bc23f 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -65,8 +65,8 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE }, { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE }, - { "Heretic: Shadow of the Serpent Riders", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt", GI_MENUHACK_EXTENDED }, - { "Heretic", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, + { "Heretic: Shadow of the Serpent Riders", "Heretic1", MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt", GI_MENUHACK_EXTENDED }, + { "Heretic", "Heretic1", MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, { "DOOM Shareware", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_SHAREWARE | GI_COMPATSHORTTEX }, { "The Ultimate DOOM", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/ultdoom.txt", GI_COMPATSHORTTEX }, { "DOOM Registered", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_COMPATSHORTTEX }, From 0d10718e6759ac6a26f85a21c4084137877b1469 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 13 Oct 2010 16:29:37 +0000 Subject: [PATCH 108/815] - Added SDL joystick support. SVN r2939 (trunk) --- src/CMakeLists.txt | 1 + src/sdl/i_input.cpp | 31 ++++++++++++------------------- src/sdl/i_main.cpp | 7 ++++++- src/sdl/st_start.cpp | 3 +++ 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 85f04333a..f9291a5af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -538,6 +538,7 @@ else( WIN32 ) sdl/hardware.cpp sdl/i_cd.cpp sdl/i_input.cpp + sdl/i_joystick.cpp sdl/i_main.cpp sdl/i_movie.cpp sdl/i_system.cpp diff --git a/src/sdl/i_input.cpp b/src/sdl/i_input.cpp index 0fe02cfd2..6e862e0a3 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -17,7 +17,6 @@ #include "dikeys.h" #include "templates.h" #include "s_sound.h" -#include "m_joy.h" static void I_CheckGUICapture (); static void I_CheckNativeMouse (); @@ -437,6 +436,18 @@ void MessagePump (const SDL_Event &sev) D_PostEvent (&event); } } + break; + + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + if (!GUICapture) + { + event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button; + if(event.data1 != 0) + D_PostEvent(&event); + } + break; } } @@ -468,21 +479,3 @@ void I_StartFrame () InitKeySymMap (); } } - -void I_GetJoysticks(TArray &sticks) -{ - sticks.Clear(); -} - -void I_GetAxes(float axes[NUM_JOYAXIS]) -{ - for (int i = 0; i < NUM_JOYAXIS; ++i) - { - axes[i] = 0; - } -} - -IJoystickConfig *I_UpdateDeviceList() -{ - return NULL; -} diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 4305a07ca..dc1dbfa05 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -245,6 +245,9 @@ static void unprotect_rtext() } #endif +void I_StartupJoysticks(); +void I_ShutdownJoysticks(); + int main (int argc, char **argv) { printf(GAMENAME" v%s - SVN revision %s - SDL version\nCompiled on %s\n\n", @@ -268,7 +271,7 @@ int main (int argc, char **argv) setlocale (LC_ALL, "C"); - if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) == -1) + if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE|SDL_INIT_JOYSTICK) == -1) { fprintf (stderr, "Could not initialize SDL:\n%s\n", SDL_GetError()); return -1; @@ -316,11 +319,13 @@ int main (int argc, char **argv) progdir = "./"; } + I_StartupJoysticks(); C_InitConsole (80*8, 25*8, false); D_DoomMain (); } catch (class CDoomError &error) { + I_ShutdownJoysticks(); if (error.GetMessage ()) fprintf (stderr, "%s\n", error.GetMessage ()); exit (-1); diff --git a/src/sdl/st_start.cpp b/src/sdl/st_start.cpp index 81e50d23e..33a5abe0b 100644 --- a/src/sdl/st_start.cpp +++ b/src/sdl/st_start.cpp @@ -71,6 +71,8 @@ class FTTYStartupScreen : public FStartupScreen // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- +void I_ShutdownJoysticks(); + // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void DeleteStartupScreen(); @@ -347,5 +349,6 @@ bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) void ST_Endoom() { + I_ShutdownJoysticks(); exit(0); } From 78cfbe56a191e3b1c9508b8723a4a8eb085f0b6e Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 13 Oct 2010 16:30:31 +0000 Subject: [PATCH 109/815] - Forgot to actually add new joystick code. SVN r2940 (trunk) --- src/sdl/i_joystick.cpp | 211 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/sdl/i_joystick.cpp diff --git a/src/sdl/i_joystick.cpp b/src/sdl/i_joystick.cpp new file mode 100644 index 000000000..d7e81d70d --- /dev/null +++ b/src/sdl/i_joystick.cpp @@ -0,0 +1,211 @@ +#include + +#include "m_joy.h" + +class SDLInputJoystick: public IJoystickConfig +{ +public: + SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f) + { + Device = SDL_JoystickOpen(DeviceIndex); + if(Device != NULL) + SetDefaultConfig(); + } + ~SDLInputJoystick() + { + if(Device != NULL) + M_SaveJoystickConfig(this); + SDL_JoystickClose(Device); + } + + bool IsValid() const + { + return Device != NULL; + } + + FString GetName() + { + return SDL_JoystickName(DeviceIndex); + } + float GetSensitivity() + { + return Multiplier; + } + void SetSensitivity(float scale) + { + Multiplier = scale; + } + + int GetNumAxes() + { + return SDL_JoystickNumAxes(Device); + } + float GetAxisDeadZone(int axis) + { + return Axes[axis].DeadZone; + } + EJoyAxis GetAxisMap(int axis) + { + return Axes[axis].GameAxis; + } + const char *GetAxisName(int axis) + { + return Axes[axis].Name.GetChars(); + } + float GetAxisScale(int axis) + { + return Axes[axis].Multiplier; + } + + void SetAxisDeadZone(int axis, float zone) + { + Axes[axis].DeadZone = zone; + } + void SetAxisMap(int axis, EJoyAxis gameaxis) + { + Axes[axis].GameAxis = gameaxis; + } + void SetAxisScale(int axis, float scale) + { + Axes[axis].Multiplier = scale; + } + + // Used by the saver to not save properties that are at their defaults. + bool IsSensitivityDefault() + { + return Multiplier == 1.0f; + } + bool IsAxisDeadZoneDefault(int axis) + { + return Axes[axis].DeadZone == 0.0f; + } + bool IsAxisMapDefault(int axis) + { + if(axis >= 5) + return Axes[axis].GameAxis == JOYAXIS_None; + return Axes[axis].GameAxis == DefaultAxes[axis]; + } + bool IsAxisScaleDefault(int axis) + { + return Axes[axis].Multiplier == 1.0f; + } + + void SetDefaultConfig() + { + for(int i = 0;i < GetNumAxes();i++) + { + AxisInfo info; + info.Name.Format("Axis %d", i); + info.DeadZone = 0.0f; + info.Multiplier = 1.0f; + if(i >= 5) + info.GameAxis = JOYAXIS_None; + else + info.GameAxis = DefaultAxes[i]; + Axes.Push(info); + } + } + FString GetIdentifier() + { + char id[16]; + mysnprintf(id, countof(id), "JS:%d", DeviceIndex); + return id; + } + + void AddAxes(float axes[NUM_JOYAXIS]) + { + // Add to game axes. + for (int i = 0; i < GetNumAxes(); ++i) + { + axes[Axes[i].GameAxis] -= float(((double)SDL_JoystickGetAxis(Device, i)/32768.0) * Multiplier * Axes[i].Multiplier); + } + } + +protected: + struct AxisInfo + { + FString Name; + float DeadZone; + float Multiplier; + EJoyAxis GameAxis; + }; + static const EJoyAxis DefaultAxes[5]; + + int DeviceIndex; + SDL_Joystick *Device; + + float Multiplier; + TArray Axes; +}; +const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Pitch, JOYAXIS_Yaw, JOYAXIS_Up}; + +class SDLInputJoystickManager +{ +public: + SDLInputJoystickManager() + { + for(int i = 0;i < SDL_NumJoysticks();i++) + { + SDLInputJoystick *device = new SDLInputJoystick(i); + if(device->IsValid()) + Joysticks.Push(device); + else + delete device; + } + } + ~SDLInputJoystickManager() + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + delete Joysticks[i]; + } + + void AddAxes(float axes[NUM_JOYAXIS]) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + Joysticks[i]->AddAxes(axes); + } + void GetDevices(TArray &sticks) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + { + M_LoadJoystickConfig(Joysticks[i]); + sticks.Push(Joysticks[i]); + } + } +protected: + TArray Joysticks; +}; +static SDLInputJoystickManager *JoystickManager; + +void I_StartupJoysticks() +{ + JoystickManager = new SDLInputJoystickManager(); +} +void I_ShutdownJoysticks() +{ + delete JoystickManager; +} + +void I_GetJoysticks(TArray &sticks) +{ + sticks.Clear(); + + JoystickManager->GetDevices(sticks); +} + +void I_GetAxes(float axes[NUM_JOYAXIS]) +{ + for (int i = 0; i < NUM_JOYAXIS; ++i) + { + axes[i] = 0; + } + if (use_joystick) + { + JoystickManager->AddAxes(axes); + } +} + +IJoystickConfig *I_UpdateDeviceList() +{ + return NULL; +} From eb064ebe06290825ebbab15f62f1395fa3d3058a Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 13 Oct 2010 20:07:16 +0000 Subject: [PATCH 110/815] - Fixed crash with joysticks with more than 5 axes. SVN r2941 (trunk) --- src/sdl/i_joystick.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sdl/i_joystick.cpp b/src/sdl/i_joystick.cpp index d7e81d70d..79aa9e91f 100644 --- a/src/sdl/i_joystick.cpp +++ b/src/sdl/i_joystick.cpp @@ -117,7 +117,8 @@ public: // Add to game axes. for (int i = 0; i < GetNumAxes(); ++i) { - axes[Axes[i].GameAxis] -= float(((double)SDL_JoystickGetAxis(Device, i)/32768.0) * Multiplier * Axes[i].Multiplier); + if(Axes[i].GameAxis != JOYAXIS_None) + axes[Axes[i].GameAxis] -= float(((double)SDL_JoystickGetAxis(Device, i)/32768.0) * Multiplier * Axes[i].Multiplier); } } From 7068c1df481ae62e248247a4446b2ff6a4119615 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 14 Oct 2010 14:40:26 +0000 Subject: [PATCH 111/815] - added a ResetEvent call in case that Timidity++ did not react to signalling the KillerEvent. SVN r2942 (trunk) --- src/sound/music_midi_timidity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index c0efbf74e..dcaa934b9 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -650,6 +650,7 @@ void TimidityPPMIDIDevice::Stop () SetEvent(KillerEvent); if (WaitForSingleObject(ChildProcess, 500) != WAIT_OBJECT_0) { + ResetEvent(KillerEvent); TerminateProcess(ChildProcess, 666); } CloseHandle(ChildProcess); From eded2ef345ed3d3c07c915b5ed176756bdc5daa1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 15 Oct 2010 15:13:53 +0000 Subject: [PATCH 112/815] - Each IWAD can now define its own config section. Hacx, Harmony and Action Doom2 now do that. - moved IWAD identification data into a lump in zdoom.pk3. - rewrote IWAD checking code SVN r2943 (trunk) --- src/d_iwad.cpp | 682 ++++++++++++++++--------------------- src/d_main.cpp | 16 +- src/d_main.h | 79 +++-- src/gameconfigfile.cpp | 17 +- src/gi.h | 3 +- src/keysections.cpp | 5 +- src/m_misc.cpp | 6 +- src/menu/menu.cpp | 2 +- src/p_udmf.cpp | 2 +- src/sdl/i_system.cpp | 6 +- src/sdl/i_system.h | 3 +- src/stringtable.cpp | 2 +- src/win32/i_main.cpp | 2 +- src/win32/i_system.cpp | 2 +- src/win32/i_system.h | 3 +- wadsrc/static/iwadinfo.txt | 319 +++++++++++++++++ 16 files changed, 683 insertions(+), 466 deletions(-) create mode 100644 wadsrc/static/iwadinfo.txt diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 8900bc23f..93ea10837 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -43,6 +43,8 @@ #include "m_argv.h" #include "m_misc.h" #include "c_cvars.h" +#include "sc_man.h" +#include "v_video.h" #include "gameconfigfile.h" #include "resourcefiles/resourcefile.h" @@ -50,99 +52,224 @@ CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -EIWADType gameiwad; +//========================================================================== +// +// Clear check list +// +//========================================================================== -// If autoname is NULL, that's either because that game doesn't allow -// loading of external wads or because it's already caught by the -// general game-specific wads section. -const IWADInfo IWADInfos[NUM_IWAD_TYPES] = +void FIWadManager::ClearChecks() { - // banner text, autoname, fg color, bg color - { "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx | GI_COMPATSHORTTEX | GI_COMPATSTAIRS }, - { "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx | GI_COMPATSHORTTEX }, - { "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 }, - { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexdd.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 }, - { "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE }, - { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX }, - { "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE }, - { "Heretic: Shadow of the Serpent Riders", "Heretic1", MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt", GI_MENUHACK_EXTENDED }, - { "Heretic", "Heretic1", MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt" }, - { "DOOM Shareware", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_SHAREWARE | GI_COMPATSHORTTEX }, - { "The Ultimate DOOM", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/ultdoom.txt", GI_COMPATSHORTTEX }, - { "DOOM Registered", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_COMPATSHORTTEX }, - { "Strife: Quest for the Sigil", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx }, - { "Strife: Teaser (Old Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE }, - { "Strife: Teaser (New Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE | GI_TEASER2 }, - { "Freedoom", "Freedoom", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, - { "Ultimate Freedoom", "Freedoom1",MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom1.txt" }, - { "Freedoom \"Demo\"", NULL, MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom1.txt" }, - { "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", GI_NOTEXTCOLOR }, - { "Action Doom 2: Urban Brawl", "UrbanBrawl",MAKERGB(168,168,0), MAKERGB(168,0,0), GAME_Doom, "mapinfo/urbanbrawl.txt", GI_MAPxx }, - { "Harmony", "Harmony", MAKERGB(110,180,230), MAKERGB(69,79,126), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, - { "Hacx: Twitch n' Kill", "Hacx", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx }, - //{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) }, -}; + mLumpsFound.Resize(mIWads.Size()); + for(unsigned i=0;igametype = GAME_Doom; + else if (sc.Compare("Heretic")) iwad->gametype = GAME_Heretic; + else if (sc.Compare("Hexen")) iwad->gametype = GAME_Hexen; + else if (sc.Compare("Strife")) iwad->gametype = GAME_Strife; + else if (sc.Compare("Chex")) iwad->gametype = GAME_Chex; + else sc.ScriptError(NULL); + } + else if (sc.Compare("Mapinfo")) + { + sc.MustGetStringName("="); + sc.MustGetString(); + iwad->MapInfo = sc.String; + } + else if (sc.Compare("Compatibility")) + { + sc.MustGetStringName("="); + do + { + sc.MustGetString(); + if(sc.Compare("NoTextcolor")) iwad->flags |= GI_NOTEXTCOLOR; + else if(sc.Compare("Poly1")) iwad->flags |= GI_COMPATPOLY1; + else if(sc.Compare("Poly2")) iwad->flags |= GI_COMPATPOLY2; + else if(sc.Compare("Shareware")) iwad->flags |= GI_SHAREWARE; + else if(sc.Compare("Teaser2")) iwad->flags |= GI_TEASER2; + else if(sc.Compare("Extended")) iwad->flags |= GI_MENUHACK_EXTENDED; + else if(sc.Compare("Shorttex")) iwad->flags |= GI_COMPATSHORTTEX; + else if(sc.Compare("Stairs")) iwad->flags |= GI_COMPATSTAIRS; + else sc.ScriptError(NULL); + } + while (sc.CheckString(",")); + } + else if (sc.Compare("MustContain")) + { + sc.MustGetStringName("="); + do + { + sc.MustGetString(); + iwad->Lumps.Push(FString(sc.String)); + } + while (sc.CheckString(",")); + } + else if (sc.Compare("BannerColors")) + { + sc.MustGetStringName("="); + sc.MustGetString(); + iwad->FgColor = V_GetColor(NULL, sc.String); + sc.MustGetStringName(","); + sc.MustGetString(); + iwad->BkColor = V_GetColor(NULL, sc.String); + } + else if (sc.Compare("Load")) + { + sc.MustGetStringName("="); + do + { + sc.MustGetString(); + iwad->Load.Push(FString(sc.String)); + } + while (sc.CheckString(",")); + } + else if (sc.Compare("Required")) + { + sc.MustGetStringName("="); + sc.MustGetString(); + iwad->Required = sc.String; + } + else + { + sc.ScriptError("Unknown keyword '%s'", sc.String); + } + } + } + else if (sc.Compare("NAMES")) + { + sc.MustGetStringName("{"); + mIWadNames.Push(FString()); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + FString wadname = sc.String; +#ifdef _WIN32 + mIWadNames.Push(wadname); +#else + // check for lowercase, uppercased first letter and full uppercase on Linux etc. + wadname.ToLower(); + mIWadNames.Push(wadname); + wadname[0] = toupper(wadname[0]); + mIWadNames.Push(wadname); + wadname.ToUpper(); + mIWadNames.Push(wadname); #endif - NULL -}; + } + } + } +} + +//========================================================================== +// +// Lool for IWAD definition lump +// +//========================================================================== + +void FIWadManager::ParseIWadInfos(const char *fn) +{ + FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true); + if (resfile != NULL) + { + DWORD cnt = resfile->LumpCount(); + for(int i=cnt-1; i>=0; i--) + { + FResourceLump *lmp = resfile->GetLump(i); + + if (lmp->Namespace == ns_global && !stricmp(lmp->Name, "IWADINFO")) + { + // Found one! + ParseIWadInfo(resfile->Filename, (const char*)lmp->CacheLump(), lmp->LumpSize); + break; + } + } + delete resfile; + } +} + //========================================================================== // @@ -151,279 +278,30 @@ static const char *IWADNames[] = // Scan the contents of an IWAD to determine which one it is //========================================================================== -static EIWADType ScanIWAD (const char *iwad) +int FIWadManager::ScanIWAD (const char *iwad) { - static const char checklumps[][8] = - { - "AD2LIB", - "E1M1", - "E4M2", - "MAP01", - "MAP40", - "MAP60", - "TITLE", - "REDTNT2", - "CAMO1", - { 'E','X','T','E','N','D','E','D'}, - "ENDSTRF", - "MAP33", - "INVCURS", - { 'F','R','E','E','D','O','O','M' }, - { 'B','L','A','S','P','H','E','M' }, - "W94_1", - { 'P','O','S','S','H','0','M','0' }, - "CYCLA1", - "FLMBA1", - "MAPINFO", - "0HAWK01", - "0CARA3", - "0NOSE1", - "HACX-R", - { 'G','A','M','E','I','N','F','O' }, - "E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", - "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", - "DPHOOF","BFGGA0","HEADA1","CYBRA1", - { 'S','P','I','D','A','1','D','1' }, - - }; -#define NUM_CHECKLUMPS (countof(checklumps)) - enum - { - Check_ad2lib, - Check_e1m1, - Check_e4m1, - Check_map01, - Check_map40, - Check_map60, - Check_title, - Check_redtnt2, - Check_cam01, - Check_Extended, - Check_endstrf, - Check_map33, - Check_invcurs, - Check_FreeDoom, - Check_Blasphem, - Check_W94_1, - Check_POSSH0M0, - Check_Cycla1, - Check_Flmba1, - Check_Mapinfo, - Check_Hawk, - Check_Car, - Check_Nose, - Check_Hacx, - Check_Gameinfo, - Check_e2m1 - }; - bool lumpsfound[NUM_CHECKLUMPS]; - size_t i; - - memset (lumpsfound, 0, sizeof(lumpsfound)); FResourceFile *iwadfile = FResourceFile::OpenResourceFile(iwad, NULL, true); if (iwadfile != NULL) { + ClearChecks(); for(DWORD ii = 0; ii < iwadfile->LumpCount(); ii++) { FResourceLump *lump = iwadfile->GetLump(ii); - size_t j; - for (j = 0; j < NUM_CHECKLUMPS; j++) + CheckLumpName(lump->Name); + if (lump->FullName != NULL) { - if (!lumpsfound[j]) + if (strnicmp(lump->FullName, "maps/", 5) == 0) { - if (strnicmp (lump->Name, checklumps[j], 8) == 0) - { - lumpsfound[j] = true; - break; - } - // Check for maps inside zips, too. - else if (lump->FullName != NULL) - { - if (checklumps[j][0] == 'E' && checklumps[j][2] == 'M' && checklumps[j][4] == '\0') - { - if (strnicmp(lump->FullName, "maps/", 5) == 0 && - strnicmp(lump->FullName + 5, checklumps[j], 4) == 0 && - stricmp(lump->FullName + 9, ".wad") == 0) - { - lumpsfound[j] = true; - break; - } - } - else if (checklumps[j][0] == 'M' && checklumps[j][1] == 'A' && checklumps[j][2] == 'P' && - checklumps[j][5] == '\0') - { - if (strnicmp(lump->FullName, "maps/", 5) == 0 && - strnicmp(lump->FullName + 5, checklumps[j], 5) == 0 && - stricmp(lump->FullName + 10, ".wad") == 0) - { - lumpsfound[j] = true; - break; - } - } - } + FString mapname(lump->FullName+5, strcspn(lump->FullName+5, ".")); + CheckLumpName(mapname); } } } delete iwadfile; } - - // Always check for custom iwads first. -#if 0 - if (lumpsfound[Check_Gameinfo]) - { - return IWAD_Custom; - } -#endif - if (lumpsfound[Check_title] && lumpsfound[Check_map60]) - { - return IWAD_HexenDK; - } - else if (lumpsfound[Check_map33] && lumpsfound[Check_endstrf]) - { - if (lumpsfound[Check_map01]) - { - return IWAD_Strife; - } - else if (lumpsfound[Check_invcurs]) - { - return IWAD_StrifeTeaser2; // Strife0.wad from 14 Mar 1996 - } - else - { - return IWAD_StrifeTeaser; // Strife0.wad from 22 Feb 1996 - } - } - else if (lumpsfound[Check_map01]) - { - if (lumpsfound[Check_ad2lib]) - { - return IWAD_ActionDoom2; - } - else if (lumpsfound[Check_Hawk] && lumpsfound[Check_Car] && lumpsfound[Check_Nose]) - { - return IWAD_Harmony; - } - else if (lumpsfound[Check_Hacx]) - { - return IWAD_Hacx; - } - else if (lumpsfound[Check_FreeDoom]) - { - // Is there a 100% reliable way to tell FreeDoom and FreeDM - // apart based solely on the lump names? - if (strstr(iwad, "freedm.wad") || strstr(iwad, "FREEDM.WAD")) - { - return IWAD_FreeDM; - } - else - { - return IWAD_FreeDoom; - } - } - else if (lumpsfound[Check_redtnt2]) - { - return IWAD_Doom2TNT; - } - else if (lumpsfound[Check_cam01]) - { - return IWAD_Doom2Plutonia; - } - else - { - if (lumpsfound[Check_title]) - { - if (lumpsfound[Check_map40]) - { - return IWAD_Hexen; - } - else - { - return IWAD_HexenDemo; - } - } - else - { - return IWAD_Doom2; - } - } - } - else if (lumpsfound[Check_e1m1]) - { - if (lumpsfound[Check_title]) - { - if (!lumpsfound[Check_e2m1]) - { - return IWAD_HereticShareware; - } - else - { - if (lumpsfound[Check_Blasphem]) - { - return IWAD_Blasphemer; - } - else if (lumpsfound[Check_Extended]) - { - return IWAD_HereticExtended; - } - else - { - return IWAD_Heretic; - } - } - } - else if (lumpsfound[Check_Cycla1] && lumpsfound[Check_Flmba1]) - { - if (!lumpsfound[Check_Mapinfo]) - { - // The original release won't work without its hacked custom EXE. - //I_FatalError("Found an incompatible version of Chex Quest 3"); - return NUM_IWAD_TYPES; // Can't use it. - } - return IWAD_ChexQuest3; - } - else - { - if (lumpsfound[Check_FreeDoom]) - { - if (!lumpsfound[Check_e2m1]) - { - return IWAD_FreeDoom1; - } - else - { - return IWAD_FreeDoomU; - } - } - for (i = Check_e2m1; i < NUM_CHECKLUMPS; i++) - { - if (!lumpsfound[i]) - { - return IWAD_DoomShareware; - } - } - if (i == NUM_CHECKLUMPS) - { - if (lumpsfound[Check_e4m1]) - { - if (lumpsfound[Check_W94_1] && lumpsfound[Check_POSSH0M0]) - { - return IWAD_ChexQuest; - } - else - { - return IWAD_UltimateDoom; - } - } - else - { - return IWAD_DoomRegistered; - } - } - } - } - return NUM_IWAD_TYPES; // Don't know + return GetIWadInfo(); } //========================================================================== @@ -437,10 +315,9 @@ static EIWADType ScanIWAD (const char *iwad) // //========================================================================== -static int CheckIWAD (const char *doomwaddir, WadStuff *wads) +int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads) { const char *slash; - int i; int numfound; numfound = 0; @@ -448,20 +325,21 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads) slash = (doomwaddir[0] && doomwaddir[strlen (doomwaddir)-1] != '/') ? "/" : ""; // Search for a pre-defined IWAD - for (i = IWADNames[0] ? 0 : 1; IWADNames[i]; i++) + for (unsigned i=0; i< mIWadNames.Size(); i++) { - if (wads[i].Path.IsEmpty()) + if (mIWadNames[i].IsNotEmpty() && wads[i].Path.IsEmpty()) { FString iwad; - iwad.Format ("%s%s%s", doomwaddir, slash, IWADNames[i]); + iwad.Format ("%s%s%s", doomwaddir, slash, mIWadNames[i]); FixPathSeperator (iwad); if (FileExists (iwad)) { wads[i].Type = ScanIWAD (iwad); - if (wads[i].Type != NUM_IWAD_TYPES) + if (wads[i].Type != -1) { wads[i].Path = iwad; + wads[i].Name = mIWads[wads[i].Type].Name; numfound++; } } @@ -492,10 +370,10 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads) // //========================================================================== -static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, const char *zdoom_wad) +int FIWadManager::IdentifyVersion (TArray &wadfiles, const char *iwad, const char *zdoom_wad) { - WadStuff wads[countof(IWADNames)]; - size_t foundwads[NUM_IWAD_TYPES] = { 0 }; + TArray wads; + TArray foundwads; const char *iwadparm = Args->CheckValue ("-iwad"); size_t numwads; int pickwad; @@ -503,6 +381,11 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c bool iwadparmfound = false; FString custwad; + ParseIWadInfos(zdoom_wad); + wads.Resize(mIWadNames.Size()); + foundwads.Resize(mIWads.Size()); + memset(&foundwads[0], 0, foundwads.Size() * sizeof(foundwads[0])); + if (iwadparm == NULL && iwad != NULL && *iwad != 0) { iwadparm = iwad; @@ -512,7 +395,7 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c { custwad = iwadparm; FixPathSeperator (custwad); - if (CheckIWAD (custwad, wads)) + if (CheckIWAD (custwad, &wads[0])) { // -iwad parameter was a directory iwadparm = NULL; } @@ -520,8 +403,8 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c { DefaultExtension (custwad, ".wad"); iwadparm = custwad; - IWADNames[0] = iwadparm; - CheckIWAD ("", wads); + mIWadNames[0] = custwad; + CheckIWAD ("", &wads[0]); } } @@ -538,7 +421,7 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c { FString nice = NicePath(value); FixPathSeperator(nice); - CheckIWAD(nice, wads); + CheckIWAD(nice, &wads[0]); } } } @@ -558,7 +441,7 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c steam_path += "/SteamApps/common/"; for (i = 0; i < countof(steam_dirs); ++i) { - CheckIWAD (steam_path + steam_dirs[i], wads); + CheckIWAD (steam_path + steam_dirs[i], &wads[0]); } } #endif @@ -569,7 +452,7 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c iwadparmfound = true; } - for (i = numwads = 0; i < countof(IWADNames); i++) + for (i = numwads = 0; i < mIWadNames.Size(); i++) { if (!wads[i].Path.IsEmpty()) { @@ -582,20 +465,42 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c } } - if (foundwads[IWAD_HexenDK] && !foundwads[IWAD_Hexen]) - { // Cannot play Hexen DK without Hexen - size_t kill = foundwads[IWAD_HexenDK]; - for (i = kill; i < numwads; ++i) + for (unsigned i=0; i kill) + bool found = false; + // needs to be loaded with another IWAD (HexenDK) + for (unsigned j=0; j kill) + { + foundwads[j]--; + } + } + } } } @@ -629,7 +534,7 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c } } } - pickwad = I_PickIWad (wads, (int)numwads, queryiwad, defiwad); + pickwad = I_PickIWad (&wads[0], (int)numwads, queryiwad, defiwad); if (pickwad >= 0) { // The newly selected IWAD becomes the new default @@ -644,15 +549,14 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c // zdoom.pk3 must always be the first file loaded and the IWAD second. D_AddFile (wadfiles, zdoom_wad); - if (wads[pickwad].Type == IWAD_HexenDK) - { // load hexen.wad before loading hexdd.wad - D_AddFile (wadfiles, wads[foundwads[IWAD_Hexen]-1].Path); + if (mIWads[wads[pickwad].Type].preload >= 0) + { + D_AddFile (wadfiles, wads[foundwads[mIWads[wads[pickwad].Type].preload]-1].Path); } - D_AddFile (wadfiles, wads[pickwad].Path); - if (wads[pickwad].Type == IWAD_Strife) - { // Try to load voices.wad along with strife1.wad + for (unsigned i=0; i < mIWads[wads[pickwad].Type].Load.Size(); i++) + { long lastslash = wads[pickwad].Path.LastIndexOf ('/'); FString path; @@ -664,25 +568,31 @@ static EIWADType IdentifyVersion (TArray &wadfiles, const char *iwad, c { path = FString (wads[pickwad].Path.GetChars(), lastslash + 1); } - path += "voices.wad"; + path += mIWads[wads[pickwad].Type].Load[i]; D_AddFile (wadfiles, path); - } + } return wads[pickwad].Type; } -const IWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad) +//========================================================================== +// +// Find an IWAD to use for this game +// +//========================================================================== + +const FIWADInfo *FIWadManager::FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad) { - EIWADType iwadType = IdentifyVersion(wadfiles, iwad, basewad); - gameiwad = iwadType; - const IWADInfo *iwad_info = &IWADInfos[iwadType]; + int iwadType = IdentifyVersion(wadfiles, iwad, basewad); + //gameiwad = iwadType; + const FIWADInfo *iwad_info = &mIWads[iwadType]; if (DoomStartupInfo.Name.IsEmpty()) DoomStartupInfo.Name = iwad_info->Name; if (DoomStartupInfo.BkColor == 0 && DoomStartupInfo.FgColor == 0) { DoomStartupInfo.BkColor = iwad_info->BkColor; DoomStartupInfo.FgColor = iwad_info->FgColor; } - I_SetIWADInfo(iwad_info); + I_SetIWADInfo(); return iwad_info; } \ No newline at end of file diff --git a/src/d_main.cpp b/src/d_main.cpp index 9c4c6b13b..6138c1607 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -122,7 +122,7 @@ extern void M_SetDefaultMode (); extern void R_ExecuteSetViewSize (); extern void G_NewInit (); extern void SetupPlayerClasses (); -const IWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad); +const FIWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -1873,11 +1873,13 @@ void D_DoomMain (void) GetCmdLineFiles(pwads); FString iwad = CheckGameInfo(pwads); - const IWADInfo *iwad_info = D_FindIWAD(allwads, iwad, basewad); + FIWadManager *iwad_man = new FIWadManager; + const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad); gameinfo.gametype = iwad_info->gametype; gameinfo.flags = iwad_info->flags; + gameinfo.ConfigName = iwad_info->Configname; - GameConfig->DoGameSetup (GameName()); + GameConfig->DoGameSetup (gameinfo.ConfigName); if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload")) { @@ -1910,7 +1912,7 @@ void D_DoomMain (void) D_AddConfigWads (allwads, "Global.Autoload"); // Add game-specific wads - file = GameName(); + file = gameinfo.ConfigName; file += ".Autoload"; D_AddConfigWads (allwads, file); @@ -1925,7 +1927,7 @@ void D_DoomMain (void) // Run automatically executed files execFiles = new DArgs; - GameConfig->AddAutoexec (execFiles, GameName()); + GameConfig->AddAutoexec (execFiles, gameinfo.ConfigName); D_MultiExec (execFiles, true); // Run .cfg files at the start of the command line. @@ -2145,7 +2147,7 @@ void D_DoomMain (void) StartScreen->Progress (); - Printf ("R_Init: Init %s refresh subsystem.\n", GameName()); + Printf ("R_Init: Init %s refresh subsystem.\n", gameinfo.ConfigName.GetChars()); StartScreen->LoadingStatus ("Loading graphics", 0x3f); R_Init (); @@ -2223,6 +2225,8 @@ void D_DoomMain (void) // about to begin the game. FBaseCVar::EnableNoSet (); + delete iwad_man; // now we won't need this anymore + // [RH] Run any saved commands from the command line or autoexec.cfg now. gamestate = GS_FULLCONSOLE; Net_NewMakeTic (); diff --git a/src/d_main.h b/src/d_main.h index eb49840f1..415a6b943 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -56,55 +56,31 @@ bool D_AddFile (TArray &wadfiles, const char *file, bool check = true, extern const char *D_DrawIcon; -enum EIWADType -{ - IWAD_Doom2TNT, - IWAD_Doom2Plutonia, - IWAD_Hexen, - IWAD_HexenDK, - IWAD_HexenDemo, - IWAD_Doom2, - IWAD_HereticShareware, - IWAD_HereticExtended, - IWAD_Heretic, - IWAD_DoomShareware, - IWAD_UltimateDoom, - IWAD_DoomRegistered, - IWAD_Strife, - IWAD_StrifeTeaser, - IWAD_StrifeTeaser2, - IWAD_FreeDoom, - IWAD_FreeDoomU, - IWAD_FreeDoom1, - IWAD_FreeDM, - IWAD_Blasphemer, - IWAD_ChexQuest, - IWAD_ChexQuest3, - IWAD_ActionDoom2, - IWAD_Harmony, - IWAD_Hacx, - IWAD_Custom, - - NUM_IWAD_TYPES -}; - struct WadStuff { - WadStuff() : Type(IWAD_Doom2TNT) {} + WadStuff() : Type(0) {} FString Path; - EIWADType Type; + FString Name; + int Type; }; -struct IWADInfo +struct FIWADInfo { - const char *Name; // Title banner text for this IWAD - const char *Autoname; // Name of autoload ini section for this IWAD + FString Name; // Title banner text for this IWAD + FString Autoname; // Name of autoload ini section for this IWAD + FString Configname; // Name of config section for this IWAD + FString Required; // Requires another IWAD DWORD FgColor; // Foreground color for title banner DWORD BkColor; // Background color for title banner EGameType gametype; // which game are we playing? - const char *MapInfo; // Base mapinfo to load + FString MapInfo; // Base mapinfo to load + TArray Load; // Wads to be loaded with this one. + TArray Lumps; // Lump names for identification int flags; + int preload; + + FIWADInfo() { flags = 0; preload = -1; FgColor = 0; BkColor= 0xc0c0c0; gametype = GAME_Doom; } }; struct FStartupInfo @@ -116,7 +92,30 @@ struct FStartupInfo extern FStartupInfo DoomStartupInfo; -extern const IWADInfo IWADInfos[NUM_IWAD_TYPES]; -extern EIWADType gameiwad; +//========================================================================== +// +// IWAD identifier class +// +//========================================================================== + +struct FIWadManager +{ +private: + TArray mIWads; + TArray mIWadNames; + TArray mLumpsFound; + + void ParseIWadInfo(const char *fn, const char *data, int datasize); + void ParseIWadInfos(const char *fn); + void ClearChecks(); + void CheckLumpName(const char *name); + int GetIWadInfo(); + int ScanIWAD (const char *iwad); + int CheckIWAD (const char *doomwaddir, WadStuff *wads); + int IdentifyVersion (TArray &wadfiles, const char *iwad, const char *zdoom_wad); +public: + const FIWADInfo *FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad); +}; + #endif diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 64ddaa4b8..53b30e02a 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -62,6 +62,7 @@ extern HWND Window; #include "a_pickups.h" #include "doomstat.h" #include "i_system.h" +#include "gi.h" EXTERN_CVAR (Bool, con_centernotify) EXTERN_CVAR (Int, msg0color) @@ -347,18 +348,6 @@ void FGameConfigFile::DoGameSetup (const char *gamename) { const char *key; const char *value; - enum { Doom, Heretic, Hexen, Strife, Chex } game; - - if (strcmp (gamename, "Heretic") == 0) - game = Heretic; - else if (strcmp (gamename, "Hexen") == 0) - game = Hexen; - else if (strcmp (gamename, "Strife") == 0) - game = Strife; - else if (strcmp (gamename, "Chex") == 0) - game = Chex; - else - game = Doom; if (bMigrating) { @@ -380,9 +369,9 @@ void FGameConfigFile::DoGameSetup (const char *gamename) ReadCVars (0); } - if (game == Heretic || game == Hexen) + if (gameinfo.gametype & GAME_Raven) { - SetRavenDefaults (game == Hexen); + SetRavenDefaults (gameinfo.gametype == GAME_Hexen); } // The NetServerInfo section will be read when it's determined that diff --git a/src/gi.h b/src/gi.h index b65926881..7559a2c4b 100644 --- a/src/gi.h +++ b/src/gi.h @@ -70,6 +70,7 @@ struct gameinfo_t { int flags; EGameType gametype; + FString ConfigName; char titlePage[9]; bool drawreadthis; @@ -135,7 +136,7 @@ struct gameinfo_t extern gameinfo_t gameinfo; -inline const char *GameName() +inline const char *GameTypeName() { return GameNames[gameinfo.gametype]; } diff --git a/src/keysections.cpp b/src/keysections.cpp index 5f8f12ea7..60afce21b 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -48,10 +48,7 @@ static void LoadKeys (const char *modname, bool dbl) { char section[64]; - if (GameName() == NULL) - return; - - mysnprintf (section, countof(section), "%s.%s%sBindings", GameName(), modname, + mysnprintf (section, countof(section), "%s.%s%sBindings", gameinfo.ConfigName, modname, dbl ? ".Double" : "."); FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 3762691b1..924f45e7c 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -375,9 +375,9 @@ bool M_SaveDefaults (const char *filename) GameConfig->ChangePathName (filename); } GameConfig->ArchiveGlobalData (); - if (GameName() != NULL) + if (gameinfo.ConfigName.IsNotEmpty()) { - GameConfig->ArchiveGameData (GameName()); + GameConfig->ArchiveGameData (gameinfo.ConfigName); } success = GameConfig->WriteConfigFile (); if (filename != NULL) @@ -619,7 +619,7 @@ static bool FindFreeName (FString &fullname, const char *extension) for (i = 0; i <= 9999; i++) { - const char *gamename = GameName(); + const char *gamename = gameinfo.ConfigName; time_t now; tm *tm; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 1d6184437..b4e64b6a9 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -960,6 +960,6 @@ CCMD(reset2defaults) CCMD(reset2saved) { GameConfig->DoGlobalSetup (); - GameConfig->DoGameSetup (GameName()); + GameConfig->DoGameSetup (gameinfo.ConfigName); R_SetViewSize (screenblocks); } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 263022501..8ae9403b2 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1449,7 +1449,7 @@ public: floordrop = true; break; default: - Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameName()); + Printf("Unknown namespace %s. Using defaults for %s\n", sc.String, GameTypeName()); switch (gameinfo.gametype) { default: // Shh, GCC diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index ff15b1e88..1a681fd65 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -414,7 +414,7 @@ void STACK_ARGS I_Error (const char *error, ...) throw CRecoverableError (errortext); } -void I_SetIWADInfo (const IWADInfo *info) +void I_SetIWADInfo () { } @@ -511,7 +511,7 @@ int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad) gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, filepart, - 1, IWADInfos[wads[i].Type].Name, + 1, wads[i].Name, 2, i, -1); if (i == defaultiwad) @@ -625,7 +625,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) filepart = wads[i].Path; else filepart++; - printf ("%d. %s (%s)\n", i+1, IWADInfos[wads[i].Type].Name, filepart); + printf ("%d. %s (%s)\n", i+1, wads[i].Name, filepart); } printf ("Which one? "); scanf ("%d", &i); diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index acdae0167..a3341f4c5 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -115,8 +115,7 @@ void popterm (); void I_PrintStr (const char *str); // Set the title string of the startup window -struct IWADInfo; -void I_SetIWADInfo (const IWADInfo *info); +void I_SetIWADInfo (); // Pick from multiple IWADs to use int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); diff --git a/src/stringtable.cpp b/src/stringtable.cpp index 1746a29d8..5453cead8 100644 --- a/src/stringtable.cpp +++ b/src/stringtable.cpp @@ -231,7 +231,7 @@ void FStringTable::LoadLanguage (int lumpnum, DWORD code, bool exactMatch, int p sc.MustGetStringName("ifgame"); sc.MustGetStringName("("); sc.MustGetString(); - skip |= !sc.Compare(GameName()); + skip |= !sc.Compare(GameTypeName()); sc.MustGetStringName(")"); sc.MustGetString(); diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 782f9fa5f..87c50da99 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -406,7 +406,7 @@ void LayoutMainWindow (HWND hWnd, HWND pane) // //========================================================================== -void I_SetIWADInfo(const IWADInfo *info) +void I_SetIWADInfo() { // Make the startup banner show itself LayoutMainWindow(Window, NULL); diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 095ef1ee5..b585be71a 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1107,7 +1107,7 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa filepart = WadList[i].Path; else filepart++; - work.Format("%s (%s)", IWADInfos[WadList[i].Type].Name, filepart); + work.Format("%s (%s)", WadList[i].Name, filepart); SendMessage(ctrl, LB_ADDSTRING, 0, (LPARAM)work.GetChars()); SendMessage(ctrl, LB_SETITEMDATA, i, (LPARAM)i); } diff --git a/src/win32/i_system.h b/src/win32/i_system.h index d7af1b4b1..6bafdc01c 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -125,8 +125,7 @@ void I_PaintConsole (void); void I_PrintStr (const char *cp); // Set the title string of the startup window -struct IWADInfo; -void I_SetIWADInfo (const IWADInfo *title); +void I_SetIWADInfo (); // Pick from multiple IWADs to use int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt new file mode 100644 index 000000000..0ee148b17 --- /dev/null +++ b/wadsrc/static/iwadinfo.txt @@ -0,0 +1,319 @@ +// Must be sorted in identification order (easiest to recognize first!) + +IWad +{ + Name = "Harmony" + Game = "Doom" + Config = "Harmony" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "MAP01", "0HAWK01", "0CARA3", "0NOSE1" + BannerColors = "6e b4 d6", "45 4f 7e" +} + +IWad +{ + Name = "Hacx: Twitch'n Kill" + Game = "Doom" + Config = "Hacx" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "MAP01", "HACX-R" + BannerColors = "00 00 a8", "a8 a8 a8" +} + +IWad +{ + Name = "Action Doom 2: Urban Brawl" + Game = "Doom" + Config = "UrbanBrawl" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "MAP01", "AD2LIB" + BannerColors = "a8 a8 00", "a8 00 00" +} + +IWad +{ + Name = "Chex(R) Quest 3" + Autoname = "Chex3" + Game = "Chex" + Config = "Chex" + Mapinfo = "mapinfo/chex.txt" + Compatibility = "NoTextcolor" + MustContain = "E1M1", "CYCLA1", "FLMBA1", "MAPINFO" + BannerColors = "ff ff 00", "00 c0 00" +} + +IWad +{ + Name = "Chex(R) Quest" + Autoname = "Chex1" + Game = "Chex" + Config = "Chex" + Mapinfo = "mapinfo/chex.txt" + MustContain = "E1M1", "E4M1", "W94_1", "POSSH0M0" + BannerColors = "ff ff 00", "00 c0 00" +} + +IWad +{ + Name = "Strife: Quest for the Sigil" + Game = "Strife" + Config = "Strife" + Mapinfo = "mapinfo/strife.txt" + MustContain = "MAP01", "MAP33", "ENDSTRF" + BannerColors = "d0 ad 99", "00 6b 65" + Load = "voices.wad" +} + +IWad +{ + Name = "Strife: Teaser (New Version)" + Game = "Strife" + Config = "Strife" + Mapinfo = "mapinfo/strife.txt" + Compatibility = "Shareware", "Teaser2" + MustContain = "MAP33", "ENDSTRF", "INVCURS" + BannerColors = "d0 ad 99", "00 6b 65" +} + +IWad +{ + Name = "Strife: Teaser (Old Version)" + Game = "Strife" + Config = "Strife" + Mapinfo = "mapinfo/strife.txt" + Compatibility = "Shareware" + MustContain = "MAP33", "ENDSTRF" + BannerColors = "d0 ad 99", "00 6b 65" +} + +IWad +{ + Name = "Hexen: Beyond Heretic" + Game = "Hexen" + Config = "Hexen" + Mapinfo = "mapinfo/doom2.txt" + Compatibility = "Poly1" + MustContain = "TITLE", "MAP01", "MAP40", "WINNOWR" + BannerColors = "f0 f0 f0", "6b 3c 18" +} + +IWad +{ + Name = "Hexen: Deathkings of the Dark Citadel" + Autoname = "HexenDK" + Game = "Hexen" + Config = "Hexen" + Mapinfo = "mapinfo/hexen.txt" + Compatibility = "Poly1", "Poly2" + MustContain = "TITLE", "MAP60", "CLUS1MSG" + BannerColors = "f0 f0 f0", "6b 3c 18" + Required = "Hexen: Beyond Heretic" +} + +IWad +{ + Name = "Hexen: Demo Version" + Game = "Hexen" + Config = "Hexen" + Mapinfo = "mapinfo/doom2.txt" + Compatibility = "Shareware" + MustContain = "TITLE", "MAP01", "WINNOWR" + BannerColors = "f0 f0 f0", "6b 3c 18" +} + +IWad +{ + Name = "Blasphemer" + Autoname = "Blasphemer" + Game = "Heretic" + Config = "Heretic" + Mapinfo = "mapinfo/heretic.txt" + MustContain = "E1M1", "E2M1", "TITLE", "BLASPHEM" + BannerColors = "73 00 00", "00 00 00" +} + +IWad +{ + Name = "Heretic: Shadow of the Serpent Riders" + Autoname = "Heretic1" + Game = "Heretic" + Config = "Heretic" + Mapinfo = "mapinfo/heretic.txt" + Compatibility = "Extended" + MustContain = "E1M1", "E2M1", "TITLE", "MUS_E1M1", "EXTENDED" + BannerColors = "fc fc 00", "a8 00 00" +} + +IWad +{ + Name = "Heretic" + Autoname = "Heretic1" + Game = "Heretic" + Config = "Heretic" + Mapinfo = "mapinfo/heretic.txt" + MustContain = "E1M1", "E2M1", "TITLE", "MUS_E1M1" + BannerColors = "fc fc 00", "a8 00 00" +} + +IWad +{ + Name = "Heretic Shareware" + Game = "Heretic" + Config = "Heretic" + Mapinfo = "mapinfo/hereticsw.txt" + Compatibility = "Shareware" + MustContain = "E1M1", "TITLE", "MUS_E1M1" + BannerColors = "fc fc 00", "a8 00 00" +} + +IWad +{ + Name = "FreeDM" + Autoname = "FreeDM" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "MAP01", "FREEDM" + BannerColors = "32 54 43", "c6 dc d1" +} + +IWad +{ + Name = "Freedoom" + Autoname = "Freedoom" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "MAP01", "FREEDOOM" + BannerColors = "32 54 43", "c6 dc d1" +} + +IWad +{ + Name = "Ultimate Freedoom" + Autoname = "Freedoom1" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom1.txt" + MustContain = "E1M1", "E2M1", "E3M1", "FREEDOOM" + BannerColors = "32 54 43", "c6 dc d1" +} + +IWad +{ + Name = "Freedoom 'Demo'" + Autoname = "Freedoom1" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom2.txt" + MustContain = "E1M1", "FREEDOOM" + BannerColors = "32 54 43", "c6 dc d1" +} + +IWad +{ + Name = "The Ultimate DOOM" + Autoname = "Doom1" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom1.txt" + Compatibility = "Shorttex" + MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", + "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", + "DPHOOF","BFGGA0","HEADA1","CYBRA1","SPIDA1D1", "E4M2" + BannerColors = "54 54 54", "a8 a8 a8" +} + +IWad +{ + Name = "DOOM Registered" + Autoname = "Doom1" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom1.txt" + Compatibility = "Shorttex" + MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", + "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", + "DPHOOF","BFGGA0","HEADA1","CYBRA1","SPIDA1D1" + BannerColors = "54 54 54", "a8 a8 a8" +} + +IWad +{ + Name = "DOOM Shareware" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom1.txt" + Compatibility = "Shareware", "Shorttex" + MustContain = "E1M1" + BannerColors = "54 54 54", "a8 a8 a8" +} + +IWad +{ + Name = "Final Doom: TNT - Evilution" + Autoname = "TNT" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/tnt.txt" + Compatibility = "Shorttex", "Stairs" + MustContain = "MAP01", "REDTNT2" + BannerColors = "a8 00 00", "a8 a8 a8" +} + +IWad +{ + Name = "Final Doom: Plutonia Experiment" + Autoname = "Plutonia" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/plutonia.txt" + Compatibility = "Shorttex" + MustContain = "MAP01", "CAMO1" + BannerColors = "a8 00 00", "a8 a8 a8" +} + +// Doom 2 must be last to be checked becaude MAP01 is its only requirement +IWad +{ + Name = "DOOM 2: Hell on Earth" + Autoname = "Doom2" + Game = "Doom" + Config = "Doom" + Mapinfo = "mapinfo/doom2.txt" + Compatibility = "Shorttex" + MustContain = "MAP01" + BannerColors = "a8 00 00", "a8 a8 a8" +} + + + +Names +{ + "doom2f.wad" + "doom2.wad" + "plutonia.wad" + "tnt.wad" + "doomu.wad" + "doom.wad" + "doom1.wad" + "heretic.wad" + "heretic1.wad" + "hexen.wad" + "hexdd.wad" + "hexendemo.wad" + "hexdemo.wad" + "strife1.wad" + "strife0.wad" + "freedoom.wad" + "freedoom1.wad" + "freedoomu.wad" + "freedm.wad" + "blasphem.wad" + "blasphemer.wad" + "chex.wad" + "chex3.wad" + "action2.wad" + "harm1.wad" + "hacx.wad" +} From 0bde8591ee0ae82bc3e02793cc3caa5b1e74d911 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 15 Oct 2010 15:40:16 +0000 Subject: [PATCH 113/815] - forgot to handle GI_MAPxx flag. SVN r2944 (trunk) --- src/d_main.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 6138c1607..75103e955 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1801,6 +1801,21 @@ static FString CheckGameInfo(TArray & pwads) return ""; } +//========================================================================== +// +// Checks the IWAD for MAP01 and if found sets GI_MAPxx +// +//========================================================================== + +static void SetMapxxFlag() +{ + int lump_name = Wads.CheckNumForName("MAP01", FWadCollection::IWAD_FILENUM); + int lump_wad = Wads.CheckNumForFullName("maps/map01.wad", FWadCollection::IWAD_FILENUM); + int lump_map = Wads.CheckNumForFullName("maps/map01.map", FWadCollection::IWAD_FILENUM); + + if (lump_name >= 0 || lump_wad >= 0 || lump_map >= 0) gameinfo.flags |= GI_MAPxx; +} + //========================================================================== // // D_DoomMain @@ -1946,7 +1961,8 @@ void D_DoomMain (void) Wads.InitMultipleFiles (allwads); allwads.Clear(); allwads.ShrinkToFit(); - + SetMapxxFlag(); + // [RH] Initialize localizable strings. GStrings.LoadStrings (false); From 8806ec294ffb16a0ebe782eed20838d9cacc058f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 16 Oct 2010 06:38:42 +0000 Subject: [PATCH 114/815] - fixwd some GCC problems with d_iwad.cpp. SVN r2946 (trunk) --- src/d_iwad.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 93ea10837..d03507bf0 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -233,7 +233,8 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) // check for lowercase, uppercased first letter and full uppercase on Linux etc. wadname.ToLower(); mIWadNames.Push(wadname); - wadname[0] = toupper(wadname[0]); + wadname.LockBuffer()[0] = toupper(wadname[0]); + wadname.UnlockBuffer(); mIWadNames.Push(wadname); wadname.ToUpper(); mIWadNames.Push(wadname); @@ -331,7 +332,7 @@ int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads) { FString iwad; - iwad.Format ("%s%s%s", doomwaddir, slash, mIWadNames[i]); + iwad.Format ("%s%s%s", doomwaddir, slash, mIWadNames[i].GetChars()); FixPathSeperator (iwad); if (FileExists (iwad)) { From 650da243641ff59b9d6912302978a679e60bcf98 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 16 Oct 2010 16:21:19 +0000 Subject: [PATCH 115/815] - fixed: The alternative HUD's number printing function did not take texture scaling into account when calculating the printing position. SVN r2947 (trunk) --- src/g_shared/shared_hud.cpp | 13 ++++++++----- src/textures/textures.h | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 31dc116a6..f82e866f6 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -124,16 +124,16 @@ void SetHUDIcon(PClass *cls, FTextureID tex) //--------------------------------------------------------------------------- static void DrawImageToBox(FTexture * tex, int x, int y, int w, int h, int trans=0xc000) { - float scale1, scale2; + double scale1, scale2; if (tex) { int texwidth=tex->GetWidth(); int texheight=tex->GetHeight(); - if (wGetChar(text[i], &width); if (texc != NULL) { - int offset = texc->TopOffset - tex_zero->TopOffset + tex_zero->GetHeight(); + double offset = texc->GetScaledTopOffsetDouble() + - tex_zero->GetScaledTopOffsetDouble() + + tex_zero->GetScaledHeightDouble(); + screen->DrawChar(font, color, x, y, text[i], DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, - DTA_LeftOffset, width/2, DTA_TopOffset, offset, + DTA_LeftOffset, width/2, DTA_TopOffsetF, offset, /*DTA_CenterBottomOffset, 1,*/ TAG_DONE); } x += zerowidth; diff --git a/src/textures/textures.h b/src/textures/textures.h index 2045a19fb..a756a7036 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -182,13 +182,13 @@ public: int GetScaledWidth () { int foo = (Width << 17) / xScale; return (foo >> 1) + (foo & 1); } int GetScaledHeight () { int foo = (Height << 17) / yScale; return (foo >> 1) + (foo & 1); } - double GetScaledWidthDouble () { return (Width * 65536.f) / xScale; } - double GetScaledHeightDouble () { return (Height * 65536.f) / yScale; } + double GetScaledWidthDouble () { return (Width * 65536.) / xScale; } + double GetScaledHeightDouble () { return (Height * 65536.) / yScale; } int GetScaledLeftOffset () { int foo = (LeftOffset << 17) / xScale; return (foo >> 1) + (foo & 1); } int GetScaledTopOffset () { int foo = (TopOffset << 17) / yScale; return (foo >> 1) + (foo & 1); } - double GetScaledLeftOffsetDouble() { return (LeftOffset * 65536.f) / xScale; } - double GetScaledTopOffsetDouble() { return (TopOffset * 65536.f) / yScale; } + double GetScaledLeftOffsetDouble() { return (LeftOffset * 65536.) / xScale; } + double GetScaledTopOffsetDouble() { return (TopOffset * 65536.) / yScale; } virtual void SetFrontSkyLayer(); From 9e2e9262a462c5ea82a1a0334dde24ec1376654c Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Sat, 16 Oct 2010 17:04:18 +0000 Subject: [PATCH 116/815] - Fixed: Crash in Linux due to passing FStrings as character arrays. - Fixed: More places where SBarInfo used the unscaled information from graphics. SVN r2948 (trunk) --- src/g_shared/sbarinfo.cpp | 4 ++-- src/g_shared/sbarinfo_commands.cpp | 2 +- src/keysections.cpp | 2 +- src/sdl/i_system.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index f31fb4912..ee29a3b5d 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1153,8 +1153,8 @@ public: if((offsetflags & SBarInfoCommand::CENTER) == SBarInfoCommand::CENTER) { - dx -= (texture->GetScaledWidthDouble()/2.0)-texture->LeftOffset; - dy -= (texture->GetScaledHeightDouble()/2.0)-texture->TopOffset; + dx -= (texture->GetScaledWidthDouble()/2.0)-texture->GetScaledLeftOffsetDouble(); + dy -= (texture->GetScaledHeightDouble()/2.0)-texture->GetScaledTopOffsetDouble(); } dx += xOffset; diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 3b612368d..fcc5a6e1e 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -2730,7 +2730,7 @@ class CommandDrawGem : public SBarInfoCommand SBarInfoCoordinate drawY = y; if(wiggle && drawValue != goalValue) // Should only wiggle when the value doesn't equal what is being drawn. drawY += chainWiggle; - int chainWidth = chainImg->GetWidth(); + int chainWidth = chainImg->GetScaledWidth(); int offset = (int) (((double) (chainWidth-leftPadding-rightPadding)/100)*drawValue); statusBar->DrawGraphic(chainImg, x+(offset%chainSize), drawY, block->XOffset(), block->YOffset(), block->Alpha(), block->FullScreenOffsets()); if(gemImg != NULL) diff --git a/src/keysections.cpp b/src/keysections.cpp index 60afce21b..6ef0538f6 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -48,7 +48,7 @@ static void LoadKeys (const char *modname, bool dbl) { char section[64]; - mysnprintf (section, countof(section), "%s.%s%sBindings", gameinfo.ConfigName, modname, + mysnprintf (section, countof(section), "%s.%s%sBindings", gameinfo.ConfigName.GetChars(), modname, dbl ? ".Double" : "."); FKeyBindings *bindings = dbl? &DoubleBindings : &Bindings; diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 1a681fd65..6fc29665c 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -511,7 +511,7 @@ int I_PickIWad_Gtk (WadStuff *wads, int numwads, bool showwin, int defaultiwad) gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, filepart, - 1, wads[i].Name, + 1, wads[i].Name.GetChars(), 2, i, -1); if (i == defaultiwad) @@ -625,7 +625,7 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) filepart = wads[i].Path; else filepart++; - printf ("%d. %s (%s)\n", i+1, wads[i].Name, filepart); + printf ("%d. %s (%s)\n", i+1, wads[i].Name.GetChars(), filepart); } printf ("Which one? "); scanf ("%d", &i); From ce7921c9d99baa0888bc35f5c99aa15d8aa6f489 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 16 Oct 2010 22:37:30 +0000 Subject: [PATCH 117/815] - externalized the vector graphics for the automap arrows and the key. SVN r2949 (trunk) --- src/am_map.cpp | 138 +++++++++++---------------- src/gi.cpp | 11 +++ src/gi.h | 1 + wadsrc/static/maparrows/arrow.txt | 7 ++ wadsrc/static/maparrows/dagger.txt | 10 ++ wadsrc/static/maparrows/ddtarrow.txt | 16 ++++ wadsrc/static/maparrows/key.txt | 12 +++ wadsrc/static/mapinfo/chex.txt | 1 + wadsrc/static/mapinfo/doomcommon.txt | 1 + wadsrc/static/mapinfo/heretic.txt | 1 + wadsrc/static/mapinfo/hexen.txt | 1 + wadsrc/static/mapinfo/strife.txt | 1 + 12 files changed, 119 insertions(+), 81 deletions(-) create mode 100644 wadsrc/static/maparrows/arrow.txt create mode 100644 wadsrc/static/maparrows/dagger.txt create mode 100644 wadsrc/static/maparrows/ddtarrow.txt create mode 100644 wadsrc/static/maparrows/key.txt diff --git a/src/am_map.cpp b/src/am_map.cpp index 45cb3f980..ce2c55979 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -287,53 +287,9 @@ struct islope_t // A line drawing of the player pointing right, // starting from the middle. // -#define R ((8*PLAYERRADIUS)/7) -mline_t player_arrow[] = { - { { -R+R/8, 0 }, { R, 0 } }, // ----- - { { R, 0 }, { R-R/2, R/4 } }, // -----> - { { R, 0 }, { R-R/2, -R/4 } }, - { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> - { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, - { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> - { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } -}; -#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) - -mline_t player_arrow_raven[] = { - { { -R+R/4, 0 }, { 0, 0} }, // center line. - { { -R+R/4, R/8 }, { R, 0} }, // blade - { { -R+R/4, -R/8 }, { R, 0 } }, - { { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece - { { -R+R/8, -R/4 }, { -R+R/8, R/4 } }, - { { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors - { { -R+R/8, R/4 }, { -R+R/4, R/4} }, - { { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel - { { -R-R/4, R/8 }, { -R+R/8, R/8 } }, - { { -R-R/4, -R/8}, { -R+R/8, -R/8 } } -}; -#define NUMPLYRLINES_RAVEN (sizeof(player_arrow_raven)/sizeof(mline_t)) - -mline_t cheat_player_arrow[] = { - { { -R+R/8, 0 }, { R, 0 } }, // ----- - { { R, 0 }, { R-R/2, R/6 } }, // -----> - { { R, 0 }, { R-R/2, -R/6 } }, - { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> - { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, - { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> - { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, - { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> - { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, - { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, - { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> - { { -R/6, -R/6 }, { 0, -R/6 } }, - { { 0, -R/6 }, { 0, R/4 } }, - { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> - { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, - { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } -}; -#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) - -#undef R +TArray MapArrow; +TArray CheatMapArrow; +TArray CheatKey; #define R (MAPUNIT) // [RH] Avoid lots of warnings without compiler-specific #pragmas @@ -361,26 +317,6 @@ mline_t square_guy[] = { #define NUMSQUAREGUYLINES (sizeof(square_guy)/sizeof(mline_t)) #undef R -#define R (MAPUNIT) - -mline_t key_guy[] = { - L (-2, 0, -1.7, -0.5), - L (-1.7, -0.5, -1.5, -0.7), - L (-1.5, -0.7, -0.8, -0.5), - L (-0.8, -0.5, -0.6, 0), - L (-0.6, 0, -0.8, 0.5), - L (-1.5, 0.7, -0.8, 0.5), - L (-1.7, 0.5, -1.5, 0.7), - L (-2, 0, -1.7, 0.5), - L (-0.6, 0, 2, 0), - L (1.7, 0, 1.7, -1), - L (1.5, 0, 1.5, -1), - L (1.3, 0, 1.3, -1) -}; -#define NUMKEYGUYLINES (sizeof(key_guy)/sizeof(mline_t)) - -#undef L -#undef R @@ -545,6 +481,51 @@ void AM_getIslope (mline_t *ml, islope_t *is) } */ + +void AM_ParseArrow(TArray &Arrow, const char *lumpname) +{ + const int R = ((8*PLAYERRADIUS)/7); + FScanner sc; + int lump = Wads.CheckNumForFullName(lumpname, true); + if (lump >= 0) + { + sc.OpenLumpNum(lump); + sc.SetCMode(true); + while (sc.GetToken()) + { + mline_t line; + sc.TokenMustBe('('); + sc.MustGetFloat(); + line.a.x = xs_RoundToInt(sc.Float*R); + sc.MustGetToken(','); + sc.MustGetFloat(); + line.a.y = xs_RoundToInt(sc.Float*R); + sc.MustGetToken(')'); + sc.MustGetToken(','); + sc.MustGetToken('('); + sc.MustGetFloat(); + line.b.x = xs_RoundToInt(sc.Float*R); + sc.MustGetToken(','); + sc.MustGetFloat(); + line.b.y = xs_RoundToInt(sc.Float*R); + sc.MustGetToken(')'); + Arrow.Push(line); + } + } +} + +void AM_InitArrows() +{ + + MapArrow.Clear(); + CheatMapArrow.Clear(); + + if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); + if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); + AM_ParseArrow(CheatKey, "maparrows/key.txt"); + if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); +} + //============================================================================= // // called by the coordinate drawer @@ -1066,6 +1047,8 @@ bool AM_clearMarks () void AM_LevelInit () { + if (MapArrow.Size() == 0) AM_InitArrows(); + leveljuststarted = 0; AM_clearMarks(); @@ -2047,20 +2030,15 @@ void AM_drawPlayers () angle = players[consoleplayer].camera->angle; } - if (gameinfo.gametype & GAME_Raven) + if (am_cheat != 0 && CheatMapArrow.Size() > 0) { - arrow = player_arrow_raven; - numarrowlines = NUMPLYRLINES_RAVEN; - } - else if (am_cheat != 0) - { - arrow = cheat_player_arrow; - numarrowlines = NUMCHEATPLYRLINES; + arrow = &CheatMapArrow[0]; + numarrowlines = CheatMapArrow.Size(); } else { - arrow = player_arrow; - numarrowlines = NUMPLYRLINES; + arrow = &MapArrow[0]; + numarrowlines = MapArrow.Size(); } AM_drawLineCharacter(arrow, numarrowlines, 0, angle, YourColor, pt.x, pt.y); return; @@ -2113,9 +2091,7 @@ void AM_drawPlayers () angle -= players[consoleplayer].camera->angle - ANG90; } - AM_drawLineCharacter - (player_arrow, NUMPLYRLINES, 0, angle, - color, pt.x, pt.y); + AM_drawLineCharacter(&MapArrow[0], MapArrow.Size(), 0, angle, color, pt.x, pt.y); } } } @@ -2171,7 +2147,7 @@ void AM_drawThings () if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c)); else color = ThingColor_CountItem; - AM_drawLineCharacter(key_guy, NUMKEYGUYLINES, 16< +(1, 0), (0.5, -0.25) +(-0.875, 0), (-1.125, -0.25) // >----> +(-0.875, 0), (-1.125, 0.25) +(-0.625, 0), (-0.875, -0.25) // >>---> +(-0.625, 0), (-0.875, 0.25) diff --git a/wadsrc/static/maparrows/dagger.txt b/wadsrc/static/maparrows/dagger.txt new file mode 100644 index 000000000..0c16efc56 --- /dev/null +++ b/wadsrc/static/maparrows/dagger.txt @@ -0,0 +1,10 @@ +(-0.75, 0), (0, 0) // center line. +(-0.75, 0.125), (1, 0) // blade +(-0.75, -0.125), (1, 0 ) +(-0.75, -0.25), (-0.75, 0.25 ) // crosspiece +(-0.875, -0.25), (-0.875, 0.25 ) +(-0.875, -0.25), (-0.75, -0.25) //crosspiece connectors +(-0.875, 0.25), (-0.75, 0.25) +(-1.125, 0.125), (-1.125, -0.125 ) //pommel +(-1.125, 0.125), (-0.875, 0.125 ) +(-1.125, -0.125), (-0.875, -0.125) diff --git a/wadsrc/static/maparrows/ddtarrow.txt b/wadsrc/static/maparrows/ddtarrow.txt new file mode 100644 index 000000000..51919c72d --- /dev/null +++ b/wadsrc/static/maparrows/ddtarrow.txt @@ -0,0 +1,16 @@ +(-0.875, 0), (1, 0) // ----- +(1, 0), (0.5, 0.167) // -----> +(1, 0), (0.5, -0.167) +(-0.875, 0), (-1.125, -0.167) // >----> +(-0.875, 0), (-1.125, 0.167) +(-0.625, 0), (-0.875, -0.167) // >>---> +(-0.625, 0), (-0.875, 0.167) +(-0.5, 0), (-0.5, -0.167) // >>-d---> +(-0.5, -0.167), (-0.333, -0.167) +(-0.333, -0.167), (-0.333, 0.25) +(-0.167, 0), (-0.167, -0.167) // >>-dd--> +(-0.167, -0.167), (0, -0.167) +(0, -0.167), (0, 0.25) +(0.167, 0.25), (0.167, -0.143) // >>-ddt-> +(0.167, -0.143), (0.198, -0.174) +(0.198, -0.174), (0.267, -0.143) diff --git a/wadsrc/static/maparrows/key.txt b/wadsrc/static/maparrows/key.txt new file mode 100644 index 000000000..f252d0bfd --- /dev/null +++ b/wadsrc/static/maparrows/key.txt @@ -0,0 +1,12 @@ +(-2, 0), (-1.7, -0.5) +(-1.7, -0.5), (-1.5, -0.7) +(-1.5, -0.7), (-0.8, -0.5) +(-0.8, -0.5), (-0.6, 0) +(-0.6, 0), (-0.8, 0.5) +(-1.5, 0.7), (-0.8, 0.5) +(-1.7, 0.5), (-1.5, 0.7) +(-2, 0), (-1.7, 0.5) +(-0.6, 0), (2, 0) +(1.7, 0), (1.7, -1) +(1.5, 0), (1.5, -1) +(1.3, 0), (1.3, -1) diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index 7ff6e2d67..79f27f5bf 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -60,6 +60,7 @@ gameinfo textscreenx = 10 textscreeny = 10 defaultendsequence = "Inter_Pic1" + maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" } skill baby diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 0ad964ca5..73d003795 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -61,6 +61,7 @@ gameinfo textscreenx = 10 textscreeny = 10 defaultendsequence = "Inter_Cast" + maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 0e269c8f7..57aa839f4 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -60,6 +60,7 @@ gameinfo textscreenx = 20 textscreeny = 5 defaultendsequence = "Inter_Pic1" + maparrow = "maparrows/dagger.txt" } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 6022c541e..eeef3b92a 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -58,6 +58,7 @@ gameinfo textscreenx = 10 textscreeny = 5 defaultendsequence = "Inter_Chess" + maparrow = "maparrows/dagger.txt" } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index aed50b223..2e6c242cb 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -60,6 +60,7 @@ gameinfo textscreenx = 10 textscreeny = 10 defaultendsequence = "Inter_Strife" + maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" } Intermission Inter_Strife_Good From 8ebb555343149ac980636e5529371885d7f9e763 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 16 Oct 2010 22:40:27 +0000 Subject: [PATCH 118/815] - fixed: Detection of MAP01 presence was wrong. SVN r2950 (trunk) --- src/d_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 75103e955..26954b564 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1809,7 +1809,7 @@ static FString CheckGameInfo(TArray & pwads) static void SetMapxxFlag() { - int lump_name = Wads.CheckNumForName("MAP01", FWadCollection::IWAD_FILENUM); + int lump_name = Wads.CheckNumForName("MAP01", ns_global, FWadCollection::IWAD_FILENUM); int lump_wad = Wads.CheckNumForFullName("maps/map01.wad", FWadCollection::IWAD_FILENUM); int lump_map = Wads.CheckNumForFullName("maps/map01.map", FWadCollection::IWAD_FILENUM); From 6538bc457b1e8183de588c1e6ea68c72384728a4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 07:56:49 +0000 Subject: [PATCH 119/815] - fixed: The cast call could time out on overlong death sequences and get stuck. - fixed: The cast call code should treat a waiting state in the death sequence as its end. SVN r2951 (trunk) --- src/intermission/intermission.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index acb9fb62c..74a98a6a2 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -478,7 +478,8 @@ int DIntermissionScreenCast::Ticker () if (--casttics > 0 && caststate != NULL) return 0; // not time to change state yet - if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL) + if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL || + caststate->GetNextState() == caststate) { return -1; } @@ -494,7 +495,7 @@ int DIntermissionScreenCast::Ticker () castframes++; } - if (castframes == 12) + if (castframes == 12 && !castdeath) { // go into attack frame castattacking = true; From 94fd56b6bdde9cb26e5e89f1dc04474dc261d627 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 08:02:04 +0000 Subject: [PATCH 120/815] - added option for the cast call to use a 'Death.Cast' sequence so that monsters with an unusable death sequence for the cast call can define an alternative. SVN r2952 (trunk) --- src/intermission/intermission.cpp | 4 +++- src/namedef.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 74a98a6a2..8448bf003 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -433,7 +433,9 @@ int DIntermissionScreenCast::Responder (event_t *ev) return 1; // already in dying frames castdeath = true; - caststate = mClass->ActorInfo->FindState(NAME_Death); + + FName label[] = {NAME_Death, NAME_Cast}; + caststate = mClass->ActorInfo->FindState(2, label); if (caststate == NULL) return -1; casttics = caststate->GetTics(); diff --git a/src/namedef.h b/src/namedef.h index c9168fed3..2405da0fd 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -186,6 +186,7 @@ xx(Greetings) xx(Idle) xx(GenericFreezeDeath) xx(GenericCrush) +xx(Cast) // Compatible death names for the decorate parser. xx(XDeath) From fe0c7bc7f9d9ccee475cc975745ce38a4a9f5e6b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 08:29:21 +0000 Subject: [PATCH 121/815] - Let FPlayerNameBox::DrawBorder decide what graphics to use based on actual presence in the WADs, not the gamemode. SVN r2953 (trunk) --- src/menu/playermenu.cpp | 21 ++++++++++++++++----- src/namedef.h | 8 +------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index c4ad7b86b..03300af14 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -115,23 +115,34 @@ bool FPlayerNameBox::GetString(int i, char *s, int len) void FPlayerNameBox::DrawBorder (int x, int y, int len) { - if (gameinfo.gametype & (GAME_DoomStrifeChex)) + FTexture *left = TexMan[TexMan.CheckForTexture("M_LSLEFT", FTexture::TEX_MiscPatch)]; + FTexture *mid = TexMan[TexMan.CheckForTexture("M_LSCNTR", FTexture::TEX_MiscPatch)]; + FTexture *right = TexMan[TexMan.CheckForTexture("M_LSRGHT", FTexture::TEX_MiscPatch)]; + if (left != NULL && right != NULL && mid != NULL) { int i; - screen->DrawTexture (TexMan["M_LSLEFT"], x-8, y+7, DTA_Clean, true, TAG_DONE); + screen->DrawTexture (left, 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); + screen->DrawTexture (mid, x, y+7, DTA_Clean, true, TAG_DONE); x += 8; } - screen->DrawTexture (TexMan["M_LSRGHT"], x, y+7, DTA_Clean, true, TAG_DONE); + screen->DrawTexture (right, x, y+7, DTA_Clean, true, TAG_DONE); } else { - screen->DrawTexture (TexMan["M_FSLOT"], x, y+1, DTA_Clean, true, TAG_DONE); + FTexture *slot = TexMan[TexMan.CheckForTexture("M_FSLOT", FTexture::TEX_MiscPatch)]; + if (slot != NULL) + { + screen->DrawTexture (slot, x, y+1, DTA_Clean, true, TAG_DONE); + } + else + { + screen->Clear(x, y, x + len, y + SmallFont->GetHeight() * 3/2, -1, 0); + } } } diff --git a/src/namedef.h b/src/namedef.h index 2405da0fd..4a00c99c9 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -105,12 +105,6 @@ xx(ArtiPoisonBag3) // Strife quests xx(QuestItem) -// Auto-usable health items -xx(ArtiHealth) -xx(ArtiSuperHealth) -xx(MedicalKit) -xx(MedPatch) - // Armor xx(BasicArmor) @@ -186,7 +180,6 @@ xx(Greetings) xx(Idle) xx(GenericFreezeDeath) xx(GenericCrush) -xx(Cast) // Compatible death names for the decorate parser. xx(XDeath) @@ -252,6 +245,7 @@ xx(PoisonCloud) // makes monsters howl. // a damage type if you wanted to force an extreme death. xx(Extreme) xx(MDK) +xx(Cast) // 'damage type' for the cast call // Special names for thingdef_exp.cpp xx(Random) From 8b63758019af550bb1e4fcdff177d0d22bd5c903 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 13:06:02 +0000 Subject: [PATCH 122/815] - fixed: Hexen loaded the Doom2 MAPINFO. SVN r2954 (trunk) --- wadsrc/static/iwadinfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 0ee148b17..470012579 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -91,7 +91,7 @@ IWad Name = "Hexen: Beyond Heretic" Game = "Hexen" Config = "Hexen" - Mapinfo = "mapinfo/doom2.txt" + Mapinfo = "mapinfo/hexen.txt" Compatibility = "Poly1" MustContain = "TITLE", "MAP01", "MAP40", "WINNOWR" BannerColors = "f0 f0 f0", "6b 3c 18" From f318653a111406e64d8a99afddebc1bd52d004ff Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 13:25:21 +0000 Subject: [PATCH 123/815] - fixed: The intermission data was never freed. SVN r2955 (trunk) --- src/g_mapinfo.cpp | 16 +++++++++++++++- src/intermission/intermission.h | 3 ++- src/intermission/intermission_parse.cpp | 13 +++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index e15de22a2..84b6aa48f 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1787,6 +1787,20 @@ void FMapInfoParser::ParseMapInfo (int lump, level_info_t &gamedefaults, level_i } +//========================================================================== +// +// +// +//========================================================================== + +void DeinitIntermissions(); + +static void ClearMapinfo() +{ + ClearEpisodes(); + DeinitIntermissions(); +} + //========================================================================== // // G_ParseMapInfo @@ -1800,7 +1814,7 @@ void G_ParseMapInfo (const char *basemapinfo) int lump, lastlump = 0; level_info_t gamedefaults; - atterm(ClearEpisodes); + atterm(ClearMapinfo); // Parse the default MAPINFO for the current game. This lump *MUST* come from zdoom.pk3. if (basemapinfo != NULL) diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h index 72dd5a2e1..4298b54b5 100644 --- a/src/intermission/intermission.h +++ b/src/intermission/intermission.h @@ -80,6 +80,7 @@ struct FIntermissionAction TArray mOverlays; FIntermissionAction(); + virtual ~FIntermissionAction() {} virtual bool ParseKey(FScanner &sc); }; @@ -145,7 +146,7 @@ struct FIntermissionActionScroller : public FIntermissionAction struct FIntermissionDescriptor { FName mLink; - TArray mActions; + TDeletingArray mActions; }; typedef TMap FIntermissionDescriptorList; diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index b79519863..bf6ae0ddd 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -47,6 +47,19 @@ static void ReplaceIntermission(FName intname,FIntermissionDescriptor *desc) IntermissionDescriptors[intname] = desc; } +void DeinitIntermissions() +{ + FIntermissionDescriptorList::Iterator it(IntermissionDescriptors); + + FIntermissionDescriptorList::Pair *pair; + + while (it.NextPair(pair)) + { + delete pair->Value; + pair->Value = NULL; + } +} + //========================================================================== // // FIntermissionAction From 0497171bc08a9efcab20bcaceee8a2649a0f4625 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 17 Oct 2010 23:22:09 +0000 Subject: [PATCH 124/815] - fixed: States jumping to themselves should only end an actor's cast call when it happens in the death sequence. SVN r2957 (trunk) --- src/intermission/intermission.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 8448bf003..f22df89fb 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -481,7 +481,7 @@ int DIntermissionScreenCast::Ticker () return 0; // not time to change state yet if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL || - caststate->GetNextState() == caststate) + (caststate->GetNextState() == caststate && castdeath)) { return -1; } From c9adcb0f47133cddeaeedf24749055623ea1d5f8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Oct 2010 22:33:39 +0000 Subject: [PATCH 125/815] - fixed: The GAMEINFO parser did not correctly handle NOSPRITERENAME - added STTPRCNT to HUDFONT_DOOM SVN r2961 (trunk) --- src/d_main.cpp | 3 ++- wadsrc/static/fontdefs.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 26954b564..61400d8a8 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1723,7 +1723,8 @@ static FString ParseGameInfo(TArray &pwads, const char *fn, const char } else if (!nextKey.CompareNoCase("NOSPRITERENAME")) { - nospriterename = true; + sc.MustGetString(); + nospriterename = sc.Compare("true"); } else if (!nextKey.CompareNoCase("STARTUPTITLE")) { diff --git a/wadsrc/static/fontdefs.txt b/wadsrc/static/fontdefs.txt index 19e53effd..a8e99bb3d 100644 --- a/wadsrc/static/fontdefs.txt +++ b/wadsrc/static/fontdefs.txt @@ -4,6 +4,7 @@ HUDFONT_DOOM { - STTMINUS + % STTPRCNT 0 STTNUM0 1 STTNUM1 2 STTNUM2 From 747f3dd97ad42a4c940754432082429fe4b09f25 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Oct 2010 22:42:53 +0000 Subject: [PATCH 126/815] - fixed: The intermission screen was not taking texture scaling into account (it was written before the introduction of scaled texture handling for 2D.) SVN r2962 (trunk) --- src/wi_stuff.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 7c07ae783..422916814 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -79,7 +79,7 @@ void WI_unloadData (); // NET GAME STUFF #define NG_STATSY 50 -#define NG_STATSX (32 + star->GetWidth()/2 + 32*!dofrags) +#define NG_STATSX (32 + star->GetScaledWidth()/2 + 32*!dofrags) #define NG_SPACINGX 64 @@ -606,8 +606,8 @@ void WI_drawBackground() // scale all animations below to fit the size of the base pic // The base pic is always scaled to fit the screen so this allows // placing the animations precisely where they belong on the base pic - animwidth = background->GetScaledWidth(); - animheight = background->GetScaledHeight(); + animwidth = background->GetScaledWidthDouble(); + animheight = background->GetScaledHeightDouble(); screen->FillBorder (NULL); screen->DrawTexture(background, 0, 0, DTA_Fullscreen, true, TAG_DONE); } @@ -746,8 +746,8 @@ int WI_drawLF () // draw if (tex) { - screen->DrawTexture(tex, midx - tex->GetWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); - y += (tex->GetHeight() + BigFont->GetHeight()/4) * CleanYfac; + screen->DrawTexture(tex, midx - tex->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); + y += (tex->GetScaledHeight() + BigFont->GetHeight()/4) * CleanYfac; } else { @@ -761,8 +761,8 @@ int WI_drawLF () // don't draw 'finished' if the level name is too high! if (gameinfo.gametype & GAME_DoomChex) { - screen->DrawTexture(finished, midx - finished->GetWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); - return y + finished->GetHeight() * CleanYfac; + screen->DrawTexture(finished, midx - finished->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); + return y + finished->GetScaledHeight() * CleanYfac; } else { @@ -793,8 +793,8 @@ void WI_drawEL () // be careful with the added height so that it works for oversized 'entering' patches! if (gameinfo.gametype & GAME_DoomChex) { - screen->DrawTexture(entering, (SCREENWIDTH - entering->GetWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); - y += (entering->GetHeight() + font->GetHeight()/4) * CleanYfac; + screen->DrawTexture(entering, (SCREENWIDTH - entering->GetScaledWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); + y += (entering->GetScaledHeight() + font->GetHeight()/4) * CleanYfac; } else { @@ -808,7 +808,7 @@ void WI_drawEL () FTexture *tex = wbs->LName1; if (tex) { - screen->DrawTexture(tex, (SCREENWIDTH - tex->GetWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); + screen->DrawTexture(tex, (SCREENWIDTH - tex->GetScaledWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); } else { @@ -853,10 +853,10 @@ void WI_drawOnLnode( int n, FTexture * c[] ,int numc) int bottom; - right = c[i]->GetWidth(); - bottom = c[i]->GetHeight(); - left = lnodes[n].x - c[i]->LeftOffset; - top = lnodes[n].y - c[i]->TopOffset; + right = c[i]->GetScaledWidth(); + bottom = c[i]->GetScaledHeight(); + left = lnodes[n].x - c[i]->GetScaledLeftOffset(); + top = lnodes[n].y - c[i]->GetScaledTopOffset(); right += left; bottom += top; @@ -980,7 +980,7 @@ void WI_drawTime (int x, int y, int t, bool no_sucks=false) { // "sucks" if (sucks != NULL) { - screen->DrawTexture (sucks, x - sucks->GetWidth(), y - IntermissionFont->GetHeight() - 2, + screen->DrawTexture (sucks, x - sucks->GetScaledWidth(), y - IntermissionFont->GetHeight() - 2, DTA_Clean, true, TAG_DONE); } else From 40c75b811d8dcfaf9f4f702280b7f020abb53010 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 Oct 2010 23:05:56 +0000 Subject: [PATCH 127/815] - fixed: When used on non-projectiles A_Explode ignored the HurtSource flag. SVN r2963 (trunk) --- src/p_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 424591be5..3c791fea6 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4325,7 +4325,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b if (thing->flags3 & MF3_NORADIUSDMG && !(bombspot->flags4 & MF4_FORCERADIUSDMG)) continue; - if (!DamageSource && thing == bombsource) + if (!DamageSource && (thing == bombsource || thing == bombspot)) { // don't damage the source of the explosion continue; } From 2a0c4b9f32b7dda19d9d63fe979767a5c8f56e9b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Oct 2010 07:31:39 +0000 Subject: [PATCH 128/815] - added a CVAR to decide when to show the map label (ExMy, MAPxx) on the automap HUD. Available settings are Never, Always and Only for hubs. - made all crosshair related CVARs game specific. They were all global to all supportesd games. SVN r2964 (trunk) --- src/g_shared/sbar.h | 1 + src/g_shared/shared_hud.cpp | 7 +++-- src/g_shared/shared_sbar.cpp | 51 +++++++++++++++++++++++++----------- wadsrc/static/menudef.txt | 8 ++++++ 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 295e0e4e1..b2f6173bf 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -381,6 +381,7 @@ DBaseStatusBar *CreateCustomStatusBar(int script=0); // Crosshair stuff ---------------------------------------------------------- +void ST_FormatMapName(FString &mapname, const char *mapnamecolor = ""); void ST_LoadCrosshair(bool alwaysload=false); extern FTexture *CrosshairImage; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index f82e866f6..d766d8902 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -833,10 +833,9 @@ void DrawHUD() } else { + FString mapname; char printstr[256]; int seconds; - cluster_info_t *thiscluster = FindClusterInfo (level.cluster); - bool hub = !!(thiscluster->flags&CLUSTER_HUB); int length=8*SmallFont->GetCharWidth('0'); int fonth=SmallFont->GetHeight()+1; int bottom=hudheight-1; @@ -865,8 +864,8 @@ void DrawHUD() } } - mysnprintf(printstr, countof(printstr), "%s: %s", level.mapname, level.LevelName.GetChars()); - screen->DrawText(SmallFont, hudcolor_titl, 1, hudheight-fonth-1, printstr, + ST_FormatMapName(mapname); + screen->DrawText(SmallFont, hudcolor_titl, 1, hudheight-fonth-1, mapname, DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, TAG_DONE); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index beb459cda..75084ae38 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -95,12 +95,16 @@ CUSTOM_CVAR (Bool, st_scale, true, CVAR_ARCHIVE) } } -CVAR (Int, crosshair, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, crosshairforce, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR (Bool, crosshairhealth, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR (Bool, crosshairscale, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR (Bool, crosshairgrow, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR (Int, crosshair, 0, CVAR_ARCHIVE) +CVAR (Bool, crosshairforce, false, CVAR_ARCHIVE) +CVAR (Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE); +CVAR (Bool, crosshairhealth, true, CVAR_ARCHIVE); +CVAR (Bool, crosshairscale, false, CVAR_ARCHIVE); +CVAR (Bool, crosshairgrow, false, CVAR_ARCHIVE); +CUSTOM_CVAR(Int, am_showmaplabel, 2, CVAR_ARCHIVE) +{ + if (self < 0 || self > 2) self = 2; +} CVAR (Bool, idmypos, false, 0); @@ -118,6 +122,30 @@ BYTE DBaseStatusBar::DamageToAlpha[114] = 230, 231, 232, 233, 234, 235, 235, 236, 237 }; +//--------------------------------------------------------------------------- +// +// Format the map name, include the map label if wanted +// +//--------------------------------------------------------------------------- + +void ST_FormatMapName(FString &mapname, const char *mapnamecolor) +{ + cluster_info_t *cluster = FindClusterInfo (level.cluster); + bool ishub = (cluster != NULL && (cluster->flags & CLUSTER_HUB)); + + if (am_showmaplabel == 1 || (am_showmaplabel == 2 && !ishub)) + { + mapname << level.mapname << ": "; + } + mapname << mapnamecolor << level.LevelName; +} + +//--------------------------------------------------------------------------- +// +// Load crosshair definitions +// +//--------------------------------------------------------------------------- + void ST_LoadCrosshair(bool alwaysload) { int num = 0; @@ -1270,18 +1298,9 @@ void DBaseStatusBar::Draw (EHudState state) y -= 8; } } - cluster_info_t *cluster = FindClusterInfo (level.cluster); - if (cluster == NULL || !(cluster->flags & CLUSTER_HUB)) - { - mysnprintf (line, countof(line), "%s: ", level.mapname); - } - else - { - *line = 0; - } FString mapname; - mapname.Format("%s%c%c%s", line, TEXTCOLOR_ESCAPE, CR_GREY + 'A', level.LevelName.GetChars()); + ST_FormatMapName(mapname, TEXTCOLOR_GREY); screen->DrawText (SmallFont, highlight, (SCREENWIDTH - SmallFont->StringWidth (mapname)*CleanXfac)/2, y, mapname, DTA_CleanNoMove, true, TAG_DONE); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index db8d4bd45..1f5cf9207 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -841,6 +841,13 @@ OptionValue OverlayTypes 2, "Overlay Only" } +OptionValue MaplabelTypes +{ + 0, "Never" + 1, "Always" + 2, "Not for hubs" +} + OptionMenu AutomapOptions { Title "AUTOMAP OPTIONS" @@ -858,6 +865,7 @@ OptionMenu AutomapOptions 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 "Show map label", "am_showmaplabel", "MaplabelTypes" Option "Draw map background", "am_drawmapback", "OnOff" Option "Show keys (cheat)", "am_showkeys", "OnOff" } From b0c7ac6868e75529a530a13fff417671087612e4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 24 Oct 2010 07:39:48 +0000 Subject: [PATCH 129/815] - made "follow player" automap option a CVAR and added a menu item for it. SVN r2965 (trunk) --- src/am_map.cpp | 17 +++++++++-------- wadsrc/static/menudef.txt | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index ce2c55979..2de0a5bc3 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -385,8 +385,6 @@ static FTextureID marknums[10]; // numbers used for marking by the automap static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are static int markpointnum = 0; // next point to be assigned -static int followplayer = 1; // specifies whether to follow the player around - static FTextureID mapback; // the automap background static fixed_t mapystart=0; // y-value for the start of the map bitmap...used in the parallax stuff. static fixed_t mapxstart=0; //x-value for the bitmap. @@ -412,11 +410,14 @@ void AM_restoreScaleAndLoc (); void AM_minOutWindowScale (); +CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE) + + CCMD(am_togglefollow) { - followplayer = !followplayer; + am_followplayer = !am_followplayer; f_oldloc.x = FIXED_MAX; - Printf ("%s\n", GStrings(followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF")); + Printf ("%s\n", GStrings(am_followplayer ? "AMSTR_FOLLOWON" : "AMSTR_FOLLOWOFF")); } CCMD(am_togglegrid) @@ -580,7 +581,7 @@ void AM_restoreScaleAndLoc () { m_w = old_m_w; m_h = old_m_h; - if (!followplayer) + if (!am_followplayer) { m_x = old_m_x; m_y = old_m_y; @@ -777,7 +778,7 @@ void AM_changeWindowLoc () { if (0 != (m_paninc.x | m_paninc.y)) { - followplayer = 0; + am_followplayer = false; f_oldloc.x = FIXED_MAX; } @@ -1199,7 +1200,7 @@ bool AM_Responder (event_t *ev, bool last) { if (automapactive && (ev->type == EV_KeyDown || ev->type == EV_KeyUp)) { - if (followplayer) + if (am_followplayer) { // check for am_pan* and ignore in follow mode const char *defbind = AutomapBindings.GetBind(ev->data1); @@ -1312,7 +1313,7 @@ void AM_Ticker () amclock++; - if (followplayer) + if (am_followplayer) { AM_doFollowPlayer(); } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 1f5cf9207..4a577d907 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -858,6 +858,7 @@ OptionMenu AutomapOptions Option "Rotate automap", "am_rotate", "RotateTypes" Option "Overlay automap", "am_overlay", "OverlayTypes" Option "Enable textured display", "am_textured", "OnOff" + Option "Follow player", "am_followplayer", "OnOff" StaticText " " Option "Show item counts", "am_showitems", "OnOff" Option "Show monster counts", "am_showmonsters", "OnOff" From 1287c9419a6dcc317977eba6347e85ced4b84374 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 24 Oct 2010 17:46:48 +0000 Subject: [PATCH 130/815] - Fixed: Controller buttons were still translated to menu buttons when the controls menu was waiting for a button press, making it impossible to bind buttons that have special meaning to the menu from the menu. SVN r2967 (trunk) --- src/menu/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index b4e64b6a9..0bdc622fc 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -544,7 +544,7 @@ bool M_Responder (event_t *ev) } } } - else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp) + else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp)) { keyup = ev->type == EV_KeyUp; From 48e17ccf1ce2d20bba37e7944f8d33b191d6b6a2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 30 Oct 2010 07:21:16 +0000 Subject: [PATCH 131/815] - fixed: The cast call could not handle actors with changing sprites - fixed: The cast call was missing some NULL pointer checks for invalid actor classes. - fixed: The cast call did not use a translation defined for an actor class. SVN r2968 (trunk) --- src/intermission/intermission.cpp | 62 +++++++++++++++++++++---------- src/intermission/intermission.h | 1 - 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index f22df89fb..0b8afa1e4 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -392,7 +392,12 @@ void DIntermissionScreenCast::Init(FIntermissionAction *desc, bool first) mName = static_cast(desc)->mName; mClass = PClass::FindClass(static_cast(desc)->mCastClass); if (mClass != NULL) mDefaults = GetDefaultByType(mClass); - else mDefaults = NULL; + else + { + mDefaults = NULL; + caststate = NULL; + return; + } mCastSounds.Resize(static_cast(desc)->mCastSounds.Size()); for (unsigned i=0; i < mCastSounds.Size(); i++) @@ -405,15 +410,17 @@ void DIntermissionScreenCast::Init(FIntermissionAction *desc, bool first) if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) { advplayerstate = mDefaults->MissileState; - castsprite = skins[players[consoleplayer].userinfo.skin].sprite; casttranslation = translationtables[TRANSLATION_Players][consoleplayer]; } else { advplayerstate = NULL; - if (caststate != NULL) castsprite = caststate->sprite; - else castsprite = -1; casttranslation = NULL; + if (mDefaults->Translation != 0) + { + casttranslation = translationtables[GetTranslationType(mDefaults->Translation)] + [GetTranslationIndex(mDefaults->Translation)]; + } } castdeath = false; castframes = 0; @@ -434,22 +441,25 @@ int DIntermissionScreenCast::Responder (event_t *ev) castdeath = true; - FName label[] = {NAME_Death, NAME_Cast}; - caststate = mClass->ActorInfo->FindState(2, label); - if (caststate == NULL) return -1; - - casttics = caststate->GetTics(); - castframes = 0; - castattacking = false; - - if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + if (mClass != NULL) { - int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); - if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); - } - else if (mDefaults->DeathSound) - { - S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->DeathSound, 1, ATTN_NONE); + FName label[] = {NAME_Death, NAME_Cast}; + caststate = mClass->ActorInfo->FindState(2, label); + if (caststate == NULL) return -1; + + casttics = caststate->GetTics(); + castframes = 0; + castattacking = false; + + if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + { + int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); + if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); + } + else if (mDefaults->DeathSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->DeathSound, 1, ATTN_NONE); + } } return true; } @@ -562,6 +572,20 @@ void DIntermissionScreenCast::Drawer () // draw the current frame in the middle of the screen if (caststate != NULL) { + int castsprite; + + if (!(mDefaults->flags4 & MF4_NOSKIN) && + mDefaults->SpawnState != NULL && caststate->sprite == mDefaults->SpawnState->sprite && + mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)) && + skins != NULL) + { + castsprite = skins[players[consoleplayer].userinfo.skin].sprite; + } + else + { + castsprite = caststate->sprite; + } + sprframe = &SpriteFrames[sprites[castsprite].spriteframes + caststate->GetFrame()]; pic = TexMan(sprframe->Texture[0]); diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h index 4298b54b5..1035bd8f3 100644 --- a/src/intermission/intermission.h +++ b/src/intermission/intermission.h @@ -234,7 +234,6 @@ class DIntermissionScreenCast : public DIntermissionScreen TArray mCastSounds; int casttics; - int castsprite; // [RH] For overriding the player sprite with a skin const FRemapTable *casttranslation; // [RH] Draw "our hero" with their chosen suit color FState* caststate; FState* basestate; From 92d11da8a577f08b55e1d057b43fc362296f18a3 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:07:56 +0000 Subject: [PATCH 132/815] - Revised usage of jumpTics. In Hexen, it went like this: * When you jump, it gets set to 18. * When you land, it gets set to 7. * As long as it is non-zero, it counts down, and you cannot jump. Of note here, is that setting it to 18 upon jumping seems useless, since you can't jump unless you're on the ground, and when you reach the ground, it will always be set to 7. With that in mind, the new behavior is: * When you jump, it gets set to -1. * When you land, if it is less than zero or you fall far enough to squat, jumpTics will be set to 7. Otherwise, jumpTics is left alone. * If jumpTics is positive, it will count down each tic. * As long as JumpTics is non-zero, you cannot jump. SVN r2970 (trunk) --- src/p_mobj.cpp | 2 +- src/p_user.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 71e0a2512..b614a25f2 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2253,7 +2253,7 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) mo->HitFloor (); if (mo->player) { - if (mo->player->jumpTics != 0 && mo->velz < -grav*4) + if (mo->player->jumpTics < 0 || mo->velz < minvel) { // delay any jumping for a short while mo->player->jumpTics = 7; } diff --git a/src/p_user.cpp b/src/p_user.cpp index e844d2680..f80ddb9ae 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2156,7 +2156,7 @@ void P_PlayerThink (player_t *player) P_DeathThink (player); return; } - if (player->jumpTics) + if (player->jumpTics > 0) { player->jumpTics--; } @@ -2217,7 +2217,7 @@ void P_PlayerThink (player_t *player) // [RH] check for jump if (cmd->ucmd.buttons & BT_JUMP) { - if (player->crouchoffset!=0) + if (player->crouchoffset != 0) { // Jumping while crouching will force an un-crouch but not jump player->crouching = 1; @@ -2231,7 +2231,7 @@ void P_PlayerThink (player_t *player) { player->mo->velz = 3*FRACUNIT; } - else if (level.IsJumpingAllowed() && onground && !player->jumpTics) + else if (level.IsJumpingAllowed() && onground && player->jumpTics == 0) { fixed_t jumpvelz = player->mo->JumpZ * 35 / TICRATE; @@ -2241,7 +2241,7 @@ void P_PlayerThink (player_t *player) player->mo->velz += jumpvelz; S_Sound (player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM); player->mo->flags2 &= ~MF2_ONMOBJ; - player->jumpTics = 18*TICRATE/35; + player->jumpTics = -1; } } From 810ca55e2a2d3dc55e94adb85197571750b68760 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:11:06 +0000 Subject: [PATCH 133/815] - Fixed: The minimum velocity for player landing in effects in P_ZMovement should be -8, not -9. SVN r2971 (trunk) --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index b614a25f2..1d6e8d40a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2237,7 +2237,7 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) mo->z = mo->floorz; if (mo->velz < 0) { - const fixed_t minvel = -9*FRACUNIT; // landing speed from a jump with normal gravity + const fixed_t minvel = -8*FRACUNIT; // landing speed from a jump with normal gravity // Spawn splashes, etc. P_HitFloor (mo); From db2147c700cab93f9ec8dff68e23d997eff2b23f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:17:08 +0000 Subject: [PATCH 134/815] - Fixed: Options selected in Strife dialogues using the number keys were off by one. SVN r2972 (trunk) --- src/p_conversation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 3c631c4e9..9b1119e0d 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -914,7 +914,7 @@ public: { 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'; + mSelection = ev->data1 == '0' ? 9 : ev->data1 - '1'; return MenuEvent(MKEY_Enter, false); } return Super::Responder(ev); From 18205c82a605f987e9cb90bcb939c1ff7e6ba8e5 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:27:46 +0000 Subject: [PATCH 135/815] - Modify AimingCamera so that it can pick up targets after spawning, since this is the only way for it to aim at players, who cannot be spawned with TIDs. SVN r2973 (trunk) --- src/g_shared/a_camera.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/g_shared/a_camera.cpp b/src/g_shared/a_camera.cpp index 99da16ae7..c75ccbe80 100644 --- a/src/g_shared/a_camera.cpp +++ b/src/g_shared/a_camera.cpp @@ -143,12 +143,21 @@ void AAimingCamera::PostBeginPlay () tracer = iterator.Next (); if (tracer == NULL) { - Printf ("AimingCamera %d: Can't find thing %d\n", tid, args[3]); + //Printf ("AimingCamera %d: Can't find TID %d\n", tid, args[3]); + } + else + { // Don't try for a new target upon losing this one. + args[3] = 0; } } void AAimingCamera::Tick () { + if (tracer == NULL && args[3] != 0) + { // Recheck, in case something with this TID was created since the last time. + TActorIterator iterator (args[3]); + tracer = iterator.Next (); + } if (tracer != NULL) { angle_t delta; From 0604d308d54a523a9f297ea72df1b8a727737926 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:33:22 +0000 Subject: [PATCH 136/815] - Fixed: Pressing left or right on a video mode option line should play "menu/cursor", not "menu/change". SVN r2974 (trunk) --- src/menu/optionmenuitems.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index e365f6039..8aab6f9c6 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -891,13 +891,13 @@ public: if (mkey == MKEY_Left) { if (--mSelection < 0) mSelection = mMaxValid; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); return true; } else if (mkey == MKEY_Right) { if (++mSelection > mMaxValid) mSelection = 0; - S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE); + S_Sound (CHAN_VOICE | CHAN_UI, "menu/cursor", snd_menuvolume, ATTN_NONE); return true; } else From c09a56ebe0439760b6a0ed38105c0f3dc50a899d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 3 Nov 2010 02:46:38 +0000 Subject: [PATCH 137/815] - Added a new AmbientSoundNoGravity actor with doomednum 14067. This is identical in every respect to the existing AmbientSound actor, except it also has the NOGRAVITY flag set. SVN r2975 (trunk) --- wadsrc/static/actors/shared/soundsequence.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wadsrc/static/actors/shared/soundsequence.txt b/wadsrc/static/actors/shared/soundsequence.txt index 871bd7241..ff240ba9c 100644 --- a/wadsrc/static/actors/shared/soundsequence.txt +++ b/wadsrc/static/actors/shared/soundsequence.txt @@ -6,6 +6,11 @@ ACTOR AmbientSound 14065 native +DONTSPLASH } +ACTOR AmbientSoundNoGravity : AmbientSound 14067 +{ + +NOGRAVITY +} + ACTOR SoundSequenceSlot native { +NOSECTOR From 2f61653caec59d25ac4a0acbaa10bf360fe0d653 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Nov 2010 23:27:31 +0000 Subject: [PATCH 138/815] - fixed: AdjustPusher compared a sector's index with a tag to check for existing pushers in that sector. SVN r2976 (trunk) --- src/p_lnspec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index d71bfbf73..a7f799c8b 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1897,7 +1897,7 @@ void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type) unsigned int i; for (i = 0; i < numcollected; i++) { - if (Collection[i].RefNum == sectors[secnum].tag) + if (Collection[i].RefNum == sectors[secnum].sectornum) break; } if (i == numcollected) From c02339501c1b3150659baaf2f1884d1ff77e07ec Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Nov 2010 23:33:57 +0000 Subject: [PATCH 139/815] - fixed: The message for trying to quickload in netgames used the wrong display mode for the message menu. SVN r2977 (trunk) --- src/menu/messagebox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/messagebox.cpp b/src/menu/messagebox.cpp index eecd997e2..e415f57c8 100644 --- a/src/menu/messagebox.cpp +++ b/src/menu/messagebox.cpp @@ -679,7 +679,7 @@ CCMD (quickload) if (netgame) { - M_StartMessage (GStrings("QLOADNET"), NULL); + M_StartMessage (GStrings("QLOADNET"), 1); return; } From d45262f96b5103d14826fe595345c82840843d76 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Nov 2010 23:43:34 +0000 Subject: [PATCH 140/815] - fixed: With the Buddha cheat active the health of the real player actor was not synchronized with the player data if a voodoo doll received damage that would have killed it. SVN r2978 (trunk) --- src/p_interaction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 93bb0c441..4a082cff0 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1164,7 +1164,8 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage // but telefragging should still do enough damage to kill the player) if ((player->cheats & CF_BUDDHA) && damage < TELEFRAG_DAMAGE) { - target->health = player->health = 1; + // If this is a voodoo doll we need to handle the real player as well. + player->mo->health = target->health = player->health = 1; } else { From ad200d8a56c36bf650a20192f2e9594ef2efde18 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 4 Nov 2010 02:19:34 +0000 Subject: [PATCH 141/815] - Tweaked jumpTics again. As before, it now counts down whenever it is non-zero. If the player is on the ground and it counts below -18, it is zeroed so that the player can jump again. This handles cases where either the player did not actually jump when they pressed +jump (because there was a ceiling in the way) or when they land on something other than the floor. SVN r2979 (trunk) --- src/p_user.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index f80ddb9ae..836b665d6 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2156,9 +2156,13 @@ void P_PlayerThink (player_t *player) P_DeathThink (player); return; } - if (player->jumpTics > 0) + if (player->jumpTics != 0) { player->jumpTics--; + if (onground && player->jumpTics < -18) + { + player->jumpTics = 0; + } } if (player->morphTics && !(player->cheats & CF_PREDICTING)) { From 4a9892725ddd163d405772d571f829580375be4c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 4 Nov 2010 03:47:49 +0000 Subject: [PATCH 142/815] - Use the so-called SafeTerminateProcess() function to kill the child TiMidity++ process instead of signaling an event. I would have preferred to use GenerateConsoleCtrlEvent(), but since it requires the caller be attached to the same console as the process it wants to kill, it's pretty much worthless. We will continue to look for the presence of the event name in the TiMidity++ binary despite no longer using it, because standard TiMidity++ builds do not write to stdout in binary mode on Windows systems. SVN r2980 (trunk) --- src/sound/i_musicinterns.h | 1 - src/sound/music_midi_timidity.cpp | 89 ++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 29b655d11..4ce8a7b09 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -202,7 +202,6 @@ protected: #ifdef _WIN32 HANDLE ReadWavePipe; HANDLE WriteWavePipe; - HANDLE KillerEvent; HANDLE ChildProcess; bool Validated; bool ValidateTimidity(); diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index dcaa934b9..bb897beff 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -20,8 +20,10 @@ void ChildSigHandler (int signum) #endif #ifdef _WIN32 -const char TimidityPPMIDIDevice::EventName[] = "TiMidity Killer"; +BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode); + static char TimidityTitle[] = "TiMidity (ZDoom Launched)"; +const char TimidityPPMIDIDevice::EventName[] = "TiMidity Killer"; CVAR (String, timidity_exe, "timidity.exe", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) #else @@ -71,7 +73,6 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice() : DiskName("zmid"), #ifdef _WIN32 ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), - KillerEvent(INVALID_HANDLE_VALUE), ChildProcess(INVALID_HANDLE_VALUE), Validated(false) #else @@ -108,11 +109,6 @@ TimidityPPMIDIDevice::~TimidityPPMIDIDevice () CloseHandle (ReadWavePipe); ReadWavePipe = INVALID_HANDLE_VALUE; } - if (KillerEvent != INVALID_HANDLE_VALUE) - { - CloseHandle (KillerEvent); - KillerEvent = INVALID_HANDLE_VALUE; - } #else if (WavePipe[1] != -1) { @@ -186,12 +182,6 @@ int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWO } Validated = true; - KillerEvent = CreateEvent(NULL, FALSE, FALSE, EventName); - if (KillerEvent == INVALID_HANDLE_VALUE) - { - Printf(PRINT_BOLD, "Could not create TiMidity++ kill event.\n"); - return 102; - } #endif // WIN32 CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", @@ -398,11 +388,11 @@ bool TimidityPPMIDIDevice::LaunchTimidity () startup.wShowWindow = SW_SHOWMINNOACTIVE; if (CreateProcess(NULL, CommandLine.LockBuffer(), NULL, NULL, TRUE, - /*HIGH_PRIORITY_CLASS|*/DETACHED_PROCESS, NULL, NULL, &startup, &procInfo)) + DETACHED_PROCESS, NULL, NULL, &startup, &procInfo)) { ChildProcess = procInfo.hProcess; //SetThreadPriority (procInfo.hThread, THREAD_PRIORITY_HIGHEST); - CloseHandle (procInfo.hThread); // Don't care about the created thread + CloseHandle(procInfo.hThread); // Don't care about the created thread CommandLine.UnlockBuffer(); return true; } @@ -647,10 +637,8 @@ void TimidityPPMIDIDevice::Stop () #ifdef _WIN32 if (ChildProcess != INVALID_HANDLE_VALUE) { - SetEvent(KillerEvent); - if (WaitForSingleObject(ChildProcess, 500) != WAIT_OBJECT_0) + if (!SafeTerminateProcess(ChildProcess, 666) && GetLastError() != ERROR_PROCESS_ABORTED) { - ResetEvent(KillerEvent); TerminateProcess(ChildProcess, 666); } CloseHandle(ChildProcess); @@ -670,3 +658,68 @@ void TimidityPPMIDIDevice::Stop () } Started = false; } + +#ifdef _WIN32 +/* + Safely terminate a process by creating a remote thread + in the process that calls ExitProcess + + Source is a Dr Dobbs article circa 1999. +*/ +typedef HANDLE (WINAPI *CreateRemoteThreadProto)(HANDLE,LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD); + +BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) +{ + DWORD dwTID, dwCode; + HRESULT dwErr = 0; + HANDLE hRT = NULL; + HINSTANCE hKernel = GetModuleHandle("Kernel32"); + BOOL bSuccess = FALSE; + + // Detect the special case where the process is already dead... + if ( GetExitCodeProcess(hProcess, &dwCode) && (dwCode == STILL_ACTIVE) ) + { + FARPROC pfnExitProc; + CreateRemoteThreadProto pfCreateRemoteThread; + + pfnExitProc = GetProcAddress(hKernel, "ExitProcess"); + + // CreateRemoteThread does not exist on 9x systems. + pfCreateRemoteThread = (CreateRemoteThreadProto)GetProcAddress(hKernel, "CreateRemoteThread"); + + if (pfCreateRemoteThread == NULL) + { + dwErr = ERROR_INVALID_FUNCTION; + } + else + { + hRT = pfCreateRemoteThread(hProcess, + NULL, + 0, + (LPTHREAD_START_ROUTINE)pfnExitProc, + (PVOID)(UINT_PTR)uExitCode, 0, &dwTID); + + if ( hRT == NULL ) + dwErr = GetLastError(); + } + } + else + { + dwErr = ERROR_PROCESS_ABORTED; + } + + if ( hRT ) + { + // Must wait process to terminate to guarantee that it has exited... + WaitForSingleObject(hProcess, INFINITE); + + CloseHandle(hRT); + bSuccess = TRUE; + } + + if ( !bSuccess ) + SetLastError(dwErr); + + return bSuccess; +} +#endif From 72192397adbec5e7c9e2c86bc9de645d7a4eba7b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 6 Nov 2010 09:28:17 +0000 Subject: [PATCH 143/815] - added some initial configurability to statistics intermission screen: * Font and color for map name can be set if it's not a titlepatch * 'Finished' and 'Entering' can be either patches or a printed text in all gamees now. * Font and color for 'finished' and 'entering' text can be set. * moved 'finished' and 'Now entering:' texts into string table. SVN r2981 (trunk) --- src/g_level.cpp | 7 +- src/gi.cpp | 27 ++++ src/gi.h | 9 ++ src/wi_stuff.cpp | 190 +++++++++++++++------------ wadsrc/static/language.enu | 3 + wadsrc/static/mapinfo/chex.txt | 3 + wadsrc/static/mapinfo/doomcommon.txt | 3 + wadsrc/static/mapinfo/heretic.txt | 3 + wadsrc/static/mapinfo/hexen.txt | 3 + wadsrc/static/mapinfo/strife.txt | 3 + 10 files changed, 162 insertions(+), 89 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 7008572a7..e3321f884 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -742,12 +742,7 @@ void G_DoCompleted (void) { // Reset world variables for the new hub. P_ClearACSVars(false); } - // With hub statistics the time should be per hub. - // Additionally there is a global time counter now so nothing is missed by changing it - //else if (mode == FINISH_NoHub) - { // Reset time to zero if not entering/staying in a hub. - level.time = 0; - } + level.time = 0; level.maptime = 0; } diff --git a/src/gi.cpp b/src/gi.cpp index 4dfac16f9..705f9b801 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -163,6 +163,28 @@ const char* GameInfoBoarders[] = } \ } +#define GAMEINFOKEY_FONT(key, variable) \ + else if(nextKey.CompareNoCase(variable) == 0) \ + { \ + sc.MustGetToken(TK_StringConst); \ + gameinfo.key.fontname = sc.String; \ + if (sc.CheckToken(',')) { \ + sc.MustGetToken(TK_StringConst); \ + gameinfo.key.color = sc.String; \ + } else { \ + gameinfo.key.color = NAME_None; \ + } \ + } + +#define GAMEINFOKEY_PATCH(key, variable) \ + else if(nextKey.CompareNoCase(variable) == 0) \ + { \ + sc.MustGetToken(TK_StringConst); \ + gameinfo.key.fontname = sc.String; \ + gameinfo.key.color = NAME_Null; \ + } + + void FMapInfoParser::ParseGameInfo() { sc.MustGetToken('{'); @@ -311,6 +333,11 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_INT(TextScreenX, "textscreenx") GAMEINFOKEY_INT(TextScreenY, "textscreeny") GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence") + GAMEINFOKEY_FONT(mStatscreenMapNameFont, "statscreen_mapnamefont") + GAMEINFOKEY_FONT(mStatscreenFinishedFont, "statscreen_finishedfont") + GAMEINFOKEY_FONT(mStatscreenEnteringFont, "statscreen_enteringfont") + GAMEINFOKEY_PATCH(mStatscreenFinishedFont, "statscreen_finishedpatch") + GAMEINFOKEY_PATCH(mStatscreenEnteringFont, "statscreen_enteringpatch") else { diff --git a/src/gi.h b/src/gi.h index d9422c039..424052625 100644 --- a/src/gi.h +++ b/src/gi.h @@ -66,6 +66,12 @@ struct gameborder_t char br[8]; }; +struct FGIFont +{ + FName fontname; + FName color; +}; + struct gameinfo_t { int flags; @@ -130,6 +136,9 @@ struct gameinfo_t int TextScreenY; FName DefaultEndSequence; FString mMapArrow, mCheatMapArrow; + FGIFont mStatscreenMapNameFont; + FGIFont mStatscreenFinishedFont; + FGIFont mStatscreenEnteringFont; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 422916814..dbf8446a9 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -47,6 +47,7 @@ #include "gi.h" #include "r_translate.h" #include "templates.h" +#include "gstrings.h" // States for the intermission typedef enum @@ -206,10 +207,39 @@ static bool noautostartmap; // GRAPHICS // +struct FPatchInfo +{ + FFont *mFont; + FTexture *mPatch; + EColorRange mColor; + + void Init(FGIFont &gifont) + { + if (gifont.color == NAME_Null) + { + mPatch = TexMan[gifont.fontname]; // "entering" + mColor = mPatch == NULL? CR_UNTRANSLATED : CR_UNDEFINED; + mFont = NULL; + } + else + { + mFont = V_GetFont(gifont.fontname); + mColor = V_FindFontColor(gifont.color); + mPatch = NULL; + } + if (mFont == NULL) + { + mFont = BigFont; + } + } +}; + +static FPatchInfo mapname; +static FPatchInfo finished; +static FPatchInfo entering; + static TArray yah; // You Are Here graphic static FTexture* splat; // splat -static FTexture* finished; // "Finished!" graphics -static FTexture* entering; // "Entering" graphic static FTexture* sp_secret; // "secret" static FTexture* kills; // "Kills", "Scrt", "Items", "Frags" static FTexture* secret; @@ -697,34 +727,67 @@ static int WI_DrawCharPatch (FFont *font, int charcode, int x, int y, EColorRang // //==================================================================== -int WI_DrawName(int y, const char *levelname) +int WI_DrawName(int y, FTexture *tex, const char *levelname) { - int i; - size_t l; - const char *p; - int h = 0; - int lumph; - - lumph = BigFont->GetHeight() * CleanYfac; - - p = levelname; - if (!p) return 0; - l = strlen(p); - if (!l) return 0; - - FBrokenLines *lines = V_BreakLines(BigFont, screen->GetWidth() / CleanXfac, p); - - if (lines) + // draw + if (tex) { - for (i = 0; lines[i].Width >= 0; i++) - { - screen->DrawText(BigFont, CR_UNTRANSLATED, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h, - lines[i].Text, DTA_CleanNoMove, true, TAG_DONE); - h += lumph; - } - V_FreeBrokenLines(lines); + screen->DrawTexture(tex, (screen->GetWidth() - tex->GetScaledWidth()*CleanXfac) /2, y, DTA_CleanNoMove, true, TAG_DONE); + return y + (tex->GetScaledHeight() + BigFont->GetHeight()/4) * CleanYfac; + } + else + { + int i; + size_t l; + const char *p; + int h = 0; + int lumph; + + lumph = mapname.mFont->GetHeight() * CleanYfac; + + p = levelname; + if (!p) return 0; + l = strlen(p); + if (!l) return 0; + + FBrokenLines *lines = V_BreakLines(mapname.mFont, screen->GetWidth() / CleanXfac, p); + + if (lines) + { + for (i = 0; lines[i].Width >= 0; i++) + { + screen->DrawText(mapname.mFont, mapname.mColor, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h, + lines[i].Text, DTA_CleanNoMove, true, TAG_DONE); + h += lumph; + } + V_FreeBrokenLines(lines); + } + return y + h + lumph/4; + } +} + +//==================================================================== +// +// Draws a text, either as patch or as string from the string table +// +//==================================================================== + +int WI_DrawPatchText(int y, FPatchInfo *pinfo, const char *stringname) +{ + const char *string = GStrings(stringname); + int midx = screen->GetWidth() / 2; + + if (pinfo->mPatch != NULL) + { + screen->DrawTexture(pinfo->mPatch, midx - pinfo->mPatch->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); + return y + (pinfo->mPatch->GetScaledHeight() * CleanYfac); + } + else + { + screen->DrawText(pinfo->mFont, pinfo->mColor, midx - pinfo->mFont->StringWidth(string)*CleanXfac/2, + y, string, DTA_CleanNoMove, true, TAG_DONE); + return y + pinfo->mFont->GetHeight() * CleanYfac; } - return h + lumph/4; } @@ -736,41 +799,21 @@ int WI_DrawName(int y, const char *levelname) // A level name patch can be specified for all games now, not just Doom. // //==================================================================== + int WI_drawLF () { int y = WI_TITLEY * CleanYfac; - int midx = screen->GetWidth() / 2; - FTexture *tex = wbs->LName0; - - // draw - if (tex) - { - screen->DrawTexture(tex, midx - tex->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); - y += (tex->GetScaledHeight() + BigFont->GetHeight()/4) * CleanYfac; - } - else - { - y += WI_DrawName(y, lnametexts[0]); - } + y = WI_DrawName(y, wbs->LName0, lnametexts[0]); + // Adjustment for different font sizes for map name and 'finished'. + y -= ((mapname.mFont->GetHeight() - finished.mFont->GetHeight()) * CleanYfac) / 4; + // draw "Finished!" - FFont *font = gameinfo.gametype & GAME_Raven ? SmallFont : BigFont; - if (y < (NG_STATSY - font->GetHeight()*3/4) * CleanYfac) + if (y < (NG_STATSY - finished.mFont->GetHeight()*3/4) * CleanYfac) { - // don't draw 'finished' if the level name is too high! - if (gameinfo.gametype & GAME_DoomChex) - { - screen->DrawTexture(finished, midx - finished->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE); - return y + finished->GetScaledHeight() * CleanYfac; - } - else - { - screen->DrawText(font, CR_WHITE, - midx - font->StringWidth("finished")*CleanXfac/2, y - 4*CleanYfac, "finished", - DTA_CleanNoMove, true, TAG_DONE); - return y + font->GetHeight() * CleanYfac; - } + // don't draw 'finished' if the level name is too tall + y = WI_DrawPatchText(y, &finished, "WI_FINISHED"); } return y; } @@ -784,36 +827,14 @@ int WI_drawLF () // A level name patch can be specified for all games now, not just Doom. // //==================================================================== + void WI_drawEL () { int y = WI_TITLEY * CleanYfac; - FFont *font = gameinfo.gametype & GAME_Raven ? SmallFont : BigFont; - // draw "entering" - // be careful with the added height so that it works for oversized 'entering' patches! - if (gameinfo.gametype & GAME_DoomChex) - { - screen->DrawTexture(entering, (SCREENWIDTH - entering->GetScaledWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); - y += (entering->GetScaledHeight() + font->GetHeight()/4) * CleanYfac; - } - else - { - screen->DrawText(font, CR_WHITE, - (SCREENWIDTH - font->StringWidth("now entering:") * CleanXfac) / 2, y, - "now entering:", DTA_CleanNoMove, true, TAG_DONE); - y += font->GetHeight()*5*CleanYfac/4; - } - - // draw - FTexture *tex = wbs->LName1; - if (tex) - { - screen->DrawTexture(tex, (SCREENWIDTH - tex->GetScaledWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE); - } - else - { - WI_DrawName(y, lnametexts[1]); - } + y = WI_DrawPatchText(y, &entering, "WI_ENTERING"); + y += entering.mFont->GetHeight() * CleanYfac / 4; + WI_DrawName(y, wbs->LName1, lnametexts[1]); } @@ -1905,12 +1926,15 @@ void WI_Ticker(void) } } + void WI_loadData(void) { + entering.Init(gameinfo.mStatscreenEnteringFont); + finished.Init(gameinfo.mStatscreenFinishedFont); + mapname.Init(gameinfo.mStatscreenMapNameFont); + if (gameinfo.gametype & GAME_DoomChex) { - finished = TexMan["WIF"]; // "finished" - entering = TexMan["WIENTER"]; // "entering" kills = TexMan["WIOSTK"]; // "kills" secret = TexMan["WIOSTS"]; // "scrt" sp_secret = TexMan["WISCRT2"]; // "secret" diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index f3fb2bcef..1a222f1f3 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1563,6 +1563,9 @@ $ifgame(heretic) SWSTRING = "ONLY AVAILABLE IN THE REGISTERED VERSION"; MNU_EPISODE = "Select Episode"; +WI_FINISHED = "finished"; +WI_ENTERING = "Now entering:"; + // 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 79f27f5bf..acc4462b1 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -61,6 +61,9 @@ gameinfo textscreeny = 10 defaultendsequence = "Inter_Pic1" maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" + statscreen_mapnamefont = "BigFont" + statscreen_finishedpatch = "WIF" + statscreen_enteringpatch = "WIENTER" } skill baby diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 73d003795..f152aa730 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -62,6 +62,9 @@ gameinfo textscreeny = 10 defaultendsequence = "Inter_Cast" maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" + statscreen_mapnamefont = "BigFont" + statscreen_finishedpatch = "WIF" + statscreen_enteringpatch = "WIENTER" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 57aa839f4..33ab58190 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -61,6 +61,9 @@ gameinfo textscreeny = 5 defaultendsequence = "Inter_Pic1" maparrow = "maparrows/dagger.txt" + statscreen_mapnamefont = "BigFont" + statscreen_finishedfont = "SmallFont" + statscreen_enteringfont = "SmallFont" } skill baby diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index eeef3b92a..83202485b 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -59,6 +59,9 @@ gameinfo textscreeny = 5 defaultendsequence = "Inter_Chess" maparrow = "maparrows/dagger.txt" + statscreen_mapnamefont = "BigFont" + statscreen_finishedfont = "SmallFont" + statscreen_enteringfont = "SmallFont" } skill baby diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index 2e6c242cb..7791d7369 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -61,6 +61,9 @@ gameinfo textscreeny = 10 defaultendsequence = "Inter_Strife" maparrow = "maparrows/arrow.txt", "maparrows/ddtarrow.txt" + statscreen_mapnamefont = "BigFont" + statscreen_finishedfont = "BigFont", "white" + statscreen_enteringfont = "BigFont", "white" } Intermission Inter_Strife_Good From 94d132b83258bc3e65d5b0747d55f22b0c6179a5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 6 Nov 2010 21:43:08 +0000 Subject: [PATCH 144/815] - fixed: Ultimate Doom loaded the default MAPINFO for regular Doom 1. SVN r2982 (trunk) --- wadsrc/static/iwadinfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 470012579..2bf5f836a 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -216,7 +216,7 @@ IWad Autoname = "Doom1" Game = "Doom" Config = "Doom" - Mapinfo = "mapinfo/doom1.txt" + Mapinfo = "mapinfo/ultdoom.txt" Compatibility = "Shorttex" MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", From ce3b17d6272685582e9200e004d80ea02f7a14d8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 6 Nov 2010 23:45:55 +0000 Subject: [PATCH 145/815] - fixed the value set of a few dmflags options. SVN r2983 (trunk) --- wadsrc/static/menudef.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 4a577d907..5097106e3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1103,12 +1103,12 @@ OptionMenu GameplayOptions 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 "Allow health", "sv_nohealth", "NoYes" + Option "Allow armor", "sv_noarmor", "NoYes" 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 "Allow exit", "sv_noexit", "NoYes" Option "Barrels respawn", "sv_barrelrespawn", "YesNo" Option "Respawn protection", "sv_respawnprotect", "YesNo" Option "Lose frag if fragged", "sv_losefrag", "YesNo" From efe70a07995bef2d458e76beae8866f90717953f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 07:29:23 +0000 Subject: [PATCH 146/815] - added THeShooter7's patch to use the PUFFGETSOWNER flag for blood, too. SVN r2985 (trunk) --- src/m_fixed.h | 1 + src/p_mobj.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/m_fixed.h b/src/m_fixed.h index 14db59d63..61080d73d 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -139,5 +139,6 @@ inline SDWORD ModDiv (SDWORD num, SDWORD den, SDWORD *dmval) #define FLOAT2FIXED(f) xs_Fix<16>::ToFix(f) #define FIXED2FLOAT(f) ((f) / float(65536)) +#define FIXED2DBL(f) ((f) / double(65536)) #endif diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1d6e8d40a..79c20c1ed 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4526,6 +4526,8 @@ void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AAc th = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement th->velz = FRACUNIT*2; th->angle = dir; + // [NG] Applying PUFFGETSOWNER to the blood will make it target the owner + if (th->flags5 & MF5_PUFFGETSOWNER) th->target = originator; if (gameinfo.gametype & GAME_DoomChex) { th->tics -= pr_spawnblood() & 3; @@ -4667,6 +4669,8 @@ void P_RipperBlood (AActor *mo, AActor *bleeder) { AActor *th; th = Spawn (bloodcls, x, y, z, NO_REPLACE); // GetBloodType already performed the replacement + // [NG] Applying PUFFGETSOWNER to the blood will make it target the owner + if (th->flags5 & MF5_PUFFGETSOWNER) th->target = bleeder; if (gameinfo.gametype == GAME_Heretic) th->flags |= MF_NOGRAVITY; th->velx = mo->velx >> 1; From b771426ea2798ec60ec9cbc42fafa7f2e772c940 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 07:31:58 +0000 Subject: [PATCH 147/815] - added Demolisher's APROP_Waterlevel patch. SVN r2986 (trunk) --- src/p_acs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index b789f22b9..e7fff7d7a 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2536,7 +2536,8 @@ enum APROP_DamageFactor = 24, APROP_MasterTID = 25, APROP_TargetTID = 26, - APROP_TracerTID = 27 + APROP_TracerTID = 27, + APROP_WaterLevel = 28 }; // These are needed for ACS's APROP_RenderStyle @@ -2778,6 +2779,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_MasterTID: return DoGetMasterTID (actor); case APROP_TargetTID: return (actor->target != NULL)? actor->target->tid : 0; case APROP_TracerTID: return (actor->tracer != NULL)? actor->tracer->tid : 0; + case APROP_WaterLevel: return actor->waterlevel; default: return 0; } } From dff45536637ad7c5b7ebcce2498f379f27ce8500 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 14:25:08 +0000 Subject: [PATCH 148/815] - fixed: When playing non-looping songs GMESong::Read could return without releasing the critical section. SVN r2988 (trunk) --- src/sound/i_music.cpp | 2 -- src/sound/music_gme.cpp | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index ba98b470d..0eedfbdc8 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -477,8 +477,6 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { - TArray midi; - EMidiDevice devtype = (EMidiDevice)device; retry_as_fmod: diff --git a/src/sound/music_gme.cpp b/src/sound/music_gme.cpp index 08700c8b3..0c3d8c8c3 100644 --- a/src/sound/music_gme.cpp +++ b/src/sound/music_gme.cpp @@ -349,6 +349,7 @@ bool GMESong::Read(SoundStream *stream, void *buff, int len, void *userdata) else { memset(buff, 0, len); + song->CritSec.Leave(); return false; } } From 7dd8a0fce947d0a8b9cb8fd23f5a0165b3562830 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 14:39:09 +0000 Subject: [PATCH 149/815] - replaced all calls to sqrtf with sqrt. Also changed P_RadiusAttack to use doubles for all floating point calculations. SVN r2989 (trunk) --- src/am_map.cpp | 2 +- src/g_heretic/a_hereticweaps.cpp | 4 ++-- src/g_shared/a_decals.cpp | 2 +- src/p_map.cpp | 36 ++++++++++++++++---------------- src/p_setup.cpp | 6 +++--- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 2de0a5bc3..cf8876157 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -1533,7 +1533,7 @@ void AM_drawGrid (const AMColor &color) // [RH] Calculate a minimum for how long the grid lines should be so that // they cover the screen at any rotation. - minlen = (fixed_t)sqrtf ((float)m_w*(float)m_w + (float)m_h*(float)m_h); + minlen = (fixed_t)sqrt ((double)m_w*(double)m_w + (double)m_h*(double)m_h); extx = (minlen - m_w) / 2; exty = (minlen - m_h) / 2; diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index ad0115089..0a44cdbec 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -462,8 +462,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_MacePL1Check) self->velx = FixedMul(7*FRACUNIT, finecosine[angle]); self->vely = FixedMul(7*FRACUNIT, finesine[angle]); #else - double velscale = sqrtf ((float)self->velx * (float)self->velx + - (float)self->vely * (float)self->vely); + double velscale = sqrt ((double)self->velx * (double)self->velx + + (double)self->vely * (double)self->vely); velscale = 458752 / velscale; self->velx = (int)(self->velx * velscale); self->vely = (int)(self->vely * velscale); diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index d992bec9e..1c15bd914 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -414,7 +414,7 @@ static void GetWallStuff (side_t *wall, vertex_t *&v1, fixed_t &ldx, fixed_t &ld static fixed_t Length (fixed_t dx, fixed_t dy) { - return (fixed_t)sqrtf ((float)dx*(float)dx+(float)dy*(float)dy); + return (fixed_t)sqrt ((double)dx*(double)dx+(double)dy*(double)dy); } static side_t *NextWall (const side_t *wall) diff --git a/src/p_map.cpp b/src/p_map.cpp index 3c791fea6..082db59fa 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4305,8 +4305,8 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b return; fulldamagedistance = clamp(fulldamagedistance, 0, bombdistance-1); - float bombdistancefloat = 1.f / (float)(bombdistance - fulldamagedistance); - float bombdamagefloat = (float)bombdamage; + double bombdistancefloat = 1.f / (double)(bombdistance - fulldamagedistance); + double bombdamagefloat = (double)bombdamage; FVector3 bombvec(FIXED2FLOAT(bombspot->x), FIXED2FLOAT(bombspot->y), FIXED2FLOAT(bombspot->z)); @@ -4349,29 +4349,29 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b { // [RH] New code. The bounding box only covers the // height of the thing and not the height of the map. - float points; - float len; + double points; + double len; fixed_t dx, dy; - float boxradius; + double boxradius; dx = abs (thing->x - bombspot->x); dy = abs (thing->y - bombspot->y); - boxradius = float (thing->radius); + boxradius = double (thing->radius); // The damage pattern is square, not circular. - len = float (dx > dy ? dx : dy); + len = double (dx > dy ? dx : dy); if (bombspot->z < thing->z || bombspot->z >= thing->z + thing->height) { - float dz; + double dz; if (bombspot->z > thing->z) { - dz = float (bombspot->z - thing->z - thing->height); + dz = double (bombspot->z - thing->z - thing->height); } else { - dz = float (thing->z - bombspot->z); + dz = double (thing->z - bombspot->z); } if (len <= boxradius) { @@ -4380,7 +4380,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b else { len -= boxradius; - len = sqrtf (len*len + dz*dz); + len = sqrt (len*len + dz*dz); } } else @@ -4390,18 +4390,18 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b len = 0.f; } len /= FRACUNIT; - len = clamp(len - (float)fulldamagedistance, 0, len); + len = clamp(len - (double)fulldamagedistance, 0, len); points = bombdamagefloat * (1.f - len * bombdistancefloat); if (thing == bombsource) { points = points * splashfactor; } - points *= thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT)/(float)FRACUNIT; + points *= thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT)/(double)FRACUNIT; if (points > 0.f && P_CheckSight (thing, bombspot, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) { // OK to damage; target is in direct path - float velz; - float thrust; + double velz; + double thrust; int damage = (int)points; if (bombdodamage) P_DamageMobj (thing, bombspot, bombsource, damage, bombmod); @@ -4415,12 +4415,12 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b { if (bombsource == NULL || !(bombsource->flags2 & MF2_NODMGTHRUST)) { - thrust = points * 0.5f / (float)thing->Mass; + thrust = points * 0.5f / (double)thing->Mass; if (bombsource == thing) { thrust *= selfthrustscale; } - velz = (float)(thing->z + (thing->height>>1) - bombspot->z) * thrust; + velz = (double)(thing->z + (thing->height>>1) - bombspot->z) * thrust; if (bombsource != thing) { velz *= 0.5f; @@ -4460,7 +4460,7 @@ void P_RadiusAttack (AActor *bombspot, AActor *bombsource, int bombdamage, int b { // OK to damage; target is in direct path dist = clamp(dist - fulldamagedistance, 0, dist); int damage = Scale (bombdamage, bombdistance-dist, bombdistance); - damage = (int)((float)damage * splashfactor); + damage = (int)((double)damage * splashfactor); damage = Scale(damage, thing->GetClass()->Meta.GetMetaFixed(AMETA_RDFactor, FRACUNIT), FRACUNIT); if (damage > 0) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index b26d44ee5..f91542916 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1832,8 +1832,8 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) ld->frontsector = ld->sidedef[0] != NULL ? ld->sidedef[0]->sector : NULL; ld->backsector = ld->sidedef[1] != NULL ? ld->sidedef[1]->sector : NULL; - float dx = FIXED2FLOAT(ld->v2->x - ld->v1->x); - float dy = FIXED2FLOAT(ld->v2->y - ld->v1->y); + double dx = FIXED2DBL(ld->v2->x - ld->v1->x); + double dy = FIXED2DBL(ld->v2->y - ld->v1->y); int linenum = int(ld-lines); if (ld->frontsector == NULL) @@ -1842,7 +1842,7 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) } // [RH] Set some new sidedef properties - int len = (int)(sqrtf (dx*dx + dy*dy) + 0.5f); + int len = (int)(sqrt (dx*dx + dy*dy) + 0.5f); if (ld->sidedef[0] != NULL) { From 669ce7327229074912c186dab0ae07b7aaa922bf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 15:30:41 +0000 Subject: [PATCH 150/815] - made the alpha used by stacked sectors part of the visplane. This will be needed to fix the merging of stacks with the same displacement but different alpha values. SVN r2990 (trunk) --- src/r_plane.cpp | 24 ++++++++---------------- src/r_plane.h | 1 + 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index cd0b9f4a5..8fd055053 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -1034,26 +1034,19 @@ void R_DrawSinglePlane (visplane_t *pl, fixed_t alpha, bool masked) CVAR (Bool, r_skyboxes, true, 0) static int numskyboxes; -struct VisplaneAndAlpha -{ - visplane_t *Visplane; - fixed_t Alpha; -}; - void R_DrawSkyBoxes () { static TArray interestingStack; static TArray drawsegStack; static TArray visspriteStack; static TArray viewxStack, viewyStack, viewzStack; - static TArray visplaneStack; + static TArray visplaneStack; numskyboxes = 0; if (visplanes[MAXVISPLANES] == NULL) return; - VisplaneAndAlpha vaAdder = { 0 }; int savedextralight = extralight; fixed_t savedx = viewx; fixed_t savedy = viewy; @@ -1169,9 +1162,8 @@ void R_DrawSkyBoxes () viewxStack.Push (viewx); viewyStack.Push (viewy); viewzStack.Push (viewz); - vaAdder.Visplane = pl; - vaAdder.Alpha = sky->PlaneAlpha; - visplaneStack.Push (vaAdder); + pl->alpha = sky->PlaneAlpha; + visplaneStack.Push (pl); R_RenderBSPNode (nodes + numnodes - 1); R_DrawPlanes (); @@ -1200,13 +1192,13 @@ void R_DrawSkyBoxes () ds_p = firstdrawseg; vissprite_p = firstvissprite; - visplaneStack.Pop (vaAdder); - if (vaAdder.Alpha > 0) + visplaneStack.Pop (pl); + if (pl->alpha > 0) { - R_DrawSinglePlane (vaAdder.Visplane, vaAdder.Alpha, true); + R_DrawSinglePlane (pl, pl->alpha, true); } - *freehead = vaAdder.Visplane; - freehead = &vaAdder.Visplane->next; + *freehead = pl; + freehead = &pl->next; } firstvissprite = vissprites; vissprite_p = vissprites + savedvissprite_p; diff --git a/src/r_plane.h b/src/r_plane.h index 369083bad..6dfd1e00d 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -54,6 +54,7 @@ struct visplane_s float visibility; fixed_t viewx, viewy, viewz; angle_t viewangle; + fixed_t alpha; unsigned short *bottom; // [RH] bottom and top arrays are dynamically unsigned short pad; // allocated immediately after the From c31c4755faf41cc9e5cb5fff69968a0b1ceb43b0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 16:11:42 +0000 Subject: [PATCH 151/815] - add an alpha parameter to R_FindPlane. - fixed: R_FindPlane must do a full visplane comparison for stacked sectors with a non-0 alpha for the sector plane. SVN r2991 (trunk) --- src/r_bsp.cpp | 2 ++ src/r_plane.cpp | 18 +++++++++++++++++- src/r_plane.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index e7ba7c116..b2060fd7e 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -1197,6 +1197,7 @@ void R_Subsector (subsector_t *sub) R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 frontsector->GetTexture(sector_t::ceiling), ceilinglightlevel + r_actualextralight, // killough 4/11/98 + FRACUNIT, frontsector->GetXOffset(sector_t::ceiling), // killough 3/7/98 frontsector->GetYOffset(sector_t::ceiling), // killough 3/7/98 frontsector->GetXScale(sector_t::ceiling), @@ -1221,6 +1222,7 @@ void R_Subsector (subsector_t *sub) R_FindPlane(frontsector->floorplane, frontsector->GetTexture(sector_t::floor), floorlightlevel + r_actualextralight, // killough 3/16/98 + FRACUNIT, frontsector->GetXOffset(sector_t::floor), // killough 3/7/98 frontsector->GetYOffset(sector_t::floor), // killough 3/7/98 frontsector->GetXScale(sector_t::floor), diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 8fd055053..a5a40bc12 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -522,7 +522,7 @@ static visplane_t *new_visplane (unsigned hash) // killough 2/28/98: Add offsets //========================================================================== -visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightlevel, +visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightlevel, fixed_t alpha, fixed_t xoffs, fixed_t yoffs, fixed_t xscale, fixed_t yscale, angle_t angle, int sky, ASkyViewpoint *skybox) @@ -540,6 +540,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl xscale = 0; yscale = 0; angle = 0; + alpha = 0; plane.a = plane.b = plane.d = 0; // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't // always necessary, but it is needed if a floor and ceiling sky are in the @@ -560,6 +561,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl plane = height; isskybox = false; sky = 0; // not skyflatnum so it can't be a sky + alpha = FRACUNIT; } // New visplane algorithm uses hash table -- killough @@ -579,6 +581,19 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl check->viewx == stacked_viewx && check->viewy == stacked_viewy && check->viewz == stacked_viewz && + check->alpha == alpha && + (alpha == 0 || // if alpha is > 0 everything needs to be checked + (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs && + basecolormap == check->colormap && // [RH] Add more checks + xscale == check->xscale && + yscale == check->yscale && + angle == check->angle + ) + ) && check->viewangle == stacked_angle) { return check; @@ -628,6 +643,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl check->viewy = stacked_viewy; check->viewz = stacked_viewz; check->viewangle = stacked_angle; + check->alpha = alpha; clearbufshort (check->top, viewwidth, 0x7fff); diff --git a/src/r_plane.h b/src/r_plane.h index 6dfd1e00d..2df6b1fda 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -93,6 +93,7 @@ visplane_t *R_FindPlane ( const secplane_t &height, FTextureID picnum, int lightlevel, + fixed_t alpha, fixed_t xoffs, // killough 2/28/98: add x-y offsets fixed_t yoffs, fixed_t xscale, From 492bead36030db9ffca242bc3e34af2118067dd9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 16:17:14 +0000 Subject: [PATCH 152/815] - add alpha property to sector_t::splane. Not used yet. SVN r2992 (trunk) --- src/p_saveg.cpp | 8 ++++++++ src/p_setup.cpp | 3 ++- src/r_bsp.cpp | 4 ++-- src/r_defs.h | 11 +++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 7381aaf31..c7ad7125c 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -445,6 +445,14 @@ FArchive &operator<< (FArchive &arc, sector_t::splane &p) arc << p.xform.xoffs << p.xform.yoffs << p.xform.xscale << p.xform.yscale << p.xform.angle << p.xform.base_yoffs << p.xform.base_angle << p.Flags << p.Light << p.Texture << p.TexZ; + if (SaveVersion >= 2992) + { + arc << p.alpha; + } + else + { + p.alpha = FRACUNIT; + } return arc; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f91542916..905f68c4c 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1420,7 +1420,8 @@ void P_LoadSectors (MapData * map) ss->nextsec = -1; //jff 2/26/98 add fields to support locking out ss->prevsec = -1; // stair retriggering until build completes - // killough 3/7/98: + ss->SetAlpha(sector_t::ceiling, FRACUNIT); + ss->SetAlpha(sector_t::ceiling, FRACUNIT); ss->SetXScale(sector_t::floor, FRACUNIT); // [RH] floor and ceiling scaling ss->SetYScale(sector_t::floor, FRACUNIT); ss->SetXScale(sector_t::ceiling, FRACUNIT); diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index b2060fd7e..e26b92ca8 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -1197,7 +1197,7 @@ void R_Subsector (subsector_t *sub) R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 frontsector->GetTexture(sector_t::ceiling), ceilinglightlevel + r_actualextralight, // killough 4/11/98 - FRACUNIT, + frontsector->GetAlpha(sector_t::ceiling), frontsector->GetXOffset(sector_t::ceiling), // killough 3/7/98 frontsector->GetYOffset(sector_t::ceiling), // killough 3/7/98 frontsector->GetXScale(sector_t::ceiling), @@ -1222,7 +1222,7 @@ void R_Subsector (subsector_t *sub) R_FindPlane(frontsector->floorplane, frontsector->GetTexture(sector_t::floor), floorlightlevel + r_actualextralight, // killough 3/16/98 - FRACUNIT, + frontsector->GetAlpha(sector_t::floor), frontsector->GetXOffset(sector_t::floor), // killough 3/7/98 frontsector->GetYOffset(sector_t::floor), // killough 3/7/98 frontsector->GetXScale(sector_t::floor), diff --git a/src/r_defs.h b/src/r_defs.h index 9e0477ac2..07a716d97 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -457,6 +457,7 @@ struct sector_t FTransform xform; int Flags; int Light; + fixed_t alpha; FTextureID Texture; fixed_t TexZ; }; @@ -544,6 +545,16 @@ struct sector_t planes[pos].xform.base_angle = o; } + void SetAlpha(int pos, fixed_t o) + { + planes[pos].alpha = o; + } + + fixed_t GetAlpha(int pos) const + { + return planes[pos].alpha; + } + int GetFlags(int pos) const { return planes[pos].Flags; From ef133dc68270b77f9fd9b611ec794fab991780fb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 17:05:21 +0000 Subject: [PATCH 153/815] - changed skybox code to use the alpha from the sector plane. - removed alpha from skybox viewpoints. SVN r2993 (trunk) --- src/g_shared/a_sharedglobal.h | 1 - src/g_shared/a_skies.cpp | 7 ++++++- src/p_spec.cpp | 5 +++-- src/r_plane.cpp | 3 ++- src/r_segs.cpp | 2 ++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index c152bf44d..849321451 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -97,7 +97,6 @@ public: bool bInSkybox; bool bAlways; TObjPtr Mate; - fixed_t PlaneAlpha; }; class AStackPoint : public ASkyViewpoint diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 0454498e2..21edd2ba1 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -69,7 +69,12 @@ void ASkyViewpoint::BeginPlay () void ASkyViewpoint::Serialize (FArchive &arc) { Super::Serialize (arc); - arc << bInSkybox << bAlways << Mate << PlaneAlpha; + arc << bInSkybox << bAlways << Mate; + if (SaveVersion < 2992) + { + fixed_t eatme; + arc << eatme; + } } void ASkyViewpoint::Destroy () diff --git a/src/p_spec.cpp b/src/p_spec.cpp index d48ef90d6..79242f52c 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -870,7 +870,7 @@ static void SetupFloorPortal (AStackPoint *point) if (Sector->FloorSkyBox != NULL) { Sector->FloorSkyBox->Mate = point; - Sector->FloorSkyBox->PlaneAlpha = Scale (point->args[0], OPAQUE, 255); + Sector->SetAlpha(sector_t::floor, Scale (point->args[0], OPAQUE, 255)); } } @@ -882,7 +882,7 @@ static void SetupCeilingPortal (AStackPoint *point) if (Sector->CeilingSkyBox != NULL) { Sector->CeilingSkyBox->Mate = point; - Sector->CeilingSkyBox->PlaneAlpha = Scale (point->args[0], OPAQUE, 255); + Sector->SetAlpha(sector_t::ceiling, Scale (point->args[0], OPAQUE, 255)); } } @@ -919,6 +919,7 @@ void P_SetupPortals() fixed_t deltay1 = points[i]->Mate->y - points[i]->y; fixed_t deltax2 = points[j]->Mate->x - points[j]->x; fixed_t deltay2 = points[j]->Mate->y - points[j]->y; + if (deltax1 == deltax2 && deltay1 == deltay2) { if (points[j]->Sector->FloorSkyBox == points[j]->Mate) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index a5a40bc12..abf813fd7 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -561,6 +561,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl plane = height; isskybox = false; sky = 0; // not skyflatnum so it can't be a sky + skybox = NULL; alpha = FRACUNIT; } @@ -728,6 +729,7 @@ visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop) new_pl->viewz = pl->viewz; new_pl->viewangle = pl->viewangle; new_pl->sky = pl->sky; + new_pl->alpha = pl->alpha; pl = new_pl; pl->minx = start; pl->maxx = stop; @@ -1178,7 +1180,6 @@ void R_DrawSkyBoxes () viewxStack.Push (viewx); viewyStack.Push (viewy); viewzStack.Push (viewz); - pl->alpha = sky->PlaneAlpha; visplaneStack.Push (pl); R_RenderBSPNode (nodes + numnodes - 1); diff --git a/src/r_segs.cpp b/src/r_segs.cpp index eed5d78a8..55f80649e 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -1295,6 +1295,7 @@ void R_NewWall (bool needlights) // killough 3/7/98: Add checks for (x,y) offsets || backsector->GetXOffset(sector_t::floor) != frontsector->GetXOffset(sector_t::floor) || backsector->GetYOffset(sector_t::floor) != frontsector->GetYOffset(sector_t::floor) + || backsector->GetAlpha(sector_t::floor) != frontsector->GetAlpha(sector_t::floor) // killough 4/15/98: prevent 2s normals // from bleeding through deep water @@ -1326,6 +1327,7 @@ void R_NewWall (bool needlights) // killough 3/7/98: Add checks for (x,y) offsets || backsector->GetXOffset(sector_t::ceiling) != frontsector->GetXOffset(sector_t::ceiling) || backsector->GetYOffset(sector_t::ceiling) != frontsector->GetYOffset(sector_t::ceiling) + || backsector->GetAlpha(sector_t::ceiling) != frontsector->GetAlpha(sector_t::ceiling) // killough 4/15/98: prevent 2s normals // from bleeding through fake ceilings From 12d21247008b2f748ece605a09f62a1377bf2485 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 20:18:52 +0000 Subject: [PATCH 154/815] - fixed: Explosions may not spawn splashes in sectors with a Transfer_Heights effect. Due to the way it handles floor textures it cannot spawn them correctly there. SVN r2994 (trunk) --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 79c20c1ed..adfbeb0ef 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4913,7 +4913,7 @@ bool P_HitFloor (AActor *thing) void P_CheckSplash(AActor *self, fixed_t distance) { - if (self->z <= self->floorz + (distance<floorsector == self->Sector) + if (self->z <= self->floorz + (distance<floorsector == self->Sector && self->Sector->GetHeightSec() == NULL) { // Explosion splashes never alert monsters. This is because A_Explode has // a separate parameter for that so this would get in the way of proper From 92783750647a5149473b33837ab35af472848ac5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 22:12:38 +0000 Subject: [PATCH 155/815] - added 'flooralpha' and 'ceilingalpha' sector properties. They only have meaning if a sector stack portal is defined in this sector. If set to anything less than 1.0 these will override the alpha set by a portal. This is mostly for Sector_SetPortal to avoid defining multiple portals that only differ by their plane translucency. SVN r2995 (trunk) --- specs/udmf_zdoom.txt | 2 ++ src/namedef.h | 2 ++ src/p_spec.cpp | 6 ++++-- src/p_udmf.cpp | 10 ++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index fef3fa996..1a4dbeffa 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -153,6 +153,8 @@ Note: All fields default to false unless mentioned otherwise. // relative to the owning sector's light level. lightceilingabsolute = ; // true = 'lightceiling' is an absolute value. Default is // relative to the owning sector's light level. + alphafloor = ; // translucency of floor plane (only has meaning with Sector_SetPortal) Default is 1.0. + alphaceiling = ; // translucency of ceiling plane (only has meaning with Sector_SetPortal) Default is 1.0. gravity = ; // Sector's gravity. Default is 1.0. lightcolor = ; // Sector'S light color as RRGGBB value, default = 0xffffff. fadecolor = ; // Sector'S fog color as RRGGBB value, default = 0x000000. diff --git a/src/namedef.h b/src/namedef.h index 4a00c99c9..684d447f6 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -419,6 +419,8 @@ xx(Silent) xx(Nofallingdamage) xx(Dropactors) xx(NoRespawn) +xx(Alphafloor) +xx(Alphaceiling) xx(offsetx_top) xx(offsety_top) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 79242f52c..631662641 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -870,7 +870,8 @@ static void SetupFloorPortal (AStackPoint *point) if (Sector->FloorSkyBox != NULL) { Sector->FloorSkyBox->Mate = point; - Sector->SetAlpha(sector_t::floor, Scale (point->args[0], OPAQUE, 255)); + if (Sector->GetAlpha(sector_t::floor) == OPAQUE) + Sector->SetAlpha(sector_t::floor, Scale (point->args[0], OPAQUE, 255)); } } @@ -882,7 +883,8 @@ static void SetupCeilingPortal (AStackPoint *point) if (Sector->CeilingSkyBox != NULL) { Sector->CeilingSkyBox->Mate = point; - Sector->SetAlpha(sector_t::ceiling, Scale (point->args[0], OPAQUE, 255)); + if (Sector->GetAlpha(sector_t::ceiling) == OPAQUE) + Sector->SetAlpha(sector_t::ceiling, Scale (point->args[0], OPAQUE, 255)); } } diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 8ae9403b2..0fba825b0 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1074,6 +1074,8 @@ public: sec->SetYScale(sector_t::floor, FRACUNIT); sec->SetXScale(sector_t::ceiling, FRACUNIT); sec->SetYScale(sector_t::ceiling, FRACUNIT); + sec->SetAlpha(sector_t::floor, FRACUNIT); + sec->SetAlpha(sector_t::ceiling, FRACUNIT); sec->thinglist = NULL; sec->touching_thinglist = NULL; // phares 3/14/98 sec->seqType = (level.flags & LEVEL_SNDSEQTOTALCTRL) ? 0 : -1; @@ -1185,6 +1187,14 @@ public: sec->SetPlaneLight(sector_t::ceiling, CheckInt(key)); continue; + case NAME_Alphafloor: + sec->SetAlpha(sector_t::floor, CheckFixed(key)); + continue; + + case NAME_Alphaceiling: + sec->SetAlpha(sector_t::ceiling, CheckFixed(key)); + continue; + case NAME_Lightfloorabsolute: if (CheckBool(key)) sec->ChangeFlags(sector_t::floor, 0, PLANEF_ABSLIGHTING); else sec->ChangeFlags(sector_t::floor, PLANEF_ABSLIGHTING, 0); From ad18c7396eda3ca245559e7676ac542b32c67a0b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 23:37:03 +0000 Subject: [PATCH 156/815] - fixed: Sector_SetPortal did not set the portal's alpha value. SVN r2996 (trunk) --- src/p_spec.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 631662641..0ba2f01d8 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -938,16 +938,23 @@ void P_SetupPortals() } } -inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal) +inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t alpha) { // plane: 0=floor, 1=ceiling, 2=both if (plane > 0) { - if (sector->CeilingSkyBox == NULL) sector->CeilingSkyBox = portal; + if (sector->CeilingSkyBox == NULL) + { + sector->CeilingSkyBox = portal; + if (sector->GetAlpha(sector_t::ceiling) == OPAQUE) + sector->SetAlpha(sector_t::ceiling, alpha); + } } if (plane == 2 || plane == 0) { if (sector->FloorSkyBox == NULL) sector->FloorSkyBox = portal; + if (sector->GetAlpha(sector_t::floor) == OPAQUE) + sector->SetAlpha(sector_t::floor, alpha); } } @@ -967,6 +974,7 @@ void P_SpawnPortal(line_t *line, int sectortag, int plane, int alpha) fixed_t y1 = (line->v1->y + line->v2->y) >> 1; fixed_t x2 = (lines[i].v1->x + lines[i].v2->x) >> 1; fixed_t y2 = (lines[i].v1->y + lines[i].v2->y) >> 1; + fixed_t alpha = Scale (lines[i].args[4], OPAQUE, 255); AStackPoint *anchor = Spawn(x1, y1, 0, NO_REPLACE); AStackPoint *reference = Spawn(x2, y2, 0, NO_REPLACE); @@ -981,7 +989,7 @@ void P_SpawnPortal(line_t *line, int sectortag, int plane, int alpha) for (int s=-1; (s = P_FindSectorFromTag(sectortag,s)) >= 0;) { - SetPortal(§ors[s], plane, reference); + SetPortal(§ors[s], plane, reference, alpha); } for (int j=0;j= 0;) { - SetPortal(§ors[s], plane, reference); + SetPortal(§ors[s], plane, reference, alpha); } } } From 332d9b9ad127f18bf9c8515bc52905101a4ee691 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 23:48:55 +0000 Subject: [PATCH 157/815] - Changed savegame versioning for SVN-less builds to use a SAVEVER value of 999999 so that it is guaranteed that such a build can load its own savegames - even if it is at the cost of losing the ability to handle older savegames. People should build ZDoom with revision info anyway. ;) SVN r2997 (trunk) --- src/version.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index 5854c5f01..ee742c1c8 100644 --- a/src/version.h +++ b/src/version.h @@ -78,8 +78,9 @@ #define MINSAVEVER 1848 #if SVN_REVISION_NUMBER < MINSAVEVER -// Never write a savegame with a version lower than what we need -#define SAVEVER MINSAVEVER +// If we don't know the current revision write something very high to ensure that +// the reesulting executable can read its own savegames but no regular engine can. +#define SAVEVER 9999 #define SAVESIG MakeSaveSig() static inline const char *MakeSaveSig() { From 2f06007ad4909dd8564dbc6f8116291b3b62e3e7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Nov 2010 23:50:21 +0000 Subject: [PATCH 158/815] - added Edward-san's fix for the turbo CCMD. SVN r2998 (trunk) --- src/g_game.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 1153a9e7c..19acf8c57 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -240,9 +240,9 @@ CUSTOM_CVAR (Float, turbo, 100.f, 0) { self = 10.f; } - else if (self > 256.f) + else if (self > 255.f) { - self = 256.f; + self = 255.f; } else { From 1f43f4e961642a37fc324e6aa9dd016222c5496d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Nov 2010 00:01:21 +0000 Subject: [PATCH 159/815] - Added DavidPH's AProp_ScaleX/Y / A_SetScale submission. SVN r3000 (trunk) --- src/p_acs.cpp | 18 ++++++++++++++++-- src/thingdef/thingdef_codeptr.cpp | 18 ++++++++++++++++++ src/thingdef/thingdef_expression.cpp | 3 +++ src/version.h | 2 +- wadsrc/static/actors/actor.txt | 3 +++ 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e7fff7d7a..34275da14 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -312,6 +312,7 @@ static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count { SDWORD key, val; key = arc.ReadCount(); + val = arc.ReadCount(); vars[i].Insert (key, val); } @@ -2537,8 +2538,10 @@ enum APROP_MasterTID = 25, APROP_TargetTID = 26, APROP_TracerTID = 27, - APROP_WaterLevel = 28 -}; + APROP_WaterLevel = 28, + APROP_ScaleX = 29, + APROP_ScaleY = 30, +}; // These are needed for ACS's APROP_RenderStyle static const int LegacyRenderStyleIndices[] = @@ -2718,6 +2721,14 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) DoSetMaster (actor, other); break; + case APROP_ScaleX: + actor->scaleX = value; + break; + + case APROP_ScaleY: + actor->scaleY = value; + break; + default: // do nothing. break; @@ -2780,6 +2791,9 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_TargetTID: return (actor->target != NULL)? actor->target->tid : 0; case APROP_TracerTID: return (actor->tracer != NULL)? actor->tracer->tid : 0; case APROP_WaterLevel: return actor->waterlevel; + case APROP_ScaleX: return actor->scaleX; + case APROP_ScaleY: return actor->scaleY; + default: return 0; } } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 1ac84d38a..a40c49467 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2020,6 +2020,23 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) } } +//=========================================================================== +// +// A_Scale(float scalex, optional float scaley) +// +// Scales the actor's graphics. If scaley is 0, use scalex. +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetScale) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(scalex, 0); + ACTION_PARAM_FIXED(scaley, 1); + + self->scaleX = scalex; + self->scaleY = scaley ? scaley : scalex; +} + //=========================================================================== // // A_SpawnDebris @@ -2632,6 +2649,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) { + return; // [KS] Outside of FOV - return } diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 9440a05b0..1315d70ee 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -77,6 +77,8 @@ DEFINE_MEMBER_VARIABLE(velz, AActor) DEFINE_MEMBER_VARIABLE_ALIAS(momx, velx, AActor) DEFINE_MEMBER_VARIABLE_ALIAS(momy, vely, AActor) DEFINE_MEMBER_VARIABLE_ALIAS(momz, velz, AActor) +DEFINE_MEMBER_VARIABLE(scaleX, AActor) +DEFINE_MEMBER_VARIABLE(scaleY, AActor) DEFINE_MEMBER_VARIABLE(Damage, AActor) DEFINE_MEMBER_VARIABLE(Score, AActor) @@ -675,6 +677,7 @@ FxExpression *FxUnaryNotBoolean::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); if (Operand) + { Operand = Operand->ResolveAsBoolean(ctx); } diff --git a/src/version.h b/src/version.h index ee742c1c8..6a99a02ad 100644 --- a/src/version.h +++ b/src/version.h @@ -80,7 +80,7 @@ #if SVN_REVISION_NUMBER < MINSAVEVER // If we don't know the current revision write something very high to ensure that // the reesulting executable can read its own savegames but no regular engine can. -#define SAVEVER 9999 +#define SAVEVER 999999 #define SAVESIG MakeSaveSig() static inline const char *MakeSaveSig() { diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index fb0655524..f8dd3d863 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -47,6 +47,8 @@ ACTOR Actor native //: Thinker native fixed_t momx; // alias for velx native fixed_t momy; // alias for vely native fixed_t momz; // alias for velz + native fixed_t scaleX; + native fixed_t scaleY; native int score; // Meh, MBF redundant functions. Only for DeHackEd support. @@ -204,6 +206,7 @@ ACTOR Actor native //: Thinker action native A_FadeIn(float reduce = 0.1); action native A_FadeOut(float reduce = 0.1, bool remove = true); action native A_FadeTo(float target, float amount = 0.1, bool remove = false); + action native A_SetScale(float scalex, float scaley = 0); action native A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); action native A_CheckSight(state label); action native A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false); From aa2e14b27f6ffa1de24aed0b5e59de99b496ba35 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Nov 2010 06:57:28 +0000 Subject: [PATCH 160/815] - fixed typo in Firemace obituary. SVN r3001 (trunk) --- wadsrc/static/language.enu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 1a222f1f3..3acfe4047 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -737,7 +737,7 @@ OB_MPCROSSBOW = "%o was pegged by %k's ethereal crossbow."; OB_MPBLASTER = "%o was blasted a new one by %k's dragon claw."; OB_MPSKULLROD = "%o got sent down under by %k's hellstaff."; OB_MPPHOENIXROD = "%o was scorched to cinders by %k's phoenix rod."; -OB_MPMACE = "%o was bounced by $k's firemace."; +OB_MPMACE = "%o was bounced by %k's firemace."; OB_MPPSTAFF = "%o got clapped by %k's charged staff."; OB_MPPGAUNTLETS = "%o was bled dry by %k's gauntlets."; From 2fcf1af21b5b263e0fe6885ff7d85b06926d65b8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Nov 2010 17:24:27 +0000 Subject: [PATCH 161/815] - added a $musicalias command to SNDINFO that allows remapping of music tracks. Mapping to 'none' means that starting the remapped song will have no effect at all. There's one limitation though: If you load a WAD with the same music name after the one with the SNDINFO lump the mapping will be ignored. This is so that music resources can use this command without interfering with WADs that replace the music with their own. SVN r3002 (trunk) --- src/s_advsound.cpp | 30 ++++++++++++++++++++++++++++++ src/s_sound.cpp | 10 +++++++++- src/s_sound.h | 2 ++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 240498f0c..c194d7448 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -149,6 +149,7 @@ enum SICommands SI_IfStrife, SI_Rolloff, SI_Volume, + SI_MusicAlias, }; // Blood was a cool game. If Monolith ever releases the source for it, @@ -183,6 +184,7 @@ struct FSavedPlayerSoundInfo }; // This specifies whether Timidity or Windows playback is preferred for a certain song (only useful for Windows.) +MusicAliasMap MusicAliases; MidiDeviceMap MidiDevices; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- @@ -241,6 +243,7 @@ static const char *SICommandStrings[] = "$ifstrife", "$rolloff", "$volume", + "$musicalias", NULL }; @@ -254,6 +257,7 @@ static bool PlayerClassesIsSorted; static TArray PlayerClassLookups; static TArray PlayerSounds; + static FString DefPlayerClassName; static int DefPlayerClass; @@ -1285,6 +1289,32 @@ static void S_AddSNDINFO (int lump) } break; + case SI_MusicAlias: { + sc.MustGetString(); + int lump = Wads.CheckNumForName(sc.String, ns_music); + if (lump >= 0) + { + // do not set the alias if a later WAD defines its own music of this name + int file = Wads.GetLumpFile(lump); + int sndifile = Wads.GetLumpFile(sc.LumpNum); + if (file > sndifile) + { + sc.MustGetString(); + continue; + } + } + FName alias = sc.String; + sc.MustGetString(); + FName mapped = sc.String; + + // only set the alias if the lump it maps to exists. + if (mapped == NAME_None || Wads.CheckNumForName(sc.String, ns_music) >= 0) + { + MusicAliases[alias] = mapped; + } + } + break; + case SI_MidiDevice: { sc.MustGetString(); FName nm = sc.String; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 95130d309..bb85541c4 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2386,8 +2386,16 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) int offset = 0, length = 0; int device = MDEV_DEFAULT; MusInfo *handle = NULL; + FName musicasname = musicname; - int *devp = MidiDevices.CheckKey(FName(musicname)); + FName *aliasp = MusicAliases.CheckKey(musicasname); + if (aliasp != NULL) + { + musicname = (musicasname = *aliasp).GetChars(); + if (musicasname == NAME_None) return true; + } + + int *devp = MidiDevices.CheckKey(musicasname); if (devp != NULL) device = *devp; // Strip off any leading file:// component. diff --git a/src/s_sound.h b/src/s_sound.h index 5c2796d5b..ee831bddf 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -382,8 +382,10 @@ enum EMidiDevice MDEV_GUS = 5, }; +typedef TMap MusicAliasMap; typedef TMap MidiDeviceMap; +extern MusicAliasMap MusicAliases; extern MidiDeviceMap MidiDevices; #endif From 5e7753d7e801c243d152cf5ba600045e2615f379 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 8 Nov 2010 20:18:06 +0000 Subject: [PATCH 162/815] - fixed: The floor plane's alpha was not initialized. SVN r3003 (trunk) --- src/p_setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 905f68c4c..549028cdb 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1420,7 +1420,7 @@ void P_LoadSectors (MapData * map) ss->nextsec = -1; //jff 2/26/98 add fields to support locking out ss->prevsec = -1; // stair retriggering until build completes - ss->SetAlpha(sector_t::ceiling, FRACUNIT); + ss->SetAlpha(sector_t::floor, FRACUNIT); ss->SetAlpha(sector_t::ceiling, FRACUNIT); ss->SetXScale(sector_t::floor, FRACUNIT); // [RH] floor and ceiling scaling ss->SetYScale(sector_t::floor, FRACUNIT); From 340ffc08d6a7a7dd859288744e65c47841deb4c8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 10 Nov 2010 11:25:34 +0000 Subject: [PATCH 163/815] - fixed: Boom's switch-based equivalents of FloorandCeiling_LowerRaise can only move either the ceiling or the floor but never both due to a programming error. Changed this special so that Boom's broken mode can be reactivated through xlat. SVN r3004 (trunk) --- src/p_lnspec.cpp | 14 +++++++++++--- wadsrc/static/xlat/base.txt | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index a7f799c8b..ac7520170 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1683,10 +1683,18 @@ FUNC(LS_FloorAndCeiling_RaiseByValue) } FUNC(LS_FloorAndCeiling_LowerRaise) -// FloorAndCeiling_LowerRaise (tag, fspeed, cspeed) +// FloorAndCeiling_LowerRaise (tag, fspeed, cspeed, boomemu) { - return EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0, false) | - EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0, false); + // The switch based Boom equivalents of FloorandCeiling_LowerRaise do incorrect checks + // which cause the floor only to move when the ceiling fails to do so. + // To avoid problems with maps that have incorrect args this only uses a + // more or less unintuitive value for the fourth arg to trigger Boom's broken behavior + if (arg3 != 1998 || !res) // (1998 for the year in which Boom was released... :P) + { + res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, 0, false); + } + return res; } FUNC(LS_Elevator_MoveToFloor) diff --git a/wadsrc/static/xlat/base.txt b/wadsrc/static/xlat/base.txt index 45b23e52f..c4e0cc083 100644 --- a/wadsrc/static/xlat/base.txt +++ b/wadsrc/static/xlat/base.txt @@ -168,7 +168,7 @@ include "xlat/defines.i" 163 = USE, Plat_Stop (tag) 164 = USE, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) 165 = USE, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) -166 = USE, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW) +166 = USE, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW, 1998) 167 = USE, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 168 = USE, Ceiling_CrushStop (tag) 169 = USE, Light_MaxNeighbor (tag) @@ -188,7 +188,7 @@ include "xlat/defines.i" 183 = USE|REP, Ceiling_CrushAndRaiseA (tag, C_NORMAL, C_NORMAL, 10) 184 = USE|REP, Ceiling_CrushAndRaiseA (tag, C_SLOW, C_SLOW, 10) 185 = USE|REP, Ceiling_CrushAndRaiseSilentA (tag, C_SLOW, C_SLOW, 10) -186 = USE|REP, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW) +186 = USE|REP, FloorAndCeiling_LowerRaise (tag, F_SLOW, C_SLOW, 1998) 187 = USE|REP, Ceiling_LowerAndCrush (tag, C_SLOW, 0, 2) 188 = USE|REP, Ceiling_CrushStop (tag) 189 = USE, Floor_TransferTrigger (tag) From a00730c160a916ec2a3ab4e2857fa2078cf73449 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 10 Nov 2010 23:39:34 +0000 Subject: [PATCH 164/815] - Fixed: Cocoa IWAD picker was not updated. Also changed instances of the deprecated stringWithCString to stringWithUTF8String. - Fixed: Mac OS X should be case insensitive like Windows. SVN r3005 (trunk) --- src/d_iwad.cpp | 2 +- src/sdl/iwadpicker_cocoa.mm | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index d03507bf0..6fd42a629 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -227,7 +227,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) { sc.MustGetString(); FString wadname = sc.String; -#ifdef _WIN32 +#if defined(_WIN32) || defined(__APPLE__) // Turns out Mac OS X is case insensitive. mIWadNames.Push(wadname); #else // check for lowercase, uppercased first letter and full uppercase on Linux etc. diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index a835c6dff..4e4cdf7b6 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -48,7 +48,7 @@ enum static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; // Class to convert the IWAD data into a form that Cocoa can use. -@interface IWADTableData : NSObject +@interface IWADTableData : NSObject { NSMutableArray *data; } @@ -81,8 +81,8 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; filename = wads[i].Path; else filename++; - [record setObject:[NSString stringWithCString:filename] forKey:[NSString stringWithCString:tableHeaders[COLUMN_IWAD]]]; - [record setObject:[NSString stringWithCString:IWADInfos[wads[i].Type].Name] forKey:[NSString stringWithCString:tableHeaders[COLUMN_GAME]]]; + [record setObject:[NSString stringWithUTF8String:filename] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_IWAD]]]; + [record setObject:[NSString stringWithUTF8String:wads[i].Name] forKey:[NSString stringWithUTF8String:tableHeaders[COLUMN_GAME]]]; [data addObject:record]; [record release]; } @@ -144,7 +144,7 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; // little more automated. - (void)makeLabel:(NSTextField *)label:(const char*) str { - [label setStringValue:[NSString stringWithCString:str]]; + [label setStringValue:[NSString stringWithUTF8String:str]]; [label setBezeled:NO]; [label setDrawsBackground:NO]; [label setEditable:NO]; @@ -156,7 +156,7 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; cancelled = false; app = [NSApplication sharedApplication]; - id windowTitle = [NSString stringWithCString:GAMESIG " " DOTVERSIONSTR ": Select an IWAD to use"]; + id windowTitle = [NSString stringWithUTF8String:GAMESIG " " DOTVERSIONSTR ": Select an IWAD to use"]; NSRect frame = NSMakeRect(0, 0, 440, 450); window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]; @@ -174,7 +174,7 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; IWADTableData *tableData = [[IWADTableData alloc] init:wads:numwads]; for(int i = 0;i < NUM_COLUMNS;i++) { - NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:[NSString stringWithCString:tableHeaders[i]]]; + NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:[NSString stringWithUTF8String:tableHeaders[i]]]; [[column headerCell] setStringValue:[column identifier]]; if(i == 0) [column setMaxWidth:110]; @@ -211,7 +211,7 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [[window contentView] addSubview:dontAsk];*/ okButton = [[NSButton alloc] initWithFrame:NSMakeRect(236, 12, 96, 32)]; - [okButton setTitle:[NSString stringWithCString:"OK"]]; + [okButton setTitle:[NSString stringWithUTF8String:"OK"]]; [okButton setBezelStyle:NSRoundedBezelStyle]; [okButton setAction:@selector(buttonPressed:)]; [okButton setTarget:self]; @@ -219,7 +219,7 @@ static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; [[window contentView] addSubview:okButton]; cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(332, 12, 96, 32)]; - [cancelButton setTitle:[NSString stringWithCString:"Cancel"]]; + [cancelButton setTitle:[NSString stringWithUTF8String:"Cancel"]]; [cancelButton setBezelStyle:NSRoundedBezelStyle]; [cancelButton setAction:@selector(buttonPressed:)]; [cancelButton setTarget:self]; From ae6ad394d1c99725c4a414b20a88d3e5ac5b04fe Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Wed, 10 Nov 2010 23:46:18 +0000 Subject: [PATCH 165/815] - Fixed: Hexen's frag counter was not placed correctly. SVN r3006 (trunk) --- wadsrc/static/sbarinfo/hexen.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/sbarinfo/hexen.txt b/wadsrc/static/sbarinfo/hexen.txt index f1c294525..635cb650f 100755 --- a/wadsrc/static/sbarinfo/hexen.txt +++ b/wadsrc/static/sbarinfo/hexen.txt @@ -59,7 +59,7 @@ statusbar Normal gamemode deathmatch, teamgame { drawimage "KILLS", 38, 163; - drawnumber 3, HUDFONT_RAVEN, untranslated, frags, 58, 163, 1; + drawnumber 3, HUDFONT_RAVEN, untranslated, frags, 65, 176, 1; } else { From c8eb4bbc8d1556f87e824704995b7621e61a4b7a Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 11 Nov 2010 00:22:36 +0000 Subject: [PATCH 166/815] - Turns out I can't clear one protocol warning on 10.6 without breaking compatibility with 10.4/10.5. SVN r3007 (trunk) --- src/sdl/iwadpicker_cocoa.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/iwadpicker_cocoa.mm b/src/sdl/iwadpicker_cocoa.mm index 4e4cdf7b6..1a1fa8b8c 100644 --- a/src/sdl/iwadpicker_cocoa.mm +++ b/src/sdl/iwadpicker_cocoa.mm @@ -48,7 +48,7 @@ enum static const char* const tableHeaders[NUM_COLUMNS] = { "IWAD", "Game" }; // Class to convert the IWAD data into a form that Cocoa can use. -@interface IWADTableData : NSObject +@interface IWADTableData : NSObject// { NSMutableArray *data; } From c5f5bcc43297d0dc9ced63fa96b1a360886ac5b6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 12 Nov 2010 21:12:47 +0000 Subject: [PATCH 167/815] - added a hidden compatiblity option for maps that fell victim to the broken destination search code in some 2.0.9x versions SVN r3008 (trunk) --- src/compatibility.cpp | 1 + src/doomdef.h | 1 + src/p_teleport.cpp | 3 +++ wadsrc/static/compatibility.txt | 4 ++++ 4 files changed, 9 insertions(+) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index ab1c98ca7..880f88789 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -89,6 +89,7 @@ static FCompatOption Options[] = { "setslopeoverflow", 0, BCOMPATF_SETSLOPEOVERFLOW }, { "resetplayerspeed", 0, BCOMPATF_RESETPLAYERSPEED }, { "vileghosts", 0, BCOMPATF_VILEGHOSTS }, + { "ignoreteleporttags", 0, BCOMPATF_BADTELEPORTERS }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, 0 }, diff --git a/src/doomdef.h b/src/doomdef.h index 9ad019db4..cad56d642 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -341,6 +341,7 @@ enum BCOMPATF_SETSLOPEOVERFLOW = 1 << 0, // SetSlope things can overflow BCOMPATF_RESETPLAYERSPEED = 1 << 1, // Set player speed to 1.0 when changing maps BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected. + BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials }; // phares 3/20/98: diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 8fe870e82..6ba60be0e 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -228,6 +228,9 @@ static AActor *SelectTeleDest (int tid, int tag) // behavior is used instead (return the first teleport dest found in a tagged // sector). + // Compatibility hack for some maps that fell victim to a bug in the teleport code in 2.0.9x + if (ib_compatflags & BCOMPATF_BADTELEPORTERS) tag = 0; + if (tid != 0) { NActorIterator iterator (NAME_TeleportDest, tid); diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 98528363d..c3a4404aa 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -135,3 +135,7 @@ DCE862393CAAA6FF1294FB7056B53057 // UAC Ultra map07: Contains a scroller dependi setlinespecial 391 Sector_CopyScroller 99 6 0 0 0 } +1D9E43988940CCD3555724E15DD8B1AB // Happy Time Circus map01 has bad teleporters +{ + ignoreteleporttags +} From 54163bc81cdf8d866e4e551a599b14d88a1ea36e Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 19 Nov 2010 04:01:15 +0000 Subject: [PATCH 168/815] - Changed sprite-on-drawseg calculations to use untransformed coordinates. Build does it like this. I don't recall what Doom did. SVN r3009 (trunk) --- src/r_things.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/r_things.cpp b/src/r_things.cpp index 600354ced..4d40c04cd 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2231,8 +2231,8 @@ void R_DrawSprite (vissprite_t *spr) } if (neardepth > spr->depth || (fardepth > spr->depth && // Check if sprite is in front of draw seg: - DMulScale24 (spr->depth - ds->cy, ds->cdx, ds->cdy, ds->cx - spr->cx) < 0)) - + DMulScale32(spr->gy - ds->curline->v1->y, ds->curline->v2->x - ds->curline->v1->x, + ds->curline->v1->x - spr->gx, ds->curline->v2->y - ds->curline->v1->y) <= 0)) { // seg is behind sprite, so draw the mid texture if it has one if (ds->maskedtexturecol != -1 || ds->bFogBoundary) From f7fa3c8dbb4fc889574a93a5547a528f89b5311c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 19 Nov 2010 04:20:33 +0000 Subject: [PATCH 169/815] - Add a SetCursor() call to I_SetCursor() when the pointer is within the window's client area to make the pointer change instant instead of waiting until the next mouse event. SVN r3010 (trunk) --- src/r_defs.h | 2 +- src/win32/i_mouse.cpp | 2 +- src/win32/i_system.cpp | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/r_defs.h b/src/r_defs.h index 07a716d97..22745f54c 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -1049,7 +1049,7 @@ struct vissprite_t { short x1, x2; fixed_t cx; // for line side calculation - fixed_t gx, gy; // for fake floor clipping + fixed_t gx, gy; // for drawseg and fake floor clipping fixed_t gz, gzt; // global bottom / top for silhouette clipping fixed_t startfrac; // horizontal position of x1 fixed_t xscale, yscale; diff --git a/src/win32/i_mouse.cpp b/src/win32/i_mouse.cpp index 873b9271a..4ed347234 100644 --- a/src/win32/i_mouse.cpp +++ b/src/win32/i_mouse.cpp @@ -118,7 +118,6 @@ extern int BlockMouseMove; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static bool NativeMouse; static EMouseMode MouseMode = MM_None; static FMouse *(*MouseFactory[])() = { @@ -130,6 +129,7 @@ static FMouse *(*MouseFactory[])() = // PUBLIC DATA DEFINITIONS ------------------------------------------------- FMouse *Mouse; +bool NativeMouse; bool CursorState; diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index b585be71a..1da4855cc 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -131,6 +131,7 @@ extern HANDLE StdOut; extern bool FancyStdOut; extern HINSTANCE g_hInst; extern FILE *Logfile; +extern bool NativeMouse; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -1214,6 +1215,23 @@ bool I_SetCursor(FTexture *cursorpic) cursor = LoadCursor(NULL, IDC_ARROW); } SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor); + if (NativeMouse) + { + POINT pt; + RECT client; + + // If the mouse pointer is within the window's client rect, set it now. + if (GetCursorPos(&pt) && GetClientRect(Window, &client) && + ClientToScreen(Window, (LPPOINT)&client.left) && + ClientToScreen(Window, (LPPOINT)&client.right)) + { + if (pt.x >= client.left && pt.x < client.right && + pt.y >= client.top && pt.y < client.bottom) + { + SetCursor(cursor); + } + } + } return true; } From 976fe64f7997317542e73326d6e8b5227147f926 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Nov 2010 17:43:05 +0000 Subject: [PATCH 170/815] - fixed: Action Doom 2 loaded the wrong MAPINFO. SVN r3011 (trunk) --- wadsrc/static/iwadinfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 2bf5f836a..efdf836a5 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -25,7 +25,7 @@ IWad Name = "Action Doom 2: Urban Brawl" Game = "Doom" Config = "UrbanBrawl" - Mapinfo = "mapinfo/doom2.txt" + Mapinfo = "mapinfo/urbanbrawl.txt" MustContain = "MAP01", "AD2LIB" BannerColors = "a8 a8 00", "a8 00 00" } From 14f4a9e835f4f8b57f6141fb8896de69880d9167 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 29 Nov 2010 12:43:52 +0000 Subject: [PATCH 171/815] - added some fudging to make sectors that are neighboring a portal plane but share the same texture properties actual parts of the portal. This is only done when portals are defined with portal things. This was necessary to preserve an effect that depended on incomplete checks in the renderer that could not be preserved with the implementation of linedef based portals. SVN r3012 (trunk) --- src/p_spec.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 0ba2f01d8..bdd6820f4 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -888,6 +888,72 @@ static void SetupCeilingPortal (AStackPoint *point) } } +static bool SpreadCeilingPortal(AStackPoint *pt, fixed_t alpha, sector_t *sector) +{ + bool fail = false; + sector->validcount = validcount; + for(int i=0; ilinecount; i++) + { + line_t *line = sector->lines[i]; + sector_t *backsector = sector == line->frontsector? line->backsector : line->frontsector; + if (line->backsector == line->frontsector) continue; + if (backsector == NULL) { fail = true; continue; } + if (backsector->validcount == validcount) continue; + if (backsector->CeilingSkyBox == pt) continue; + + // Check if the backside would map to the same visplane + if (backsector->CeilingSkyBox != NULL) { fail = true; continue; } + if (backsector->ceilingplane != sector->ceilingplane) { fail = true; continue; } + if (backsector->lightlevel != sector->lightlevel) { fail = true; continue; } + if (backsector->GetTexture(sector_t::ceiling) != sector->GetTexture(sector_t::ceiling)) { fail = true; continue; } + if (backsector->GetXOffset(sector_t::ceiling) != sector->GetXOffset(sector_t::ceiling)) { fail = true; continue; } + if (backsector->GetYOffset(sector_t::ceiling) != sector->GetYOffset(sector_t::ceiling)) { fail = true; continue; } + if (backsector->GetXScale(sector_t::ceiling) != sector->GetXScale(sector_t::ceiling)) { fail = true; continue; } + if (backsector->GetYScale(sector_t::ceiling) != sector->GetYScale(sector_t::ceiling)) { fail = true; continue; } + if (backsector->GetAngle(sector_t::ceiling) != sector->GetAngle(sector_t::ceiling)) { fail = true; continue; } + if (SpreadCeilingPortal(pt, alpha, backsector)) { fail = true; continue; } + } + if (!fail) + { + sector->CeilingSkyBox = pt; + sector->SetAlpha(sector_t::ceiling, alpha); + } + return fail; +} + +static bool SpreadFloorPortal(AStackPoint *pt, fixed_t alpha, sector_t *sector) +{ + bool fail = false; + sector->validcount = validcount; + for(int i=0; ilinecount; i++) + { + line_t *line = sector->lines[i]; + sector_t *backsector = sector == line->frontsector? line->backsector : line->frontsector; + if (line->backsector == line->frontsector) continue; + if (backsector == NULL) { fail = true; continue; } + if (backsector->validcount == validcount) continue; + if (backsector->FloorSkyBox == pt) continue; + + // Check if the backside would map to the same visplane + if (backsector->FloorSkyBox != NULL) { fail = true; continue; } + if (backsector->floorplane != sector->ceilingplane) { fail = true; continue; } + if (backsector->lightlevel != sector->lightlevel) { fail = true; continue; } + if (backsector->GetTexture(sector_t::floor) != sector->GetTexture(sector_t::floor)) { fail = true; continue; } + if (backsector->GetXOffset(sector_t::floor) != sector->GetXOffset(sector_t::floor)) { fail = true; continue; } + if (backsector->GetYOffset(sector_t::floor) != sector->GetYOffset(sector_t::floor)) { fail = true; continue; } + if (backsector->GetXScale(sector_t::floor) != sector->GetXScale(sector_t::floor)) { fail = true; continue; } + if (backsector->GetYScale(sector_t::floor) != sector->GetYScale(sector_t::floor)) { fail = true; continue; } + if (backsector->GetAngle(sector_t::floor) != sector->GetAngle(sector_t::floor)) { fail = true; continue; } + if (SpreadFloorPortal(pt, alpha, backsector)) { fail = true; continue; } + } + if (!fail) + { + sector->FloorSkyBox = pt; + sector->SetAlpha(sector_t::floor, alpha); + } + return fail; +} + void P_SetupPortals() { TThinkerIterator it; @@ -936,6 +1002,20 @@ void P_SetupPortals() } } } + validcount++; + // Some fudging to preserve an unintended 'portal bleeding' effect caused by incomplete checks in the rendering code. + // Due to the addition of linedef-based portals this effect no longer works. + for(int i=0;ibAlways && sectors[i].validcount != validcount) + { + SpreadCeilingPortal(barrier_cast(sectors[i].CeilingSkyBox), sectors[i].GetAlpha(sector_t::ceiling), §ors[i]); + } + if (sectors[i].FloorSkyBox != NULL && sectors[i].FloorSkyBox->bAlways && sectors[i].validcount != validcount) + { + SpreadFloorPortal(barrier_cast(sectors[i].FloorSkyBox), sectors[i].GetAlpha(sector_t::floor), §ors[i]); + } + } } inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t alpha) From 5ad9e0c3b8c725fba0330d38ac765228f9f4c893 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 30 Nov 2010 08:18:11 +0000 Subject: [PATCH 172/815] - added code submission for printing secret information. - added missing TRXTCOLOR_CYAN #define. - changed bridge things to be completely immobile towards sector plane movement. This problem again reared its ugly head in 007LTSD where the oversized bridges got messed up by some opening doors and lowering lifts. Now any plane trying to move such a thing will get blocked. Moving these things by other means still works normally, of course SVN r3013 (trunk) --- src/c_cmds.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++++++++ src/p_map.cpp | 29 +++++++++--- src/v_text.h | 1 + 3 files changed, 144 insertions(+), 6 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index f3cb40958..12055ebca 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -68,6 +68,8 @@ #include "p_setup.h" #include "cmdlib.h" #include "d_net.h" +#include "v_text.h" +#include "p_lnspec.h" extern FILE *Logfile; extern bool insave; @@ -929,6 +931,7 @@ CCMD(nextsecret) // // //----------------------------------------------------------------------------- + CCMD(currentpos) { AActor *mo = players[consoleplayer].mo; @@ -936,4 +939,121 @@ 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); } +//----------------------------------------------------------------------------- +// +// Print secret info (submitted by Karl Murks) +// +//----------------------------------------------------------------------------- +static void PrintSecretString(const char *string, bool thislevel) +{ + const char *colstr = thislevel? TEXTCOLOR_YELLOW : TEXTCOLOR_CYAN; + if (string != NULL) + { + if (*string == '$') + { + if (string[1] == 'S' || string[1] == 's') + { + long secnum = strtol(string+2, (char**)&string, 10); + if (*string == ';') string++; + if (thislevel && secnum >= 0 && secnum < numsectors) + { + if (sectors[secnum].secretsector) + { + if ((sectors[secnum].special & SECRET_MASK)) colstr = TEXTCOLOR_RED; + else colstr = TEXTCOLOR_GREEN; + } + else colstr = TEXTCOLOR_ORANGE; + } + } + else if (string[1] == 'T' || string[1] == 't') + { + long tid = strtol(string+2, (char**)&string, 10); + if (*string == ';') string++; + FActorIterator it(tid); + AActor *actor; + bool foundone = false; + if (thislevel) + { + while ((actor = it.Next())) + { + if (!actor->IsKindOf(PClass::FindClass("SecretTrigger"))) continue; + foundone = true; + break; + } + } + if (foundone) colstr = TEXTCOLOR_YELLOW; + else colstr = TEXTCOLOR_RED; + } + } + FBrokenLines *brok = V_BreakLines(ConFont, screen->GetWidth()*95/100, string); + + for (int k = 0; brok[k].Width >= 0; k++) + { + Printf("%s%s\n", colstr, brok[k].Text.GetChars()); + } + V_FreeBrokenLines(brok); + } +} + +//============================================================================ +// +// Print secret hints +// +//============================================================================ + +CCMD(secret) +{ + const char *mapname = argv.argc() < 2? level.mapname : argv[1]; + bool thislevel = !stricmp(mapname, level.mapname); + bool foundsome = false; + + int lumpno=Wads.CheckNumForName("SECRETS"); + if (lumpno < 0) return; + + FWadLump lump = Wads.OpenLumpNum(lumpno); + FString maphdr; + maphdr.Format("[%s]", mapname); + + FString linebuild; + char readbuffer[1024]; + bool inlevel = false; + + while (lump.Gets(readbuffer, 1024)) + { + if (!inlevel) + { + if (readbuffer[0] == '[') + { + inlevel = !strnicmp(readbuffer, maphdr, maphdr.Len()); + if (!foundsome) + { + FString levelname; + level_info_t *info = FindLevelInfo(mapname); + levelname.Format("%s - %s\n", mapname, info->LevelName); + size_t llen = levelname.Len() - 1; + for(size_t ii=0; iiflags2 & MF2_PASSMOBJ) || - (!(intersect->flags3 & MF3_ISMONSTER) && - intersect->Mass > mymass)) - { // Can't push things more massive than ourself + (!(intersect->flags3 & MF3_ISMONSTER) && intersect->Mass > mymass) || + (intersect->flags4 & MF4_ACTLIKEBRIDGE) + ) + { + // Can't push bridges or things more massive than ourself return 2; } fixed_t oldz = intersect->z; @@ -4779,9 +4781,11 @@ int P_PushDown (AActor *thing, FChangePosition *cpos) { AActor *intersect = intersectors[firstintersect]; if (!(intersect->flags2 & MF2_PASSMOBJ) || - (!(intersect->flags3 & MF3_ISMONSTER) && - intersect->Mass > mymass)) - { // Can't push things more massive than ourself + (!(intersect->flags3 & MF3_ISMONSTER) && intersect->Mass > mymass) || + (intersect->flags4 & MF4_ACTLIKEBRIDGE) + ) + { + // Can't push bridges or things more massive than ourself return 2; } fixed_t oldz = intersect->z; @@ -4813,6 +4817,7 @@ void PIT_FloorDrop (AActor *thing, FChangePosition *cpos) P_AdjustFloorCeil (thing, cpos); if (oldfloorz == thing->floorz) return; + if (thing->flags4 & MF4_ACTLIKEBRIDGE) return; // do not move bridge things if (thing->velz == 0 && (!(thing->flags & MF_NOGRAVITY) || @@ -4856,6 +4861,11 @@ void PIT_FloorRaise (AActor *thing, FChangePosition *cpos) if (thing->z <= thing->floorz || (!(thing->flags & MF_NOGRAVITY) && (thing->flags2 & MF2_FLOATBOB))) { + if (thing->flags4 & MF4_ACTLIKEBRIDGE) + { + cpos->nofit = true; + return; // do not move bridge things + } intersectors.Clear (); fixed_t oldz = thing->z; if (!(thing->flags2 & MF2_FLOATBOB)) @@ -4898,6 +4908,11 @@ void PIT_CeilingLower (AActor *thing, FChangePosition *cpos) if (thing->z + thing->height > thing->ceilingz) { + if (thing->flags4 & MF4_ACTLIKEBRIDGE) + { + cpos->nofit = true; + return; // do not move bridge things + } intersectors.Clear (); fixed_t oldz = thing->z; if (thing->ceilingz - thing->height >= thing->floorz) @@ -4935,6 +4950,8 @@ void PIT_CeilingRaise (AActor *thing, FChangePosition *cpos) { bool isgood = P_AdjustFloorCeil (thing, cpos); + if (thing->flags4 & MF4_ACTLIKEBRIDGE) return; // do not move bridge things + // For DOOM compatibility, only move things that are inside the floor. // (or something else?) Things marked as hanging from the ceiling will // stay where they are. diff --git a/src/v_text.h b/src/v_text.h index 494ebb315..29dfa627b 100644 --- a/src/v_text.h +++ b/src/v_text.h @@ -67,6 +67,7 @@ struct FBrokenLines #define TEXTCOLOR_DARKBROWN "\034S" #define TEXTCOLOR_PURPLE "\034T" #define TEXTCOLOR_DARKGRAY "\034U" +#define TEXTCOLOR_CYAN "\034V" #define TEXTCOLOR_NORMAL "\034-" #define TEXTCOLOR_BOLD "\034+" From ce8be35000f23e150d16ec4d7640092b21d40e22 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 30 Nov 2010 09:36:36 +0000 Subject: [PATCH 173/815] - fixed: Normal skyboxes should not block spawning of a portal. SVN r3014 (trunk) --- src/p_spec.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index bdd6820f4..eeb58d0c3 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -867,7 +867,7 @@ static void SetupFloorPortal (AStackPoint *point) NActorIterator it (NAME_LowerStackLookOnly, point->tid); sector_t *Sector = point->Sector; Sector->FloorSkyBox = static_cast(it.Next()); - if (Sector->FloorSkyBox != NULL) + if (Sector->FloorSkyBox != NULL || !Sector->FloorSkyBox->bAlways) { Sector->FloorSkyBox->Mate = point; if (Sector->GetAlpha(sector_t::floor) == OPAQUE) @@ -880,7 +880,7 @@ static void SetupCeilingPortal (AStackPoint *point) NActorIterator it (NAME_UpperStackLookOnly, point->tid); sector_t *Sector = point->Sector; Sector->CeilingSkyBox = static_cast(it.Next()); - if (Sector->CeilingSkyBox != NULL) + if (Sector->CeilingSkyBox != NULL || !Sector->CeilingSkyBox->bAlways) { Sector->CeilingSkyBox->Mate = point; if (Sector->GetAlpha(sector_t::ceiling) == OPAQUE) @@ -1023,7 +1023,7 @@ inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t // plane: 0=floor, 1=ceiling, 2=both if (plane > 0) { - if (sector->CeilingSkyBox == NULL) + if (sector->CeilingSkyBox == NULL || !sector->CeilingSkyBox->bAlways) { sector->CeilingSkyBox = portal; if (sector->GetAlpha(sector_t::ceiling) == OPAQUE) @@ -1032,7 +1032,10 @@ inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t } if (plane == 2 || plane == 0) { - if (sector->FloorSkyBox == NULL) sector->FloorSkyBox = portal; + if (sector->FloorSkyBox == NULL || !sector->FloorSkyBox->bAlways) + { + sector->FloorSkyBox = portal; + } if (sector->GetAlpha(sector_t::floor) == OPAQUE) sector->SetAlpha(sector_t::floor, alpha); } From 86516ecfaced96ee0b57f61107691bd9f2fdcf87 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 1 Dec 2010 19:56:35 +0000 Subject: [PATCH 174/815] - fixed: The code that set DIntermissionController::CurrentIntermission was missing a write barrier. SVN r3015 (trunk) --- src/intermission/intermission.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 0b8afa1e4..f0d308579 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -838,6 +838,7 @@ void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE stat viewactive = false; automapactive = false; DIntermissionController::CurrentIntermission = new DIntermissionController(desc, deleteme, state); + GC::WriteBarrier(DIntermissionController::CurrentIntermission); } From d9c811fc616b23cee6895d893e38c27984a54d41 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 2 Dec 2010 02:08:54 +0000 Subject: [PATCH 175/815] - Increase MAX_SKYBOX_PLANES to 1000. SVN r3016 (trunk) --- src/r_plane.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index abf813fd7..eaa3d4b47 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -75,7 +75,7 @@ planefunction_t ceilingfunc; #define MAXVISPLANES 128 /* must be a power of 2 */ // Avoid infinite recursion with stacked sectors by limiting them. -#define MAX_SKYBOX_PLANES 100 +#define MAX_SKYBOX_PLANES 1000 // [RH] Allocate one extra for sky box planes. static visplane_t *visplanes[MAXVISPLANES+1]; // killough From 5bc4bc90bb1900e6961415b1c0330c4be3bba079 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 2 Dec 2010 13:57:05 +0000 Subject: [PATCH 176/815] - fixed: A new episode definition for a map that already had one defined did not replace the old definition. - fixed: Skipping the skill menu did not work because it checked the number of episodes, not skills. SVN r3017 (trunk) --- src/g_mapinfo.cpp | 8 +++++++- src/menu/menudef.cpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 84b6aa48f..adf0b2cd0 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1626,7 +1626,13 @@ void FMapInfoParser::ParseEpisodeInfo () } else { - FEpisode *epi = &AllEpisodes[AllEpisodes.Reserve(1)]; + // Only allocate a new entry if this doesn't replace an existing episode. + if (i >= AllEpisodes.Size()) + { + i = AllEpisodes.Reserve(1); + } + + FEpisode *epi = &AllEpisodes[i]; epi->mEpisodeMap = map; epi->mEpisodeName = name; diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index a751caf5c..79ae99a23 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -1327,7 +1327,7 @@ void M_StartupSkillMenu(FGameStartup *gs) } if (AllEpisodes[gs->Episode].mNoSkill || AllSkills.Size() == 1) { - ld->mAutoselect = firstitem + MIN(2u, AllEpisodes.Size()-1); + ld->mAutoselect = firstitem + MIN(2u, AllSkills.Size()-1); } else { From 74822572da39b8e2c1c2d5f56ec401345399c5aa Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 2 Dec 2010 15:08:44 +0000 Subject: [PATCH 177/815] - restore old portal checks for Action Doom 2 (as a hidden compatibility option.) SVN r3018 (trunk) --- src/compatibility.cpp | 8 +++++++- src/d_iwad.cpp | 1 + src/doomdef.h | 1 + src/p_spec.cpp | 3 +++ src/r_plane.cpp | 33 ++++++++++++++++++++------------- wadsrc/static/iwadinfo.txt | 1 + 6 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 880f88789..bfd5ae325 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -90,6 +90,7 @@ static FCompatOption Options[] = { "resetplayerspeed", 0, BCOMPATF_RESETPLAYERSPEED }, { "vileghosts", 0, BCOMPATF_VILEGHOSTS }, { "ignoreteleporttags", 0, BCOMPATF_BADTELEPORTERS }, + { "oldportals", 0, BCOMPATF_BADPORTALS }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, 0 }, @@ -280,7 +281,12 @@ void CheckCompatibility(MapData *map) ib_compatflags = 0; ii_compatparams = -1; } - + else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPORTAL)) + { + ii_compatflags = 0; + ib_compatflags = BCOMPATF_BADPORTALS; + ii_compatparams = -1; + } else { map->GetChecksum(md5.Bytes); diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 6fd42a629..123f33f7d 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -174,6 +174,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) else if(sc.Compare("Extended")) iwad->flags |= GI_MENUHACK_EXTENDED; else if(sc.Compare("Shorttex")) iwad->flags |= GI_COMPATSHORTTEX; else if(sc.Compare("Stairs")) iwad->flags |= GI_COMPATSTAIRS; + else if(sc.Compare("Portals")) iwad->flags |= GI_BADPORTALS; else sc.ScriptError(NULL); } while (sc.CheckString(",")); diff --git a/src/doomdef.h b/src/doomdef.h index cad56d642..748f2df4a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -342,6 +342,7 @@ enum BCOMPATF_RESETPLAYERSPEED = 1 << 1, // Set player speed to 1.0 when changing maps BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected. BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials + BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior }; // phares 3/20/98: diff --git a/src/p_spec.cpp b/src/p_spec.cpp index eeb58d0c3..a2e09d347 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -975,6 +975,9 @@ void P_SetupPortals() points.Push(pt); } + // Maps using undefined portal hacks may not benefit from portal optimizations. + if (ib_compatflags & BCOMPATF_BADPORTALS) return; + for(unsigned i=0;ispecial1 == 0 && points[i]->Mate != NULL) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index eaa3d4b47..3fa6bdf73 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -582,20 +582,27 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl check->viewx == stacked_viewx && check->viewy == stacked_viewy && check->viewz == stacked_viewz && - check->alpha == alpha && - (alpha == 0 || // if alpha is > 0 everything needs to be checked - (plane == check->height && - picnum == check->picnum && - lightlevel == check->lightlevel && - xoffs == check->xoffs && // killough 2/28/98: Add offset checks - yoffs == check->yoffs && - basecolormap == check->colormap && // [RH] Add more checks - xscale == check->xscale && - yscale == check->yscale && - angle == check->angle + ( + // headache inducing logic... :( + (ib_compatflags & BCOMPATF_BADPORTALS) || + ( + check->alpha == alpha && + (alpha == 0 || // if alpha is > 0 everything needs to be checked + (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs && + basecolormap == check->colormap && // [RH] Add more checks + xscale == check->xscale && + yscale == check->yscale && + angle == check->angle + ) + ) && + check->viewangle == stacked_angle ) - ) && - check->viewangle == stacked_angle) + ) + ) { return check; } diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index efdf836a5..7a94f244a 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -26,6 +26,7 @@ IWad Game = "Doom" Config = "UrbanBrawl" Mapinfo = "mapinfo/urbanbrawl.txt" + Compatibility = "Portals" MustContain = "MAP01", "AD2LIB" BannerColors = "a8 a8 00", "a8 00 00" } From 007fae107e5ff73626a43da9f5305e579d91df42 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 2 Dec 2010 17:01:05 +0000 Subject: [PATCH 178/815] - fix last commit SVN r3019 (trunk) --- src/d_iwad.cpp | 2 +- src/gi.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 123f33f7d..b02407b15 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -174,7 +174,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) else if(sc.Compare("Extended")) iwad->flags |= GI_MENUHACK_EXTENDED; else if(sc.Compare("Shorttex")) iwad->flags |= GI_COMPATSHORTTEX; else if(sc.Compare("Stairs")) iwad->flags |= GI_COMPATSTAIRS; - else if(sc.Compare("Portals")) iwad->flags |= GI_BADPORTALS; + else if(sc.Compare("Portals")) iwad->flags |= GI_COMPATPORTAL; else sc.ScriptError(NULL); } while (sc.CheckString(",")); diff --git a/src/gi.h b/src/gi.h index 424052625..0af160b6b 100644 --- a/src/gi.h +++ b/src/gi.h @@ -47,6 +47,7 @@ #define GI_COMPATPOLY1 0x00000040 // Hexen's MAP36 needs old polyobject drawing #define GI_COMPATPOLY2 0x00000080 // so does HEXDD's MAP47 #define GI_NOTEXTCOLOR 0x00000100 +#define GI_COMPATPORTAL 0x00000200 // same for stairbuilding #include "gametype.h" From 72f589c8c3786210019a2a488a55181c1e8d3ab9 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 2 Dec 2010 21:45:38 +0000 Subject: [PATCH 179/815] - Fixed what appears to be a copy/paste error in the CMakeLists.txt. SVN r3020 (trunk) --- src/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f9291a5af..7e5ecf22b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -380,8 +380,7 @@ endif( SSE_MATTERS ) if( CMAKE_COMPILER_IS_GNUCXX ) if( PROFILE ) - set( CMAKE_C_FLinclude( FindFluidSynth.cmake ) -AGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) + set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" ) set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" ) From e7bbca8e3367cd869a227fd054e30e7d5a440f4f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 4 Dec 2010 08:53:13 +0000 Subject: [PATCH 180/815] - expanded compat_trace flag to apply also to sight checks. - Set trace compatibility for Real World MAP11. SVN r3021 (trunk) --- src/p_sight.cpp | 5 +++++ wadsrc/static/compatibility.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 4324ebc0a..359b5d4d3 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -102,6 +102,11 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) // // crosses a two sided line // + + // ignore self referencing sectors if COMPAT_TRACE is on + if ((i_compatflags & COMPATF_TRACE) && li->frontsector == li->backsector) + return true; + fixed_t trX=trace.x + FixedMul (trace.dx, in->frac); fixed_t trY=trace.y + FixedMul (trace.dy, in->frac); P_LineOpening (open, NULL, li, trX, trY); diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index c3a4404aa..14cda7966 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -53,6 +53,7 @@ F84AB4557464A383E93F37CD3A82AC48 // MM2 map03 } 2FE901F659A16E58D7BCD7C30021C238 // AV map15 +74AF92E96FE10D039D31C1F6526D7D7C // Real World map11 { trace } From 9ad2a2b00e82a74c9b3ed6f440deba5188ea9273 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 6 Dec 2010 21:57:51 +0000 Subject: [PATCH 181/815] - fixed: AActor::ClearCounters should not decrease the kill count if it is called for a dead monster which can happen in Thing_Remove. SVN r3022 (trunk) --- src/p_mobj.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index adfbeb0ef..a73600891 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5630,7 +5630,7 @@ const char *AActor::GetTag(const char *def) const void AActor::ClearCounters() { - if (CountsAsKill()) + if (CountsAsKill() && health > 0) { level.total_monsters--; flags &= ~MF_COUNTKILL; From d54ee476569bb4e32ec2a287d0b875c40158a906 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 7 Dec 2010 16:16:31 +0000 Subject: [PATCH 182/815] - fixed: The intermission screen did not initialize its texture variable when the texture was invalid. SVN r3023 (trunk) --- src/intermission/intermission.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index f0d308579..1287f93cc 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -110,12 +110,8 @@ void DIntermissionScreen::Init(FIntermissionAction *desc, bool first) { texname = GStrings[texname+1]; } - FTextureID tex = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); - if (tex.isValid()) - { - mBackground = tex; - mFlatfill = desc->mFlatfill; - } + mBackground = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); + mFlatfill = desc->mFlatfill; S_Sound (CHAN_VOICE | CHAN_UI, desc->mSound, 1.0f, ATTN_NONE); if (desc->mPalette.IsNotEmpty() && (lumpnum = Wads.CheckNumForFullName(desc->mPalette, true)) > 0) { From 092f53d8750a9d53df5415512d5809640fbe5648 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 9 Dec 2010 07:50:08 +0000 Subject: [PATCH 183/815] - fixed: The 'secret' CCMD used FStrings directly as printf parameters. SVN r3024 (trunk) --- src/c_cmds.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 12055ebca..0831c805f 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -1030,10 +1030,10 @@ CCMD(secret) { FString levelname; level_info_t *info = FindLevelInfo(mapname); - levelname.Format("%s - %s\n", mapname, info->LevelName); + levelname.Format("%s - %s\n", mapname, info->LevelName.GetChars()); size_t llen = levelname.Len() - 1; for(size_t ii=0; ii Date: Thu, 9 Dec 2010 07:55:46 +0000 Subject: [PATCH 184/815] - fixed: Portal setup did some incorrect validity checks. SVN r3025 (trunk) --- src/p_spec.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index a2e09d347..dd3e0ca11 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -867,7 +867,7 @@ static void SetupFloorPortal (AStackPoint *point) NActorIterator it (NAME_LowerStackLookOnly, point->tid); sector_t *Sector = point->Sector; Sector->FloorSkyBox = static_cast(it.Next()); - if (Sector->FloorSkyBox != NULL || !Sector->FloorSkyBox->bAlways) + if (Sector->FloorSkyBox != NULL && Sector->FloorSkyBox->bAlways) { Sector->FloorSkyBox->Mate = point; if (Sector->GetAlpha(sector_t::floor) == OPAQUE) @@ -880,7 +880,7 @@ static void SetupCeilingPortal (AStackPoint *point) NActorIterator it (NAME_UpperStackLookOnly, point->tid); sector_t *Sector = point->Sector; Sector->CeilingSkyBox = static_cast(it.Next()); - if (Sector->CeilingSkyBox != NULL || !Sector->CeilingSkyBox->bAlways) + if (Sector->CeilingSkyBox != NULL && Sector->CeilingSkyBox->bAlways) { Sector->CeilingSkyBox->Mate = point; if (Sector->GetAlpha(sector_t::ceiling) == OPAQUE) From 6309d90be0f1d2480f2ffba25d9639ece66e9fff Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Dec 2010 12:00:40 +0000 Subject: [PATCH 185/815] - moved texture counting code into FTextureManager. - moved all code and data for Build tile management into FTextureManager. - moved texture animation management into FTextureManager. - changed: Animate textures only once per frame, not per view. Otherwise with animations that have sub-frame accuracy camera textures of the same area can show different animation frames if the frame changes falls between the rendering of the different views. SVN r3026 (trunk) --- src/d_main.cpp | 9 +- src/p_setup.cpp | 1 - src/r_data.cpp | 98 ---- src/r_local.h | 6 - src/r_main.cpp | 1 - src/r_main.h | 1 - src/r_sky.cpp | 17 + src/r_sky.h | 1 + src/{r_anim.cpp => textures/animations.cpp} | 521 +++++++++----------- src/textures/buildtexture.cpp | 29 +- src/textures/texturemanager.cpp | 144 +++++- src/textures/textures.h | 80 ++- zdoom.vcproj | 8 +- 13 files changed, 482 insertions(+), 434 deletions(-) rename src/{r_anim.cpp => textures/animations.cpp} (64%) diff --git a/src/d_main.cpp b/src/d_main.cpp index 61400d8a8..fd03a5c4a 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -712,10 +712,12 @@ void D_Display () } else { + unsigned int nowtime = I_FPSTime(); + TexMan.UpdateAnimations(nowtime); + R_UpdateSky(nowtime); switch (gamestate) { case GS_FULLCONSOLE: - R_UpdateAnimations(I_FPSTime()); screen->SetBlendingRect(0,0,0,0); hw2d = screen->Begin2D(false); C_DrawConsole (false); @@ -780,7 +782,6 @@ void D_Display () break; case GS_INTERMISSION: - R_UpdateAnimations(I_FPSTime()); screen->SetBlendingRect(0,0,0,0); hw2d = screen->Begin2D(false); WI_Drawer (); @@ -788,7 +789,6 @@ void D_Display () break; case GS_FINALE: - R_UpdateAnimations(I_FPSTime()); screen->SetBlendingRect(0,0,0,0); hw2d = screen->Begin2D(false); F_Drawer (); @@ -796,7 +796,6 @@ void D_Display () break; case GS_DEMOSCREEN: - R_UpdateAnimations(I_FPSTime()); screen->SetBlendingRect(0,0,0,0); hw2d = screen->Begin2D(false); D_PageDrawer (); @@ -1987,7 +1986,7 @@ void D_DoomMain (void) S_Init (); Printf ("ST_Init: Init startup screen.\n"); - StartScreen = FStartupScreen::CreateInstance (R_GuesstimateNumTextures() + 5); + StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); ParseCompatibility(); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 549028cdb..9a0c001a0 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4055,7 +4055,6 @@ void P_Init () atterm (P_Shutdown); P_InitEffects (); // [RH] - R_InitPicAnims (); P_InitSwitchList (); P_InitTerrainTypes (); P_InitKeyMessages (); diff --git a/src/r_data.cpp b/src/r_data.cpp index ee40e29ca..37602007e 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -286,103 +286,6 @@ void R_InitData () StartScreen->Progress(); } -//=========================================================================== -// -// R_GuesstimateNumTextures -// -// Returns an estimate of the number of textures R_InitData will have to -// process. Used by D_DoomMain() when it calls ST_Init(). -// -//=========================================================================== - -int R_GuesstimateNumTextures () -{ - int numtex = 0; - - for(int i = Wads.GetNumLumps()-1; i>=0; i--) - { - int space = Wads.GetLumpNamespace(i); - switch(space) - { - case ns_flats: - case ns_sprites: - case ns_newtextures: - case ns_hires: - case ns_patches: - case ns_graphics: - numtex++; - break; - - default: - if (Wads.GetLumpFlags(i) & LUMPF_MAYBEFLAT) numtex++; - - break; - } - } - - numtex += R_CountBuildTiles (); - numtex += R_CountTexturesX (); - return numtex; -} - -//=========================================================================== -// -// R_CountTexturesX -// -// See R_InitTextures() for the logic in deciding what lumps to check. -// -//=========================================================================== - -static int R_CountTexturesX () -{ - int count = 0; - int wadcount = Wads.GetNumWads(); - for (int wadnum = 0; wadnum < wadcount; wadnum++) - { - // Use the most recent PNAMES for this WAD. - // Multiple PNAMES in a WAD will be ignored. - int pnames = Wads.CheckNumForName("PNAMES", ns_global, wadnum, false); - - // should never happen except for zdoom.pk3 - if (pnames < 0) continue; - - // Only count the patches if the PNAMES come from the current file - // Otherwise they have already been counted. - if (Wads.GetLumpFile(pnames) == wadnum) - { - count += R_CountLumpTextures (pnames); - } - - int texlump1 = Wads.CheckNumForName ("TEXTURE1", ns_global, wadnum); - int texlump2 = Wads.CheckNumForName ("TEXTURE2", ns_global, wadnum); - - count += R_CountLumpTextures (texlump1) - 1; - count += R_CountLumpTextures (texlump2) - 1; - } - return count; -} - -//=========================================================================== -// -// R_CountLumpTextures -// -// Returns the number of patches in a PNAMES/TEXTURE1/TEXTURE2 lump. -// -//=========================================================================== - -static int R_CountLumpTextures (int lumpnum) -{ - if (lumpnum >= 0) - { - FWadLump file = Wads.OpenLumpNum (lumpnum); - DWORD numtex; - - file >> numtex; - return numtex >= 0 ? numtex : 0; - } - return 0; -} - //=========================================================================== // // R_DeinitData @@ -392,7 +295,6 @@ static int R_CountLumpTextures (int lumpnum) void R_DeinitData () { R_DeinitColormaps (); - R_DeinitBuildTiles(); FCanvasTextureInfo::EmptyList(); // Free openings diff --git a/src/r_local.h b/src/r_local.h index b4508a229..c666b1de4 100644 --- a/src/r_local.h +++ b/src/r_local.h @@ -39,12 +39,6 @@ #include "r_things.h" #include "r_draw.h" -// r_anim.cpp - -void R_InitPicAnims (); -void R_AddSimpleAnim (FTextureID picnum, int animcount, int animtype, DWORD animspeed /* in ms */, DWORD speedrange=0); -void R_UpdateAnimations (DWORD mstime); - bool R_AlignFlat (int linenum, int side, int fc); #endif // __R_LOCAL_H__ diff --git a/src/r_main.cpp b/src/r_main.cpp index fa25f7ca1..88aab962c 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1084,7 +1084,6 @@ void R_SetupFrame (AActor *actor) iview->otic = nowtic; } - R_UpdateAnimations (I_FPSTime()); r_TicFrac = I_GetTimeFrac (&r_FrameTime); if (cl_capfps || r_NoInterpolate) { diff --git a/src/r_main.h b/src/r_main.h index e633404e1..c894a2bcf 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -189,7 +189,6 @@ void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, int x, int y, int wid void R_ResetViewInterpolation (); // Called by startup code. -int R_GuesstimateNumTextures (); void R_Init (void); void R_ExecuteSetViewSize (void); diff --git a/src/r_sky.cpp b/src/r_sky.cpp index 544c1ded9..e9c319de1 100644 --- a/src/r_sky.cpp +++ b/src/r_sky.cpp @@ -137,3 +137,20 @@ void R_InitSkyMap () sky2cyl = MAX(skytex2->GetWidth(), skytex2->xScale >> (16 - 10)); } + +//========================================================================== +// +// R_UpdateSky +// +// Performs sky scrolling +// +//========================================================================== + +void R_UpdateSky (DWORD mstime) +{ + // Scroll the sky + double ms = (double)mstime * FRACUNIT; + sky1pos = ms * level.skyspeed1; + sky2pos = ms * level.skyspeed2; +} + diff --git a/src/r_sky.h b/src/r_sky.h index 3e836ec3e..f285c4268 100644 --- a/src/r_sky.h +++ b/src/r_sky.h @@ -38,5 +38,6 @@ extern bool skystretch; // Called whenever the sky changes. void R_InitSkyMap (); +void R_UpdateSky (DWORD mstime); #endif //__R_SKY_H__ diff --git a/src/r_anim.cpp b/src/textures/animations.cpp similarity index 64% rename from src/r_anim.cpp rename to src/textures/animations.cpp index cac361866..a4f0efe0c 100644 --- a/src/r_anim.cpp +++ b/src/textures/animations.cpp @@ -49,69 +49,91 @@ // TYPES ------------------------------------------------------------------- -// -// Animating textures and planes -// -// [RH] Expanded to work with a Hexen ANIMDEFS lump -// - -struct FAnimDef -{ - FTextureID BasePic; - WORD NumFrames; - WORD CurFrame; - BYTE AnimType; - DWORD SwitchTime; // Time to advance to next frame - struct FAnimFrame - { - DWORD SpeedMin; // Speeds are in ms, not tics - DWORD SpeedRange; - FTextureID FramePic; - } Frames[1]; - enum - { - ANIM_Forward, - ANIM_Backward, - ANIM_OscillateUp, - ANIM_OscillateDown, - ANIM_DiscreteFrames - }; - - void SetSwitchTime (DWORD mstime); -}; - -// This is an array of pointers to animation definitions. -// When it is destroyed, it deletes any animations it points to as well. -class AnimArray : public TArray -{ -public: - ~AnimArray(); - void AddAnim (FAnimDef *anim); - void FixAnimations (); -}; - // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- -static void R_InitAnimDefs (); -static void R_AddComplexAnim (FTextureID picnum, const TArray &frames); -static void ParseAnim (FScanner &sc, bool istex); -static void ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing); -static void ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing, TArray &frames); -static FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetype, bool allowMissing); -static void ParseTime (FScanner &sc, DWORD &min, DWORD &max); - // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static AnimArray Anims; static FRandom pr_animatepictures ("AnimatePics"); // CODE -------------------------------------------------------------------- //========================================================================== // -// R_InitPicAnims +// FTextureManager :: AddAnim +// +// Adds a new animation to the array. If one with the same basepic as the +// new one already exists, it is replaced. +// +//========================================================================== + +void FTextureManager::AddAnim (FAnimDef *anim) +{ + // Search for existing duplicate. + for (unsigned int i = 0; i < mAnimations.Size(); ++i) + { + if (mAnimations[i]->BasePic == anim->BasePic) + { + // Found one! + free (mAnimations[i]); + mAnimations[i] = anim; + return; + } + } + // Didn't find one, so add it at the end. + mAnimations.Push (anim); +} + +//========================================================================== +// +// FTextureManager :: AddSimpleAnim +// +// Creates an animation with simple characteristics. This is used for +// original Doom (non-ANIMDEFS-style) animations and Build animations. +// +//========================================================================== + +void FTextureManager::AddSimpleAnim (FTextureID picnum, int animcount, int animtype, DWORD speedmin, DWORD speedrange) +{ + if (AreTexturesCompatible(picnum, picnum + (animcount - 1))) + { + FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); + anim->CurFrame = 0; + anim->BasePic = picnum; + anim->NumFrames = animcount; + anim->AnimType = animtype; + anim->SwitchTime = 0; + anim->Frames[0].SpeedMin = speedmin; + anim->Frames[0].SpeedRange = speedrange; + anim->Frames[0].FramePic = anim->BasePic; + AddAnim (anim); + } +} + +//========================================================================== +// +// FTextureManager :: AddComplexAnim +// +// Creates an animation with individually defined frames. +// +//========================================================================== + +void FTextureManager::AddComplexAnim (FTextureID picnum, const TArray &frames) +{ + FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef) + (frames.Size()-1) * sizeof(frames[0])); + anim->BasePic = picnum; + anim->NumFrames = frames.Size(); + anim->CurFrame = 0; + anim->AnimType = FAnimDef::ANIM_DiscreteFrames; + anim->SwitchTime = 0; + memcpy (&anim->Frames[0], &frames[0], frames.Size() * sizeof(frames[0])); + AddAnim (anim); +} + +//========================================================================== +// +// FTextureManager :: Initanimated // // [description copied from BOOM] // Load the table of animation definitions, checking for existence of @@ -139,9 +161,9 @@ static FRandom pr_animatepictures ("AnimatePics"); CVAR(Bool, debuganimated, false, 0) -void R_InitPicAnims (void) +void FTextureManager::InitAnimated (void) { - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable; + const BITFIELD texflags = TEXMAN_Overridable; // I think better not! This is only for old ANIMATED definition that // don't know about ZDoom's more flexible texture system. // | FTextureManager::TEXMAN_TryAny; @@ -163,17 +185,17 @@ void R_InitPicAnims (void) if (*anim_p /* .istexture */ & 1) { // different episode ? - if (!(pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Wall, texflags)).Exists() || - !(pic2 = TexMan.CheckForTexture (anim_p + 1 /* .endname */, FTexture::TEX_Wall, texflags)).Exists()) + if (!(pic1 = CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Wall, texflags)).Exists() || + !(pic2 = CheckForTexture (anim_p + 1 /* .endname */, FTexture::TEX_Wall, texflags)).Exists()) continue; // [RH] Bit 1 set means allow decals on walls with this texture - TexMan[pic2]->bNoDecals = TexMan[pic1]->bNoDecals = !(*anim_p & 2); + Texture(pic2)->bNoDecals = Texture(pic1)->bNoDecals = !(*anim_p & 2); } else { - if (!(pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Flat, texflags)).Exists() || - !(pic2 = TexMan.CheckForTexture (anim_p + 1 /* .startname */, FTexture::TEX_Flat, texflags)).Exists()) + if (!(pic1 = CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Flat, texflags)).Exists() || + !(pic2 = CheckForTexture (anim_p + 1 /* .startname */, FTexture::TEX_Flat, texflags)).Exists()) continue; } if (pic1 == pic2) @@ -183,8 +205,8 @@ void R_InitPicAnims (void) continue; } - FTexture *tex1 = TexMan[pic1]; - FTexture *tex2 = TexMan[pic2]; + FTexture *tex1 = Texture(pic1); + FTexture *tex2 = Texture(pic2); if (tex1->UseType != tex2->UseType) { @@ -213,71 +235,22 @@ void R_InitPicAnims (void) (BYTE(anim_p[21]) << 16) | (BYTE(anim_p[22]) << 24), 1000, 35); - R_AddSimpleAnim (pic1, pic2 - pic1 + 1, animtype, animspeed); + AddSimpleAnim (pic1, pic2 - pic1 + 1, animtype, animspeed); } } - // [RH] Load any ANIMDEFS lumps - R_InitAnimDefs (); - Anims.FixAnimations (); } //========================================================================== // -// R_AddSimpleAnim -// -// Creates an animation with simple characteristics. This is used for -// original Doom (non-ANIMDEFS-style) animations and Build animations. -// -//========================================================================== - -void R_AddSimpleAnim (FTextureID picnum, int animcount, int animtype, DWORD speedmin, DWORD speedrange) -{ - if (TexMan.AreTexturesCompatible(picnum, picnum + (animcount - 1))) - { - FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); - anim->CurFrame = 0; - anim->BasePic = picnum; - anim->NumFrames = animcount; - anim->AnimType = animtype; - anim->SwitchTime = 0; - anim->Frames[0].SpeedMin = speedmin; - anim->Frames[0].SpeedRange = speedrange; - anim->Frames[0].FramePic = anim->BasePic; - Anims.AddAnim (anim); - } -} - -//========================================================================== -// -// R_AddComplexAnim -// -// Creates an animation with individually defined frames. -// -//========================================================================== - -static void R_AddComplexAnim (FTextureID picnum, const TArray &frames) -{ - FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef) + (frames.Size()-1) * sizeof(frames[0])); - anim->BasePic = picnum; - anim->NumFrames = frames.Size(); - anim->CurFrame = 0; - anim->AnimType = FAnimDef::ANIM_DiscreteFrames; - anim->SwitchTime = 0; - memcpy (&anim->Frames[0], &frames[0], frames.Size() * sizeof(frames[0])); - Anims.AddAnim (anim); -} - -//========================================================================== -// -// R_InitAnimDefs +// FTextureManager :: InitAnimDefs // // This uses a Hexen ANIMDEFS lump to define the animation sequences // //========================================================================== -static void R_InitAnimDefs () +void FTextureManager::InitAnimDefs () { - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; int lump, lastlump = 0; while ((lump = Wads.FindLump ("ANIMDEFS", &lastlump)) != -1) @@ -288,11 +261,11 @@ static void R_InitAnimDefs () { if (sc.Compare ("flat")) { - ParseAnim (sc, false); + ParseAnim (sc, FTexture::TEX_Flat); } else if (sc.Compare ("texture")) { - ParseAnim (sc, true); + ParseAnim (sc, FTexture::TEX_Wall); } else if (sc.Compare ("switch")) { @@ -301,105 +274,11 @@ static void R_InitAnimDefs () // [GRB] Added warping type 2 else if (sc.Compare ("warp") || sc.Compare ("warp2")) { - bool isflat = false; - bool type2 = sc.Compare ("warp2"); // [GRB] - sc.MustGetString (); - if (sc.Compare ("flat")) - { - isflat = true; - sc.MustGetString (); - } - else if (sc.Compare ("texture")) - { - isflat = false; - sc.MustGetString (); - } - else - { - sc.ScriptError (NULL); - } - FTextureID picnum = TexMan.CheckForTexture (sc.String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); - if (picnum.isValid()) - { - FTexture * warper = TexMan[picnum]; - - // don't warp a texture more than once - if (!warper->bWarped) - { - if (type2) // [GRB] - warper = new FWarp2Texture (warper); - else - warper = new FWarpTexture (warper); - TexMan.ReplaceTexture (picnum, warper, false); - } - - if (sc.CheckFloat()) - { - static_cast(warper)->SetSpeed(float(sc.Float)); - } - - // No decals on warping textures, by default. - // Warping information is taken from the last warp - // definition for this texture. - warper->bNoDecals = true; - if (sc.GetString ()) - { - if (sc.Compare ("allowdecals")) - { - warper->bNoDecals = false; - } - else - { - sc.UnGet (); - } - } - } + ParseWarp(sc); } else if (sc.Compare ("cameratexture")) { - int width, height; - int fitwidth, fitheight; - FString picname; - - sc.MustGetString (); - picname = sc.String; - sc.MustGetNumber (); - width = sc.Number; - sc.MustGetNumber (); - height = sc.Number; - FTextureID picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Flat, texflags); - FTexture *viewer = new FCanvasTexture (picname, width, height); - if (picnum.Exists()) - { - FTexture *oldtex = TexMan[picnum]; - fitwidth = oldtex->GetScaledWidth (); - fitheight = oldtex->GetScaledHeight (); - viewer->UseType = oldtex->UseType; - TexMan.ReplaceTexture (picnum, viewer, true); - } - else - { - fitwidth = width; - fitheight = height; - // [GRB] No need for oldtex - viewer->UseType = FTexture::TEX_Wall; - TexMan.AddTexture (viewer); - } - if (sc.GetString()) - { - if (sc.Compare ("fit")) - { - sc.MustGetNumber (); - fitwidth = sc.Number; - sc.MustGetNumber (); - fitheight = sc.Number; - } - else - { - sc.UnGet (); - } - } - viewer->SetScaledSize(fitwidth, fitheight); + ParseCameraTexture(sc); } else if (sc.Compare ("animatedDoor")) { @@ -408,12 +287,11 @@ static void R_InitAnimDefs () else if (sc.Compare("skyoffset")) { sc.MustGetString (); - FTextureID picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + FTextureID picnum = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); sc.MustGetNumber(); if (picnum.Exists()) { - FTexture *tex = TexMan[picnum]; - tex->SkyOffset = sc.Number; + Texture(picnum)->SkyOffset = sc.Number; } } else @@ -426,31 +304,28 @@ static void R_InitAnimDefs () //========================================================================== // -// ParseAnim +// FTextureManager :: ParseAnim // // Parse a single animation definition out of an ANIMDEFS lump and // create the corresponding animation structure. // //========================================================================== -static void ParseAnim (FScanner &sc, bool istex) +void FTextureManager::ParseAnim (FScanner &sc, int usetype) { - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; TArray frames (32); FTextureID picnum; - int usetype; int defined = 0; bool optional = false, missing = false; - usetype = istex ? FTexture::TEX_Wall : FTexture::TEX_Flat; - sc.MustGetString (); if (sc.Compare ("optional")) { optional = true; sc.MustGetString (); } - picnum = TexMan.CheckForTexture (sc.String, usetype, texflags); + picnum = CheckForTexture (sc.String, usetype, texflags); if (!picnum.Exists()) { @@ -467,7 +342,7 @@ static void ParseAnim (FScanner &sc, bool istex) // no decals on animating textures, by default if (picnum.isValid()) { - TexMan[picnum]->bNoDecals = true; + Texture(picnum)->bNoDecals = true; } while (sc.GetString ()) @@ -476,7 +351,7 @@ static void ParseAnim (FScanner &sc, bool istex) { if (picnum.isValid()) { - TexMan[picnum]->bNoDecals = false; + Texture(picnum)->bNoDecals = false; } continue; } @@ -517,20 +392,20 @@ static void ParseAnim (FScanner &sc, bool istex) { sc.ScriptError ("Animation needs at least 2 frames"); } - R_AddComplexAnim (picnum, frames); + AddComplexAnim (picnum, frames); } } //========================================================================== // -// ParseRangeAnim +// FTextureManager :: ParseRangeAnim // // Parse an animation defined using "range". Not that one range entry is // enough to define a complete animation, unlike "pic". // //========================================================================== -static void ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing) +void FTextureManager::ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing) { int type; FTextureID framenum; @@ -547,7 +422,7 @@ static void ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool m if (framenum < picnum) { type = FAnimDef::ANIM_Backward; - TexMan[framenum]->bNoDecals = TexMan[picnum]->bNoDecals; + Texture(framenum)->bNoDecals = Texture(picnum)->bNoDecals; swapvalues (framenum, picnum); } if (sc.GetString()) @@ -561,18 +436,18 @@ static void ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool m sc.UnGet (); } } - R_AddSimpleAnim (picnum, framenum - picnum + 1, type, min, max - min); + AddSimpleAnim (picnum, framenum - picnum + 1, type, min, max - min); } //========================================================================== // -// ParsePicAnim +// FTextureManager :: ParsePicAnim // // Parse a single frame from ANIMDEFS defined using "pic". // //========================================================================== -static void ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing, TArray &frames) +void FTextureManager::ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing, TArray &frames) { FTextureID framenum; DWORD min, max; @@ -593,16 +468,16 @@ static void ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool mis //========================================================================== // -// ParseFramenum +// FTextureManager :: ParseFramenum // // Reads a frame's texture from ANIMDEFS. It can either be an integral // offset from basepicnum or a specific texture name. // //========================================================================== -static FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetype, bool allowMissing) +FTextureID FTextureManager::ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetype, bool allowMissing) { - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; FTextureID framenum; sc.MustGetString (); @@ -612,7 +487,7 @@ static FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetyp } else { - framenum = TexMan.CheckForTexture (sc.String, usetype, texflags); + framenum = CheckForTexture (sc.String, usetype, texflags); if (!framenum.Exists() && !allowMissing) { sc.ScriptError ("Unknown texture %s", sc.String); @@ -623,13 +498,13 @@ static FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetyp //========================================================================== // -// ParseTime +// FTextureManager :: ParseTime // // Reads a tics or rand time definition from ANIMDEFS. // //========================================================================== -static void ParseTime (FScanner &sc, DWORD &min, DWORD &max) +void FTextureManager::ParseTime (FScanner &sc, DWORD &min, DWORD &max) { sc.MustGetString (); if (sc.Compare ("tics")) @@ -652,53 +527,128 @@ static void ParseTime (FScanner &sc, DWORD &min, DWORD &max) //========================================================================== // -// AnimArray :: ~AnimArray +// FTextureManager :: ParseWarp // -// Frees all animations held in this array before freeing the array. +// Parses a warping texture definition // //========================================================================== -AnimArray::~AnimArray() +void FTextureManager::ParseWarp(FScanner &sc) { - for (unsigned i = 0; i < Size(); i++) + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + bool isflat = false; + bool type2 = sc.Compare ("warp2"); // [GRB] + sc.MustGetString (); + if (sc.Compare ("flat")) { - if ((*this)[i] != NULL) + isflat = true; + sc.MustGetString (); + } + else if (sc.Compare ("texture")) + { + isflat = false; + sc.MustGetString (); + } + else + { + sc.ScriptError (NULL); + } + FTextureID picnum = CheckForTexture (sc.String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); + if (picnum.isValid()) + { + FTexture *warper = Texture(picnum); + + // don't warp a texture more than once + if (!warper->bWarped) { - M_Free ((*this)[i]); - (*this)[i] = NULL; + if (type2) warper = new FWarp2Texture (warper); + else warper = new FWarpTexture (warper); + + ReplaceTexture (picnum, warper, false); + } + + if (sc.CheckFloat()) + { + static_cast(warper)->SetSpeed(float(sc.Float)); + } + + // No decals on warping textures, by default. + // Warping information is taken from the last warp + // definition for this texture. + warper->bNoDecals = true; + if (sc.GetString ()) + { + if (sc.Compare ("allowdecals")) + { + warper->bNoDecals = false; + } + else + { + sc.UnGet (); + } } } } //========================================================================== // -// AnimArray :: AddAnim +// ParseCameraTexture // -// Adds a new animation to the array. If one with the same basepic as the -// new one already exists, it is replaced. +// Parses a camera texture definition // //========================================================================== -void AnimArray::AddAnim (FAnimDef *anim) +void FTextureManager::ParseCameraTexture(FScanner &sc) { - // Search for existing duplicate. - for (unsigned int i = 0; i < Anims.Size(); ++i) + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + int width, height; + int fitwidth, fitheight; + FString picname; + + sc.MustGetString (); + picname = sc.String; + sc.MustGetNumber (); + width = sc.Number; + sc.MustGetNumber (); + height = sc.Number; + FTextureID picnum = CheckForTexture (picname, FTexture::TEX_Flat, texflags); + FTexture *viewer = new FCanvasTexture (picname, width, height); + if (picnum.Exists()) { - if ((*this)[i]->BasePic == anim->BasePic) + FTexture *oldtex = Texture(picnum); + fitwidth = oldtex->GetScaledWidth (); + fitheight = oldtex->GetScaledHeight (); + viewer->UseType = oldtex->UseType; + ReplaceTexture (picnum, viewer, true); + } + else + { + fitwidth = width; + fitheight = height; + // [GRB] No need for oldtex + viewer->UseType = FTexture::TEX_Wall; + AddTexture (viewer); + } + if (sc.GetString()) + { + if (sc.Compare ("fit")) { - // Found one! - free ((*this)[i]); - (*this)[i] = anim; - return; + sc.MustGetNumber (); + fitwidth = sc.Number; + sc.MustGetNumber (); + fitheight = sc.Number; + } + else + { + sc.UnGet (); } } - // Didn't find one, so add it at the end. - Push (anim); + viewer->SetScaledSize(fitwidth, fitheight); } //========================================================================== // -// AnimArray :: FixAnimations +// FTextureManager :: FixAnimations // // Copy the "front sky" flag from an animated texture to the rest // of the textures in the animation, and make every texture in an @@ -706,21 +656,21 @@ void AnimArray::AddAnim (FAnimDef *anim) // //========================================================================== -void AnimArray::FixAnimations () +void FTextureManager::FixAnimations () { unsigned int i; int j; - for (i = 0; i < Size(); ++i) + for (i = 0; i < mAnimations.Size(); ++i) { - FAnimDef *anim = operator[] (i); + FAnimDef *anim = mAnimations[i]; if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames) { - if (TexMan[anim->BasePic]->bNoRemap0) + if (Texture(anim->BasePic)->bNoRemap0) { for (j = 0; j < anim->NumFrames; ++j) { - TexMan[anim->Frames[j].FramePic]->SetFrontSkyLayer (); + Texture(anim->Frames[j].FramePic)->SetFrontSkyLayer (); } } } @@ -730,11 +680,11 @@ void AnimArray::FixAnimations () bool noremap = false; const char *name; - name = TexMan[anim->BasePic]->Name; - nodecals = TexMan[anim->BasePic]->bNoDecals; + name = Texture(anim->BasePic)->Name; + nodecals = Texture(anim->BasePic)->bNoDecals; for (j = 0; j < anim->NumFrames; ++j) { - FTexture *tex = TexMan[anim->BasePic + j]; + FTexture *tex = Texture(anim->BasePic + j); noremap |= tex->bNoRemap0; tex->bNoDecals = nodecals; } @@ -742,7 +692,7 @@ void AnimArray::FixAnimations () { for (j = 0; j < anim->NumFrames; ++j) { - TexMan[anim->BasePic + j]->SetFrontSkyLayer (); + Texture(anim->BasePic + j)->SetFrontSkyLayer (); } } } @@ -768,19 +718,41 @@ void FAnimDef::SetSwitchTime (DWORD mstime) } } + //========================================================================== // -// R_UpdateAnimations +// FTextureManager :: SetTranslation +// +// Sets animation translation for a texture +// +//========================================================================== + +void FTextureManager::SetTranslation (FTextureID fromtexnum, FTextureID totexnum) +{ + if ((size_t)fromtexnum.texnum < Translation.Size()) + { + if ((size_t)totexnum.texnum >= Textures.Size()) + { + totexnum.texnum = fromtexnum.texnum; + } + Translation[fromtexnum.texnum] = totexnum.texnum; + } +} + + +//========================================================================== +// +// FTextureManager :: UpdateAnimations // // Updates texture translations for each animation and scrolls the skies. // //========================================================================== -void R_UpdateAnimations (DWORD mstime) +void FTextureManager::UpdateAnimations (DWORD mstime) { - for (unsigned int j = 0; j < Anims.Size(); ++j) + for (unsigned int j = 0; j < mAnimations.Size(); ++j) { - FAnimDef *anim = Anims[j]; + FAnimDef *anim = mAnimations[j]; // If this is the first time through R_UpdateAnimations, just // initialize the anim's switch time without actually animating. @@ -832,19 +804,14 @@ void R_UpdateAnimations (DWORD mstime) if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames) { - TexMan.SetTranslation (anim->BasePic, anim->Frames[anim->CurFrame].FramePic); + SetTranslation (anim->BasePic, anim->Frames[anim->CurFrame].FramePic); } else { for (unsigned int i = 0; i < anim->NumFrames; i++) { - TexMan.SetTranslation (anim->BasePic + i, anim->BasePic + (i + anim->CurFrame) % anim->NumFrames); + SetTranslation (anim->BasePic + i, anim->BasePic + (i + anim->CurFrame) % anim->NumFrames); } } } - - // Scroll the sky - double ms = (double)mstime * FRACUNIT; - sky1pos = ms * level.skyspeed1; - sky2pos = ms * level.skyspeed2; } diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index 5f4172a60..2a3d1809f 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -41,8 +41,6 @@ #include "cmdlib.h" #include "st_start.h" -static TArray BuildTileFiles; - //========================================================================== // // A texture defined in a Build TILESxxx.ART file @@ -158,7 +156,7 @@ const BYTE *FBuildTexture::GetColumn (unsigned int column, const Span **spans_ou // //=========================================================================== -void AddTiles (void *tiles) +void FTextureManager::AddTiles (void *tiles) { // int numtiles = LittleLong(((DWORD *)tiles)[1]); // This value is not reliable int tilestart = LittleLong(((DWORD *)tiles)[2]); @@ -183,7 +181,7 @@ void AddTiles (void *tiles) if (width <= 0 || height <= 0) continue; tex = new FBuildTexture (i, tiledata, width, height, xoffs, yoffs); - texnum = TexMan.AddTexture (tex); + texnum = AddTexture (tex); while (size > 0) { *tiledata = 255 - *tiledata; @@ -207,7 +205,7 @@ void AddTiles (void *tiles) speed = (anm >> 24) & 15; speed = MAX (1, (1 << speed) * 1000 / 120); // Convert from 120 Hz to 1000 Hz. - R_AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); + AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); } // Blood's rotation types: @@ -262,7 +260,7 @@ void AddTiles (void *tiles) // //=========================================================================== -static int CountTiles (void *tiles) +int FTextureManager::CountTiles (void *tiles) { int version = LittleLong(*(DWORD *)tiles); if (version != 1) @@ -285,7 +283,7 @@ static int CountTiles (void *tiles) // //=========================================================================== -int R_CountBuildTiles () +int FTextureManager::CountBuildTiles () { int numartfiles = 0; char artfile[] = "tilesXXX.art"; @@ -373,25 +371,10 @@ int R_CountBuildTiles () // //=========================================================================== -void R_InitBuildTiles () +void FTextureManager::InitBuildTiles () { for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) { AddTiles (BuildTileFiles[i]); } } - -//=========================================================================== -// -// R_DeinitBuildTiles -// -//=========================================================================== - -void R_DeinitBuildTiles () -{ - for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) - { - delete[] BuildTileFiles[i]; - } - BuildTileFiles.Clear(); -} diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index e562923db..80690a03b 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -62,8 +62,6 @@ FTextureManager TexMan; FTextureManager::FTextureManager () { memset (HashFirst, -1, sizeof(HashFirst)); - // Texture 0 is a dummy texture used to indicate "no texture" - AddTexture (new FDummyTexture); } @@ -74,11 +72,43 @@ FTextureManager::FTextureManager () //========================================================================== FTextureManager::~FTextureManager () +{ + DeleteAll(); +} + +//========================================================================== +// +// FTextureManager :: DeleteAll +// +//========================================================================== + +void FTextureManager::DeleteAll() { for (unsigned int i = 0; i < Textures.Size(); ++i) { delete Textures[i].Texture; } + Textures.Clear(); + Translation.Clear(); + FirstTextureForFile.Clear(); + memset (HashFirst, -1, sizeof(HashFirst)); + DefaultTexture.SetInvalid(); + + for (unsigned i = 0; i < mAnimations.Size(); i++) + { + if (mAnimations[i] != NULL) + { + M_Free (mAnimations[i]); + mAnimations[i] = NULL; + } + } + mAnimations.Clear(); + + for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) + { + delete[] BuildTileFiles[i]; + } + BuildTileFiles.Clear(); } //========================================================================== @@ -896,8 +926,14 @@ void FTextureManager::SortTexturesByType(int start, int end) void FTextureManager::Init() { + DeleteAll(); + // Init Build Tile data if it hasn't been done already + if (BuildTileFiles.Size() == 0) CountBuildTiles (); FTexture::InitGrayMap(); + // Texture 0 is a dummy texture used to indicate "no texture" + AddTexture (new FDummyTexture); + int wadcnt = Wads.GetNumWads(); for(int i = 0; i< wadcnt; i++) { @@ -907,7 +943,7 @@ void FTextureManager::Init() // Add one marker so that the last WAD is easier to handle and treat // Build tiles as a completely separate block. FirstTextureForFile.Push(Textures.Size()); - R_InitBuildTiles (); + InitBuildTiles (); FirstTextureForFile.Push(Textures.Size()); DefaultTexture = CheckForTexture ("-NOFLAT-", FTexture::TEX_Override, 0); @@ -938,6 +974,10 @@ void FTextureManager::Init() } } } + + InitAnimated(); + InitAnimDefs(); + FixAnimations(); } //========================================================================== @@ -988,6 +1028,104 @@ int FTextureManager::ReadTexture (FArchive &arc) else return -1; } +//=========================================================================== +// +// R_GuesstimateNumTextures +// +// Returns an estimate of the number of textures R_InitData will have to +// process. Used by D_DoomMain() when it calls ST_Init(). +// +//=========================================================================== + +int FTextureManager::GuesstimateNumTextures () +{ + int numtex = 0; + + for(int i = Wads.GetNumLumps()-1; i>=0; i--) + { + int space = Wads.GetLumpNamespace(i); + switch(space) + { + case ns_flats: + case ns_sprites: + case ns_newtextures: + case ns_hires: + case ns_patches: + case ns_graphics: + numtex++; + break; + + default: + if (Wads.GetLumpFlags(i) & LUMPF_MAYBEFLAT) numtex++; + + break; + } + } + + numtex += CountBuildTiles (); + numtex += CountTexturesX (); + return numtex; +} + +//=========================================================================== +// +// R_CountTexturesX +// +// See R_InitTextures() for the logic in deciding what lumps to check. +// +//=========================================================================== + +int FTextureManager::CountTexturesX () +{ + int count = 0; + int wadcount = Wads.GetNumWads(); + for (int wadnum = 0; wadnum < wadcount; wadnum++) + { + // Use the most recent PNAMES for this WAD. + // Multiple PNAMES in a WAD will be ignored. + int pnames = Wads.CheckNumForName("PNAMES", ns_global, wadnum, false); + + // should never happen except for zdoom.pk3 + if (pnames < 0) continue; + + // Only count the patches if the PNAMES come from the current file + // Otherwise they have already been counted. + if (Wads.GetLumpFile(pnames) == wadnum) + { + count += CountLumpTextures (pnames); + } + + int texlump1 = Wads.CheckNumForName ("TEXTURE1", ns_global, wadnum); + int texlump2 = Wads.CheckNumForName ("TEXTURE2", ns_global, wadnum); + + count += CountLumpTextures (texlump1) - 1; + count += CountLumpTextures (texlump2) - 1; + } + return count; +} + +//=========================================================================== +// +// R_CountLumpTextures +// +// Returns the number of patches in a PNAMES/TEXTURE1/TEXTURE2 lump. +// +//=========================================================================== + +int FTextureManager::CountLumpTextures (int lumpnum) +{ + if (lumpnum >= 0) + { + FWadLump file = Wads.OpenLumpNum (lumpnum); + DWORD numtex; + + file >> numtex; + return numtex >= 0 ? numtex : 0; + } + return 0; +} + + //========================================================================== // // operator<< diff --git a/src/textures/textures.h b/src/textures/textures.h index a756a7036..e65aae13b 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -41,8 +41,6 @@ protected: FTextureID(int num) { texnum = num; } private: int texnum; - - friend void AddTiles (void *tiles); }; class FNullTextureID : public FTextureID @@ -53,6 +51,37 @@ public: FArchive &operator<< (FArchive &arc, FTextureID &tex); +// +// Animating textures and planes +// +// [RH] Expanded to work with a Hexen ANIMDEFS lump +// + +struct FAnimDef +{ + FTextureID BasePic; + WORD NumFrames; + WORD CurFrame; + BYTE AnimType; + DWORD SwitchTime; // Time to advance to next frame + struct FAnimFrame + { + DWORD SpeedMin; // Speeds are in ms, not tics + DWORD SpeedRange; + FTextureID FramePic; + } Frames[1]; + enum + { + ANIM_Forward, + ANIM_Backward, + ANIM_OscillateUp, + ANIM_OscillateDown, + ANIM_DiscreteFrames + }; + + void SetSwitchTime (DWORD mstime); +}; + // Patches. // A patch holds one or more columns. @@ -294,18 +323,6 @@ public: return Textures[Translation[i]].Texture; } - void SetTranslation (FTextureID fromtexnum, FTextureID totexnum) - { - if ((size_t)fromtexnum.texnum < Translation.Size()) - { - if ((size_t)totexnum.texnum >= Textures.Size()) - { - totexnum.texnum = fromtexnum.texnum; - } - Translation[fromtexnum.texnum] = totexnum.texnum; - } - } - enum { TEXMAN_TryAny = 1, @@ -322,7 +339,6 @@ public: void AddTexturesLumps (int lump1, int lump2, int patcheslump); void AddGroup(int wadnum, int ns, int usetype); void AddPatches (int lumpnum); - void AddTiles (void *tileFile); void AddHiresTextures (int wadnum); void LoadTextureDefs(int wadnum, const char *lumpname); void ParseXTexture(FScanner &sc, int usetype); @@ -336,6 +352,7 @@ public: void LoadTextureX(int wadnum); void AddTexturesForWad(int wadnum); void Init(); + void DeleteAll(); // Replaces one texture with another. The new texture will be assigned // the same name, slot, and use type as the texture it is replacing. @@ -353,8 +370,38 @@ public: void WriteTexture (FArchive &arc, int picnum); int ReadTexture (FArchive &arc); + void UpdateAnimations (DWORD mstime); + int GuesstimateNumTextures (); private: + + // texture counting + int CountTexturesX (); + int CountLumpTextures (int lumpnum); + + // Build tiles + void AddTiles (void *tiles); + int CountTiles (void *tiles); + int CountBuildTiles (); + void InitBuildTiles (); + + // Animation stuff + void AddAnim (FAnimDef *anim); + void FixAnimations (); + void InitAnimated (); + void InitAnimDefs (); + void AddSimpleAnim (FTextureID picnum, int animcount, int animtype, DWORD speedmin, DWORD speedrange=0); + void AddComplexAnim (FTextureID picnum, const TArray &frames); + void ParseAnim (FScanner &sc, int usetype); + void ParseRangeAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing); + void ParsePicAnim (FScanner &sc, FTextureID picnum, int usetype, bool missing, TArray &frames); + void ParseWarp(FScanner &sc); + void ParseCameraTexture(FScanner &sc); + FTextureID ParseFramenum (FScanner &sc, FTextureID basepicnum, int usetype, bool allowMissing); + void ParseTime (FScanner &sc, DWORD &min, DWORD &max); + FTexture *Texture(FTextureID id) { return Textures[id.GetIndex()].Texture; } + void SetTranslation (FTextureID fromtexnum, FTextureID totexnum); + struct TextureHash { FTexture *Texture; @@ -366,6 +413,9 @@ private: int HashFirst[HASH_SIZE]; FTextureID DefaultTexture; TArray FirstTextureForFile; + + TArray mAnimations; + TArray BuildTileFiles; }; extern FTextureManager TexMan; diff --git a/zdoom.vcproj b/zdoom.vcproj index 834715c60..9acb2c29e 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2299,10 +2299,6 @@ - - @@ -2423,6 +2419,10 @@ + + From 24ab37613a88f3b64c4f144d87473ef079e3885b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Dec 2010 23:02:46 +0000 Subject: [PATCH 186/815] - moved switch definitions into texture manager and split all associated code off p_switches.cpp into its own file. SVN r3027 (trunk) --- src/p_setup.cpp | 1 - src/p_spec.h | 3 - src/p_switch.cpp | 431 +++++--------------------------- src/textures/anim_switches.cpp | 390 +++++++++++++++++++++++++++++ src/textures/animations.cpp | 2 +- src/textures/texturemanager.cpp | 12 + src/textures/textures.h | 29 +++ zdoom.vcproj | 4 + 8 files changed, 493 insertions(+), 379 deletions(-) create mode 100644 src/textures/anim_switches.cpp diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 9a0c001a0..7017d5c8b 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4055,7 +4055,6 @@ void P_Init () atterm (P_Shutdown); P_InitEffects (); // [RH] - P_InitSwitchList (); P_InitTerrainTypes (); P_InitKeyMessages (); R_InitSprites (); diff --git a/src/p_spec.h b/src/p_spec.h index 70b79ec09..916dd690c 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -396,9 +396,6 @@ void EV_StartLightFading (int tag, int value, int tics); bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *quest=NULL); bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno); -void P_InitSwitchList (); -void P_ProcessSwitchDef (FScanner &sc); - // // P_PLATS // diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 423633585..02adc913c 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -46,12 +46,9 @@ #include "w_wad.h" #include "tarray.h" #include "cmdlib.h" -#include "sc_man.h" #include "gi.h" -#define MAX_FRAMES 128 - static FRandom pr_switchanim ("AnimSwitch"); class DActiveButton : public DThinker @@ -76,338 +73,14 @@ protected: bool AdvanceFrame (); }; -struct FSwitchDef -{ - FTextureID PreTexture; // texture to switch from - WORD PairIndex; // switch def to use to return to PreTexture - WORD NumFrames; // # of animation frames - FSoundID Sound; // sound to play at start of animation - bool QuestPanel; // Special texture for Strife mission - struct frame // Array of times followed by array of textures - { // actual length of each array is - DWORD Time; - FTextureID Texture; - } u[1]; -}; - -static int STACK_ARGS SortSwitchDefs (const void *a, const void *b); -static FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad); -static WORD AddSwitchDef (FSwitchDef *def); - -// -// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE -// - -class DeletingSwitchArray : public TArray -{ -public: - ~DeletingSwitchArray() - { - for(unsigned i=0;iPreTexture = def2->u[0].Texture = TexMan.CheckForTexture (list_p /* .name1 */, FTexture::TEX_Wall, texflags); - def2->PreTexture = def1->u[0].Texture = TexMan.CheckForTexture (list_p + 9, FTexture::TEX_Wall, texflags); - def1->Sound = def2->Sound = 0; - def1->NumFrames = def2->NumFrames = 1; - def1->u[0].Time = def2->u[0].Time = 0; - def2->PairIndex = AddSwitchDef (def1); - def1->PairIndex = AddSwitchDef (def2); - } - } - } - - SwitchList.ShrinkToFit (); - - // Sort SwitchList for quick searching - origMap = new FSwitchDef *[SwitchList.Size ()]; - for (i = 0; i < (int)SwitchList.Size (); i++) - { - origMap[i] = SwitchList[i]; - } - - qsort (&SwitchList[0], i, sizeof(FSwitchDef *), SortSwitchDefs); - - // Correct the PairIndex of each switch def, since the sorting broke them - for (i = (int)(SwitchList.Size () - 1); i >= 0; i--) - { - FSwitchDef *def = SwitchList[i]; - if (def->PairIndex != 65535) - { - for (j = (int)(SwitchList.Size () - 1); j >= 0; j--) - { - if (SwitchList[j] == origMap[def->PairIndex]) - { - def->PairIndex = (WORD)j; - break; - } - } - } - } - - delete[] origMap; -} - -static int STACK_ARGS SortSwitchDefs (const void *a, const void *b) -{ - return (*(FSwitchDef **)a)->PreTexture - (*(FSwitchDef **)b)->PreTexture; -} - -// Parse a switch block in ANIMDEFS and add the definitions to SwitchList -void P_ProcessSwitchDef (FScanner &sc) -{ - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; - FString picname; - FSwitchDef *def1, *def2; - FTextureID picnum; - int gametype; - bool quest = false; - - def1 = def2 = NULL; - sc.MustGetString (); - if (sc.Compare ("doom")) - { - gametype = GAME_DoomChex; - sc.CheckNumber(); // skip the gamemission filter - } - else if (sc.Compare ("heretic")) - { - gametype = GAME_Heretic; - } - else if (sc.Compare ("hexen")) - { - gametype = GAME_Hexen; - } - else if (sc.Compare ("strife")) - { - gametype = GAME_Strife; - } - else if (sc.Compare ("any")) - { - gametype = GAME_Any; - } - else - { - // There is no game specified; just treat as any - //max = 240; - gametype = GAME_Any; - sc.UnGet (); - } - - sc.MustGetString (); - picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); - picname = sc.String; - while (sc.GetString ()) - { - if (sc.Compare ("quest")) - { - quest = true; - } - else if (sc.Compare ("on")) - { - if (def1 != NULL) - { - sc.ScriptError ("Switch already has an on state"); - } - def1 = ParseSwitchDef (sc, !picnum.Exists()); - } - else if (sc.Compare ("off")) - { - if (def2 != NULL) - { - sc.ScriptError ("Switch already has an off state"); - } - def2 = ParseSwitchDef (sc, !picnum.Exists()); - } - else - { - sc.UnGet (); - break; - } - } - - if (def1 == NULL || !picnum.Exists() || - (gametype != GAME_Any && !(gametype & gameinfo.gametype))) - { - if (def2 != NULL) - { - M_Free (def2); - } - if (def1 != NULL) - { - M_Free (def1); - } - return; - } - - // If the switch did not have an off state, create one that just returns - // it to the original texture without doing anything interesting - if (def2 == NULL) - { - def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); - def2->Sound = def1->Sound; - def2->NumFrames = 1; - def2->u[0].Time = 0; - def2->u[0].Texture = picnum; - } - - def1->PreTexture = picnum; - def2->PreTexture = def1->u[def1->NumFrames-1].Texture; - if (def1->PreTexture == def2->PreTexture) - { - sc.ScriptError ("The on state for switch %s must end with a texture other than %s", picname.GetChars(), picname.GetChars()); - } - def2->PairIndex = AddSwitchDef (def1); - def1->PairIndex = AddSwitchDef (def2); - def1->QuestPanel = def2->QuestPanel = quest; -} - -FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad) -{ - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; - FSwitchDef *def; - TArray frames; - FSwitchDef::frame thisframe; - FTextureID picnum; - bool bad; - FSoundID sound; - - bad = false; - - while (sc.GetString ()) - { - if (sc.Compare ("sound")) - { - if (sound != 0) - { - sc.ScriptError ("Switch state already has a sound"); - } - sc.MustGetString (); - sound = sc.String; - } - else if (sc.Compare ("pic")) - { - sc.MustGetString (); - picnum = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); - if (!picnum.Exists() && !ignoreBad) - { - //Printf ("Unknown switch texture %s\n", sc.String); - bad = true; - } - thisframe.Texture = picnum; - sc.MustGetString (); - if (sc.Compare ("tics")) - { - sc.MustGetNumber (); - thisframe.Time = sc.Number & 65535; - } - else if (sc.Compare ("rand")) - { - int min, max; - - sc.MustGetNumber (); - min = sc.Number & 65535; - sc.MustGetNumber (); - max = sc.Number & 65535; - if (min > max) - { - swapvalues (min, max); - } - thisframe.Time = ((max - min + 1) << 16) | min; - } - else - { - thisframe.Time = 0; // Shush, GCC. - sc.ScriptError ("Must specify a duration for switch frame"); - } - frames.Push(thisframe); - } - else - { - sc.UnGet (); - break; - } - } - if (frames.Size() == 0) - { - sc.ScriptError ("Switch state needs at least one frame"); - } - if (bad) - { - return NULL; - } - - def = (FSwitchDef *)M_Malloc (myoffsetof (FSwitchDef, u[0]) + frames.Size()*sizeof(frames[0])); - def->Sound = sound; - def->NumFrames = frames.Size(); - memcpy (&def->u[0], &frames[0], frames.Size() * sizeof(frames[0])); - def->PairIndex = 65535; - return def; -} - -static WORD AddSwitchDef (FSwitchDef *def) -{ - unsigned int i; - - for (i = SwitchList.Size (); i-- > 0; ) - { - if (SwitchList[i]->PreTexture == def->PreTexture) - { - free (SwitchList[i]); - SwitchList[i] = def; - return (WORD)i; - } - } - return (WORD)SwitchList.Push (def); -} +//========================================================================== // // Start a button counting down till it turns off. // [RH] Rewritten to remove MAXBUTTONS limit. // +//========================================================================== + static bool P_StartButton (side_t *side, int Where, int switchnum, fixed_t x, fixed_t y, bool useagain) { @@ -428,40 +101,14 @@ static bool P_StartButton (side_t *side, int Where, int switchnum, return true; } -static int TryFindSwitch (side_t *side, int Where) -{ - int mid, low, high; - - FTextureID texture = side->GetTexture(Where); - high = (int)(SwitchList.Size () - 1); - if (high >= 0) - { - low = 0; - do - { - mid = (high + low) / 2; - if (SwitchList[mid]->PreTexture == texture) - { - return mid; - } - else if (texture < SwitchList[mid]->PreTexture) - { - high = mid - 1; - } - else - { - low = mid + 1; - } - } while (low <= high); - } - return -1; -} - +//========================================================================== // // Checks whether a switch is reachable // This is optional because old maps can rely on being able to // use non-reachable switches. // +//========================================================================== + bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) { // Activated from an empty side -> always succeed @@ -521,15 +168,15 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) if (open.range <= 0) goto onesided; - if ((TryFindSwitch (side, side_t::top)) != -1) + if ((TexMan.FindSwitch (side->GetTexture(side_t::top))) != -1) { return (user->z + user->height >= open.top); } - else if ((TryFindSwitch (side, side_t::bottom)) != -1) + else if ((TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != -1) { return (user->z <= open.bottom); } - else if ((line->flags & (ML_3DMIDTEX)) || (TryFindSwitch (side, side_t::mid)) != -1) + else if ((line->flags & (ML_3DMIDTEX)) || (TexMan.FindSwitch (side->GetTexture(side_t::mid))) != -1) { // 3DMIDTEX lines will force a mid texture check if no switch is found on this line // to keep compatibility with Eternity's implementation. @@ -544,24 +191,29 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) } } +//========================================================================== // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // +//========================================================================== + bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *quest) { int texture; - int i, sound; + int sound; + int i; + FSwitchDef *Switch; - if ((i = TryFindSwitch (side, side_t::top)) != -1) + if ((i = TexMan.FindSwitch (side->GetTexture(side_t::top))) != -1) { texture = side_t::top; } - else if ((i = TryFindSwitch (side, side_t::bottom)) != -1) + else if ((i = TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != -1) { texture = side_t::bottom; } - else if ((i = TryFindSwitch (side, side_t::mid)) != -1) + else if ((i = TexMan.FindSwitch (side->GetTexture(side_t::mid))) != -1) { texture = side_t::mid; } @@ -573,11 +225,12 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques } return false; } + Switch = TexMan.GetSwitch(i); // EXIT SWITCH? - if (SwitchList[i]->Sound != 0) + if (Switch->Sound != 0) { - sound = SwitchList[i]->Sound; + sound = Switch->Sound; } else { @@ -599,20 +252,32 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques pt[0] = line->v1->x + (line->dx >> 1); pt[1] = line->v1->y + (line->dy >> 1); - side->SetTexture(texture, SwitchList[i]->u[0].Texture); - if (useAgain || SwitchList[i]->NumFrames > 1) + side->SetTexture(texture, Switch->u[0].Texture); + if (useAgain || Switch->NumFrames > 1) + { playsound = P_StartButton (side, texture, i, pt[0], pt[1], !!useAgain); + } else + { playsound = true; + } if (playsound) + { S_Sound (pt[0], pt[1], 0, CHAN_VOICE|CHAN_LISTENERZ, sound, 1, ATTN_STATIC); + } if (quest != NULL) { - *quest = SwitchList[i]->QuestPanel; + *quest = Switch->QuestPanel; } return true; } +//========================================================================== +// +// Button thinker +// +//========================================================================== + IMPLEMENT_CLASS (DActiveButton) DActiveButton::DActiveButton () @@ -640,6 +305,12 @@ DActiveButton::DActiveButton (side_t *side, int Where, WORD switchnum, AdvanceFrame (); } +//========================================================================== +// +// +// +//========================================================================== + void DActiveButton::Serialize (FArchive &arc) { SDWORD sidenum; @@ -656,17 +327,23 @@ void DActiveButton::Serialize (FArchive &arc) } } +//========================================================================== +// +// +// +//========================================================================== + void DActiveButton::Tick () { if (--m_Timer == 0) { - FSwitchDef *def = SwitchList[m_SwitchDef]; + FSwitchDef *def = TexMan.GetSwitch(m_SwitchDef); if (m_Frame == def->NumFrames - 1) { m_SwitchDef = def->PairIndex; if (m_SwitchDef != 65535) { - def = SwitchList[def->PairIndex]; + def = TexMan.GetSwitch(def->PairIndex); m_Frame = 65535; S_Sound (m_X, m_Y, 0, CHAN_VOICE|CHAN_LISTENERZ, def->Sound != 0 ? def->Sound : FSoundID("switches/normbutn"), @@ -690,10 +367,16 @@ void DActiveButton::Tick () } } +//========================================================================== +// +// +// +//========================================================================== + bool DActiveButton::AdvanceFrame () { bool ret = false; - FSwitchDef *def = SwitchList[m_SwitchDef]; + FSwitchDef *def = TexMan.GetSwitch(m_SwitchDef); if (++m_Frame == def->NumFrames - 1) { diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp new file mode 100644 index 000000000..cb1b5ae8a --- /dev/null +++ b/src/textures/anim_switches.cpp @@ -0,0 +1,390 @@ +/* +** p_switch.cpp +** Switch and button maintenance and animation +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "templates.h" +#include "textures/textures.h" +#include "doomdef.h" +#include "g_game.h" +#include "s_sound.h" +#include "doomstat.h" +#include "r_state.h" +#include "w_wad.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "gi.h" + +static int STACK_ARGS SortSwitchDefs (const void *a, const void *b) +{ + return (*(FSwitchDef **)a)->PreTexture - (*(FSwitchDef **)b)->PreTexture; +} + +//========================================================================== +// +// P_InitSwitchList +// Only called at game initialization. +// +// [RH] Rewritten to use a BOOM-style SWITCHES lump and remove the +// MAXSWITCHES limit. +// +//========================================================================== + +void FTextureManager::InitSwitchList () +{ + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + int lump = Wads.CheckNumForName ("SWITCHES"); + FSwitchDef **origMap; + int i, j; + + if (lump != -1) + { + FMemLump lumpdata = Wads.ReadLump (lump); + const char *alphSwitchList = (const char *)lumpdata.GetMem(); + const char *list_p; + FSwitchDef *def1, *def2; + + for (list_p = alphSwitchList; list_p[18] || list_p[19]; list_p += 20) + { + // [RH] Check for switches that aren't really switches + if (stricmp (list_p, list_p+9) == 0) + { + Printf ("Switch %s in SWITCHES has the same 'on' state\n", list_p); + continue; + } + // [RH] Skip this switch if its textures can't be found. + if (CheckForTexture (list_p /* .name1 */, FTexture::TEX_Wall, texflags).Exists() && + CheckForTexture (list_p + 9 /* .name2 */, FTexture::TEX_Wall, texflags).Exists()) + { + def1 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); + def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); + def1->PreTexture = def2->u[0].Texture = CheckForTexture (list_p /* .name1 */, FTexture::TEX_Wall, texflags); + def2->PreTexture = def1->u[0].Texture = CheckForTexture (list_p + 9, FTexture::TEX_Wall, texflags); + def1->Sound = def2->Sound = 0; + def1->NumFrames = def2->NumFrames = 1; + def1->u[0].Time = def2->u[0].Time = 0; + def2->PairIndex = AddSwitchDef (def1); + def1->PairIndex = AddSwitchDef (def2); + } + } + } + + mSwitchDefs.ShrinkToFit (); + + // Sort mSwitchDefs for quick searching + origMap = new FSwitchDef *[mSwitchDefs.Size ()]; + for (i = 0; i < (int)mSwitchDefs.Size (); i++) + { + origMap[i] = mSwitchDefs[i]; + } + + qsort (&mSwitchDefs[0], i, sizeof(FSwitchDef *), SortSwitchDefs); + + // Correct the PairIndex of each switch def, since the sorting broke them + for (i = (int)(mSwitchDefs.Size () - 1); i >= 0; i--) + { + FSwitchDef *def = mSwitchDefs[i]; + if (def->PairIndex != 65535) + { + for (j = (int)(mSwitchDefs.Size () - 1); j >= 0; j--) + { + if (mSwitchDefs[j] == origMap[def->PairIndex]) + { + def->PairIndex = (WORD)j; + break; + } + } + } + } + + delete[] origMap; +} + +//========================================================================== +// +// Parse a switch block in ANIMDEFS and add the definitions to mSwitchDefs +// +//========================================================================== + +void FTextureManager::ProcessSwitchDef (FScanner &sc) +{ + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + FString picname; + FSwitchDef *def1, *def2; + FTextureID picnum; + int gametype; + bool quest = false; + + def1 = def2 = NULL; + sc.MustGetString (); + if (sc.Compare ("doom")) + { + gametype = GAME_DoomChex; + sc.CheckNumber(); // skip the gamemission filter + } + else if (sc.Compare ("heretic")) + { + gametype = GAME_Heretic; + } + else if (sc.Compare ("hexen")) + { + gametype = GAME_Hexen; + } + else if (sc.Compare ("strife")) + { + gametype = GAME_Strife; + } + else if (sc.Compare ("any")) + { + gametype = GAME_Any; + } + else + { + // There is no game specified; just treat as any + //max = 240; + gametype = GAME_Any; + sc.UnGet (); + } + + sc.MustGetString (); + picnum = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + picname = sc.String; + while (sc.GetString ()) + { + if (sc.Compare ("quest")) + { + quest = true; + } + else if (sc.Compare ("on")) + { + if (def1 != NULL) + { + sc.ScriptError ("Switch already has an on state"); + } + def1 = ParseSwitchDef (sc, !picnum.Exists()); + } + else if (sc.Compare ("off")) + { + if (def2 != NULL) + { + sc.ScriptError ("Switch already has an off state"); + } + def2 = ParseSwitchDef (sc, !picnum.Exists()); + } + else + { + sc.UnGet (); + break; + } + } + + if (def1 == NULL || !picnum.Exists() || + (gametype != GAME_Any && !(gametype & gameinfo.gametype))) + { + if (def2 != NULL) + { + M_Free (def2); + } + if (def1 != NULL) + { + M_Free (def1); + } + return; + } + + // If the switch did not have an off state, create one that just returns + // it to the original texture without doing anything interesting + if (def2 == NULL) + { + def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); + def2->Sound = def1->Sound; + def2->NumFrames = 1; + def2->u[0].Time = 0; + def2->u[0].Texture = picnum; + } + + def1->PreTexture = picnum; + def2->PreTexture = def1->u[def1->NumFrames-1].Texture; + if (def1->PreTexture == def2->PreTexture) + { + sc.ScriptError ("The on state for switch %s must end with a texture other than %s", picname.GetChars(), picname.GetChars()); + } + def2->PairIndex = AddSwitchDef (def1); + def1->PairIndex = AddSwitchDef (def2); + def1->QuestPanel = def2->QuestPanel = quest; +} + +//========================================================================== +// +// Parse one switch frame +// +//========================================================================== + +FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) +{ + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + FSwitchDef *def; + TArray frames; + FSwitchDef::frame thisframe; + FTextureID picnum; + bool bad; + FSoundID sound; + + bad = false; + + while (sc.GetString ()) + { + if (sc.Compare ("sound")) + { + if (sound != 0) + { + sc.ScriptError ("Switch state already has a sound"); + } + sc.MustGetString (); + sound = sc.String; + } + else if (sc.Compare ("pic")) + { + sc.MustGetString (); + picnum = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + if (!picnum.Exists() && !ignoreBad) + { + //Printf ("Unknown switch texture %s\n", sc.String); + bad = true; + } + thisframe.Texture = picnum; + sc.MustGetString (); + if (sc.Compare ("tics")) + { + sc.MustGetNumber (); + thisframe.Time = sc.Number & 65535; + } + else if (sc.Compare ("rand")) + { + int min, max; + + sc.MustGetNumber (); + min = sc.Number & 65535; + sc.MustGetNumber (); + max = sc.Number & 65535; + if (min > max) + { + swapvalues (min, max); + } + thisframe.Time = ((max - min + 1) << 16) | min; + } + else + { + thisframe.Time = 0; // Shush, GCC. + sc.ScriptError ("Must specify a duration for switch frame"); + } + frames.Push(thisframe); + } + else + { + sc.UnGet (); + break; + } + } + if (frames.Size() == 0) + { + sc.ScriptError ("Switch state needs at least one frame"); + } + if (bad) + { + return NULL; + } + + def = (FSwitchDef *)M_Malloc (myoffsetof (FSwitchDef, u[0]) + frames.Size()*sizeof(frames[0])); + def->Sound = sound; + def->NumFrames = frames.Size(); + memcpy (&def->u[0], &frames[0], frames.Size() * sizeof(frames[0])); + def->PairIndex = 65535; + return def; +} + +//========================================================================== +// +// +// +//========================================================================== + +WORD FTextureManager::AddSwitchDef (FSwitchDef *def) +{ + unsigned int i; + + for (i = mSwitchDefs.Size (); i-- > 0; ) + { + if (mSwitchDefs[i]->PreTexture == def->PreTexture) + { + M_Free (mSwitchDefs[i]); + mSwitchDefs[i] = def; + return (WORD)i; + } + } + return (WORD)mSwitchDefs.Push (def); +} + +//========================================================================== +// +// +// +//========================================================================== + +int FTextureManager::FindSwitch (FTextureID texture) +{ + int mid, low, high; + + high = (int)(mSwitchDefs.Size () - 1); + if (high >= 0) + { + low = 0; + do + { + mid = (high + low) / 2; + if (mSwitchDefs[mid]->PreTexture == texture) + { + return mid; + } + else if (texture < mSwitchDefs[mid]->PreTexture) + { + high = mid - 1; + } + else + { + low = mid + 1; + } + } while (low <= high); + } + return -1; +} + diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index a4f0efe0c..7497fe4e4 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -269,7 +269,7 @@ void FTextureManager::InitAnimDefs () } else if (sc.Compare ("switch")) { - P_ProcessSwitchDef (sc); + ProcessSwitchDef (sc); } // [GRB] Added warping type 2 else if (sc.Compare ("warp") || sc.Compare ("warp2")) diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 80690a03b..6021676ff 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -104,6 +104,17 @@ void FTextureManager::DeleteAll() } mAnimations.Clear(); + for (unsigned i = 0; i < mSwitchDefs.Size(); i++) + { + if (mSwitchDefs[i] != NULL) + { + M_Free (mSwitchDefs[i]); + mSwitchDefs[i] = NULL; + } + } + mSwitchDefs.Clear(); + + for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) { delete[] BuildTileFiles[i]; @@ -978,6 +989,7 @@ void FTextureManager::Init() InitAnimated(); InitAnimDefs(); FixAnimations(); + InitSwitchList(); } //========================================================================== diff --git a/src/textures/textures.h b/src/textures/textures.h index e65aae13b..8da1a8f96 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -82,6 +82,20 @@ struct FAnimDef void SetSwitchTime (DWORD mstime); }; +struct FSwitchDef +{ + FTextureID PreTexture; // texture to switch from + WORD PairIndex; // switch def to use to return to PreTexture + WORD NumFrames; // # of animation frames + int Sound; // sound to play at start of animation. Changed to int to avoiud having to include s_sound here. + bool QuestPanel; // Special texture for Strife mission + struct frame // Array of times followed by array of textures + { // actual length of each array is + DWORD Time; + FTextureID Texture; + } u[1]; +}; + // Patches. // A patch holds one or more columns. @@ -373,6 +387,13 @@ public: void UpdateAnimations (DWORD mstime); int GuesstimateNumTextures (); + int FindSwitch (FTextureID texture); + FSwitchDef *GetSwitch (unsigned int index) + { + if (index < mSwitchDefs.Size()) return mSwitchDefs[index]; + else return NULL; + } + private: // texture counting @@ -402,6 +423,13 @@ private: FTexture *Texture(FTextureID id) { return Textures[id.GetIndex()].Texture; } void SetTranslation (FTextureID fromtexnum, FTextureID totexnum); + // Switches + + void InitSwitchList (); + void ProcessSwitchDef (FScanner &sc); + FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad); + WORD AddSwitchDef (FSwitchDef *def); + struct TextureHash { FTexture *Texture; @@ -415,6 +443,7 @@ private: TArray FirstTextureForFile; TArray mAnimations; + TArray mSwitchDefs; TArray BuildTileFiles; }; diff --git a/zdoom.vcproj b/zdoom.vcproj index 9acb2c29e..c96f7e246 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2419,6 +2419,10 @@ + + From d851040ad61d256a054376037430b4b1d0bf5327 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 07:59:38 +0000 Subject: [PATCH 187/815] - fixed GCC compilation. SVN r3028 (trunk) --- src/CMakeLists.txt | 3 ++- src/p_switch.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e5ecf22b..992e8bb14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -707,7 +707,6 @@ add_executable( zdoom WIN32 p_xlat.cpp parsecontext.cpp po_man.cpp - r_anim.cpp r_bsp.cpp r_data.cpp r_draw.cpp @@ -840,6 +839,8 @@ add_executable( zdoom WIN32 sound/music_timidity_mididevice.cpp sound/music_win_mididevice.cpp sound/music_pseudo_mididevice.cpp + textures/animations.cpp + textures/anim_switches.cpp textures/automaptexture.cpp textures/bitmap.cpp textures/buildtexture.cpp diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 02adc913c..622fd6394 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -346,7 +346,7 @@ void DActiveButton::Tick () def = TexMan.GetSwitch(def->PairIndex); m_Frame = 65535; S_Sound (m_X, m_Y, 0, CHAN_VOICE|CHAN_LISTENERZ, - def->Sound != 0 ? def->Sound : FSoundID("switches/normbutn"), + def->Sound != 0 ? FSoundID(def->Sound) : FSoundID("switches/normbutn"), 1, ATTN_STATIC); bFlippable = false; } From e257c4cb64e3d1c08bf03b019f73129394c90c67 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 08:30:12 +0000 Subject: [PATCH 188/815] - moved animated door definitions into texture manager and split all associated code off p_doors.cpp SVN r3029 (trunk) --- src/p_doors.cpp | 164 +++++++------------------------- src/p_spec.h | 15 +-- src/textures/animations.cpp | 90 +++++++++++++++++- src/textures/texturemanager.cpp | 9 ++ src/textures/textures.h | 13 +++ 5 files changed, 147 insertions(+), 144 deletions(-) diff --git a/src/p_doors.cpp b/src/p_doors.cpp index e82adbe4c..7f385242d 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -496,51 +496,12 @@ void P_SpawnDoorRaiseIn5Mins (sector_t *sec) new DDoor (sec, DDoor::doorRaiseIn5Mins, 2*FRACUNIT, TICRATE*30/7, 0); } -// Strife's animated doors. Based on Doom's unused sliding doors, but significantly improved. - -class DeletingDoorArray : public TArray -{ -public: - ~DeletingDoorArray() - { - for(unsigned i=0;iTextureFrames != NULL) - { - delete [] ani->TextureFrames; - ani->TextureFrames = NULL; - } - } - } -}; - -DeletingDoorArray DoorAnimations; - // EV_SlidingDoor : slide a door horizontally // (animate midtexture, then set noblocking line) // -// -// Return index into "DoorAnimations" array for which door type to use -// -static int P_FindSlidingDoorType (FTextureID picnum) -{ - unsigned int i; - - for (i = 0; i < DoorAnimations.Size(); ++i) - { - if (picnum == DoorAnimations[i].BaseTexture) - return i; - } - - return -1; -} - bool DAnimatedDoor::StartClosing () { - FDoorAnimation &ani = DoorAnimations[m_WhichDoorIndex]; - // CAN DOOR CLOSE? if (m_Sector->touching_thinglist != NULL) { @@ -557,9 +518,9 @@ bool DAnimatedDoor::StartClosing () m_Line1->flags |= ML_BLOCKING; m_Line2->flags |= ML_BLOCKING; - if (ani.CloseSound != NAME_None) + if (m_DoorAnim->CloseSound != NAME_None) { - SN_StartSequence (m_Sector, CHAN_CEILING, ani.CloseSound, 1); + SN_StartSequence (m_Sector, CHAN_CEILING, m_DoorAnim->CloseSound, 1); } m_Status = Closing; @@ -569,7 +530,12 @@ bool DAnimatedDoor::StartClosing () void DAnimatedDoor::Tick () { - FDoorAnimation &ani = DoorAnimations[m_WhichDoorIndex]; + if (m_DoorAnim == NULL) + { + // can only happen when a bad savegame is loaded. + Destroy(); + return; + } switch (m_Status) { @@ -581,7 +547,7 @@ void DAnimatedDoor::Tick () case Opening: if (!m_Timer--) { - if (++m_Frame >= ani.NumTextureFrames) + if (++m_Frame >= m_DoorAnim->NumTextureFrames) { // IF DOOR IS DONE OPENING... m_Line1->flags &= ~ML_BLOCKING; @@ -602,10 +568,10 @@ void DAnimatedDoor::Tick () // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... m_Timer = m_Speed; - m_Line1->sidedef[0]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line1->sidedef[1]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line2->sidedef[0]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line2->sidedef[1]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); + m_Line1->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line1->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line2->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line2->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); } } break; @@ -648,10 +614,10 @@ void DAnimatedDoor::Tick () // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... m_Timer = m_Speed; - m_Line1->sidedef[0]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line1->sidedef[1]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line2->sidedef[0]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); - m_Line2->sidedef[1]->SetTexture(side_t::mid, ani.TextureFrames[m_Frame]); + m_Line1->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line1->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line2->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); + m_Line2->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]); } } break; @@ -673,7 +639,12 @@ void DAnimatedDoor::Serialize (FArchive &arc) { Super::Serialize (arc); - FTextureID basetex = DoorAnimations[m_WhichDoorIndex].BaseTexture; + FTextureID basetex; + + if (arc.IsStoring()) + { + basetex = m_DoorAnim->BaseTexture; + } arc << m_Line1 << m_Line2 << m_Frame @@ -694,15 +665,11 @@ void DAnimatedDoor::Serialize (FArchive &arc) if (arc.IsLoading()) { - m_WhichDoorIndex = P_FindSlidingDoorType (basetex); - if (m_WhichDoorIndex == -1) - { // Oh no! The door animation doesn't exist anymore! - m_WhichDoorIndex = 0; - } + m_DoorAnim = TexMan.FindAnimatedDoor (basetex); } } -DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay) +DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim) : DMovingCeiling (sec) { fixed_t topdist; @@ -711,13 +678,7 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay) // The DMovingCeiling constructor automatically sets up an interpolation for us. // Stop it, since the ceiling is moving instantly here. StopInterpolation(); - m_WhichDoorIndex = P_FindSlidingDoorType (line->sidedef[0]->GetTexture(side_t::top)); - if (m_WhichDoorIndex < 0) - { - Printf ("EV_SlidingDoor: Textures are not defined for sliding door!"); - m_Status = Dead; - return; - } + m_DoorAnim = anim; m_Line1 = line; m_Line2 = line; @@ -756,9 +717,9 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay) m_Line2->flags |= ML_BLOCKING; m_BotDist = m_Sector->ceilingplane.d; MoveCeiling (2048*FRACUNIT, topdist, 1); - if (DoorAnimations[m_WhichDoorIndex].OpenSound != NAME_None) + if (m_DoorAnim->OpenSound != NAME_None) { - SN_StartSequence (m_Sector, CHAN_INTERIOR, DoorAnimations[m_WhichDoorIndex].OpenSound, 1); + SN_StartSequence (m_Sector, CHAN_INTERIOR, m_DoorAnim->OpenSound, 1); } } @@ -798,9 +759,10 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay) } return false; } - if (P_FindSlidingDoorType (line->sidedef[0]->GetTexture(side_t::top)) >= 0) + FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top)); + if (anim != NULL) { - new DAnimatedDoor (sec, line, speed, delay); + new DAnimatedDoor (sec, line, speed, delay, anim); return true; } return false; @@ -821,10 +783,11 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay) { continue; } - if (P_FindSlidingDoorType (line->sidedef[0]->GetTexture(side_t::top)) >= 0) + FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top)); + if (anim != NULL) { rtn = true; - new DAnimatedDoor (sec, line, speed, delay); + new DAnimatedDoor (sec, line, speed, delay, anim); break; } } @@ -832,62 +795,3 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay) return rtn; } -void P_ParseAnimatedDoor(FScanner &sc) -{ - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; - FDoorAnimation anim; - TArray frames; - bool error = false; - FTextureID v; - - sc.MustGetString(); - anim.BaseTexture = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); - - if (!anim.BaseTexture.Exists()) - { - error = true; - } - - while (sc.GetString ()) - { - if (sc.Compare ("opensound")) - { - sc.MustGetString (); - anim.OpenSound = sc.String; - } - else if (sc.Compare ("closesound")) - { - sc.MustGetString (); - anim.CloseSound = sc.String; - } - else if (sc.Compare ("pic")) - { - sc.MustGetString (); - if (IsNum (sc.String)) - { - v = anim.BaseTexture + (atoi(sc.String) - 1); - } - else - { - v = TexMan.CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); - if (!v.Exists() && anim.BaseTexture.Exists() && !error) - { - sc.ScriptError ("Unknown texture %s", sc.String); - } - frames.Push (v); - } - } - else - { - sc.UnGet (); - break; - } - } - if (!error) - { - anim.TextureFrames = new FTextureID[frames.Size()]; - memcpy (anim.TextureFrames, &frames[0], sizeof(FTextureID) * frames.Size()); - anim.NumTextureFrames = frames.Size(); - DoorAnimations.Push (anim); - } -} diff --git a/src/p_spec.h b/src/p_spec.h index 916dd690c..21427626e 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -595,23 +595,12 @@ inline FArchive &operator<< (FArchive &arc, DDoor::EVlDoor &type) return arc; } -struct FDoorAnimation -{ - FTextureID BaseTexture; - FTextureID *TextureFrames; - int NumTextureFrames; - FName OpenSound; - FName CloseSound; -}; - -void P_ParseAnimatedDoor (FScanner &sc); - class DAnimatedDoor : public DMovingCeiling { DECLARE_CLASS (DAnimatedDoor, DMovingCeiling) public: DAnimatedDoor (sector_t *sector); - DAnimatedDoor (sector_t *sector, line_t *line, int speed, int delay); + DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim); void Serialize (FArchive &arc); void Tick (); @@ -620,7 +609,7 @@ public: protected: line_t *m_Line1, *m_Line2; int m_Frame; - int m_WhichDoorIndex; + FDoorAnimation *m_DoorAnim; int m_Timer; fixed_t m_BotDist; int m_Status; diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 7497fe4e4..3a75334ae 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -282,7 +282,7 @@ void FTextureManager::InitAnimDefs () } else if (sc.Compare ("animatedDoor")) { - P_ParseAnimatedDoor (sc); + ParseAnimatedDoor (sc); } else if (sc.Compare("skyoffset")) { @@ -699,6 +699,93 @@ void FTextureManager::FixAnimations () } } +//========================================================================== +// +// ParseAnimatedDoor +// +// Parses an animated door definition +// +//========================================================================== + +void FTextureManager::ParseAnimatedDoor(FScanner &sc) +{ + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; + FDoorAnimation anim; + TArray frames; + bool error = false; + FTextureID v; + + sc.MustGetString(); + anim.BaseTexture = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + + if (!anim.BaseTexture.Exists()) + { + error = true; + } + + while (sc.GetString ()) + { + if (sc.Compare ("opensound")) + { + sc.MustGetString (); + anim.OpenSound = sc.String; + } + else if (sc.Compare ("closesound")) + { + sc.MustGetString (); + anim.CloseSound = sc.String; + } + else if (sc.Compare ("pic")) + { + sc.MustGetString (); + if (IsNum (sc.String)) + { + v = anim.BaseTexture + (atoi(sc.String) - 1); + } + else + { + v = CheckForTexture (sc.String, FTexture::TEX_Wall, texflags); + if (!v.Exists() && anim.BaseTexture.Exists() && !error) + { + sc.ScriptError ("Unknown texture %s", sc.String); + } + frames.Push (v); + } + } + else + { + sc.UnGet (); + break; + } + } + if (!error) + { + anim.TextureFrames = new FTextureID[frames.Size()]; + memcpy (anim.TextureFrames, &frames[0], sizeof(FTextureID) * frames.Size()); + anim.NumTextureFrames = frames.Size(); + mAnimatedDoors.Push (anim); + } +} + +//========================================================================== +// +// Return index into "DoorAnimations" array for which door type to use +// +//========================================================================== + +FDoorAnimation *FTextureManager::FindAnimatedDoor (FTextureID picnum) +{ + unsigned int i; + + for (i = 0; i < mAnimatedDoors.Size(); ++i) + { + if (picnum == mAnimatedDoors[i].BaseTexture) + return &mAnimatedDoors[i]; + } + + return NULL; +} + //========================================================================== // // FAnimDef :: SetSwitchTime @@ -815,3 +902,4 @@ void FTextureManager::UpdateAnimations (DWORD mstime) } } } + diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 6021676ff..fc49d07d8 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -114,6 +114,15 @@ void FTextureManager::DeleteAll() } mSwitchDefs.Clear(); + for (unsigned i = 0; i < mAnimatedDoors.Size(); i++) + { + if (mAnimatedDoors[i].TextureFrames != NULL) + { + delete mAnimatedDoors[i].TextureFrames; + mAnimatedDoors[i].TextureFrames = NULL; + } + } + mAnimatedDoors.Clear(); for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) { diff --git a/src/textures/textures.h b/src/textures/textures.h index 8da1a8f96..21a57d226 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -96,6 +96,16 @@ struct FSwitchDef } u[1]; }; +struct FDoorAnimation +{ + FTextureID BaseTexture; + FTextureID *TextureFrames; + int NumTextureFrames; + FName OpenSound; + FName CloseSound; +}; + + // Patches. // A patch holds one or more columns. @@ -393,6 +403,7 @@ public: if (index < mSwitchDefs.Size()) return mSwitchDefs[index]; else return NULL; } + FDoorAnimation *FindAnimatedDoor (FTextureID picnum); private: @@ -422,6 +433,7 @@ private: void ParseTime (FScanner &sc, DWORD &min, DWORD &max); FTexture *Texture(FTextureID id) { return Textures[id.GetIndex()].Texture; } void SetTranslation (FTextureID fromtexnum, FTextureID totexnum); + void ParseAnimatedDoor(FScanner &sc); // Switches @@ -444,6 +456,7 @@ private: TArray mAnimations; TArray mSwitchDefs; + TArray mAnimatedDoors; TArray BuildTileFiles; }; From 0715b7dfc668f138e99b598613acdd6efde5423a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 15:43:35 +0000 Subject: [PATCH 189/815] - cleaned up switch code and fixed several problems: * savegames stored an index in the switch table and performed no validation when loading a savegame. * setting of a random switch animation duration was broken. * separated the 2 values stored in the Time variable into 2 separate variables. * defining a switch with one texture already belonging to another switch could leave broken definitions in the switch table. - added function for serializing switch and door animation pointers. - bumped min. savegame versions due to changes to DButtonThinker and removed all current savegame compatibility code. SVN r3030 (trunk) --- src/d_netinfo.cpp | 8 +- src/dobject.cpp | 5 -- src/farchive.h | 4 + src/g_shared/a_quake.cpp | 12 +-- src/g_shared/a_skies.cpp | 5 -- src/p_doors.cpp | 23 +----- src/p_mobj.cpp | 109 ++++---------------------- src/p_pspr.cpp | 12 +-- src/p_saveg.cpp | 31 +------- src/p_switch.cpp | 95 ++++++++++------------ src/p_user.cpp | 34 +------- src/r_interpolate.cpp | 11 +-- src/s_advsound.cpp | 4 - src/textures/anim_switches.cpp | 139 ++++++++++++++++++++------------- src/textures/animations.cpp | 21 +++++ src/textures/textures.h | 18 ++--- src/version.h | 7 +- 17 files changed, 194 insertions(+), 344 deletions(-) diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 51467369c..def800658 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -835,11 +835,9 @@ FArchive &operator<< (FArchive &arc, userinfo_t &info) { arc.Read (&info.netname, sizeof(info.netname)); } - arc << info.team << info.aimdist << info.color << info.skin << info.gender << info.neverswitch; - if (SaveVersion >= 2193) - { - arc << info.colorset; - } + arc << info.team << info.aimdist << info.color + << info.skin << info.gender << info.neverswitch + << info.colorset; return arc; } diff --git a/src/dobject.cpp b/src/dobject.cpp index 3bd815bd6..2dd6aa1ed 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -540,11 +540,6 @@ void DObject::SerializeUserVars(FArchive &arc) DWORD count, j; int *varloc; - if (SaveVersion < 1933) - { - return; - } - symt = &GetClass()->Symbols; if (arc.IsStoring()) diff --git a/src/farchive.h b/src/farchive.h index 533b3920e..7ee05fdcd 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -291,7 +291,11 @@ template<> inline FArchive &operator<< (FArchive &arc, FFont* &font) } struct FStrifeDialogueNode; +struct FSwitchDef; +struct FDoorAnimation; template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node); +template<> FArchive &operator<< (FArchive &arc, FSwitchDef* &sw); +template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &da); diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index c53605333..725f12048 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -55,16 +55,8 @@ void DEarthquake::Serialize (FArchive &arc) { Super::Serialize (arc); arc << m_Spot << m_Intensity << m_Countdown - << m_TremorRadius << m_DamageRadius; - - if (SaveVersion >= 1912) - { - arc << m_QuakeSFX; - } - else - { - m_QuakeSFX = "world/quake"; - } + << m_TremorRadius << m_DamageRadius + << m_QuakeSFX; } //========================================================================== diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 21edd2ba1..bb13eac01 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -70,11 +70,6 @@ void ASkyViewpoint::Serialize (FArchive &arc) { Super::Serialize (arc); arc << bInSkybox << bAlways << Mate; - if (SaveVersion < 2992) - { - fixed_t eatme; - arc << eatme; - } } void ASkyViewpoint::Destroy () diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 7f385242d..557928c55 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -638,14 +638,7 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec) void DAnimatedDoor::Serialize (FArchive &arc) { Super::Serialize (arc); - - FTextureID basetex; - if (arc.IsStoring()) - { - basetex = m_DoorAnim->BaseTexture; - } - arc << m_Line1 << m_Line2 << m_Frame << m_Timer @@ -653,20 +646,8 @@ void DAnimatedDoor::Serialize (FArchive &arc) << m_Status << m_Speed << m_Delay - << basetex; - if (SaveVersion < 2336) - { - m_SetBlocking1 = m_SetBlocking2 = true; - } - else - { - arc << m_SetBlocking1 << m_SetBlocking2; - } - - if (arc.IsLoading()) - { - m_DoorAnim = TexMan.FindAnimatedDoor (basetex); - } + << m_DoorAnim + << m_SetBlocking1 << m_SetBlocking2; } DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index a73600891..bc173b9cb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -258,18 +258,9 @@ void AActor::Serialize (FArchive &arc) << args[0] << args[1] << args[2] << args[3] << args[4] << goal << waterlevel - << MinMissileChance; - if (SaveVersion >= 2826) - { - arc << SpawnFlags; - } - else - { - WORD w; - arc << w; - SpawnFlags = w; - } - arc << Inventory + << MinMissileChance + << SpawnFlags + << Inventory << InventoryID << id << FloatBobPhase @@ -281,12 +272,9 @@ void AActor::Serialize (FArchive &arc) << ActiveSound << UseSound << BounceSound - << WallBounceSound; - if (SaveVersion >= 2234) - { - arc << CrushPainSound; - } - arc << Speed + << WallBounceSound + << CrushPainSound + << Speed << FloatSpeed << Mass << PainChance @@ -313,83 +301,14 @@ void AActor::Serialize (FArchive &arc) << pushfactor << Species << Score - << Tag; - if (SaveVersion >= 1904) - { - arc << lastpush << lastbump; - } - - if (SaveVersion >= 1900) - { - arc << PainThreshold; - } - if (SaveVersion >= 1914) - { - arc << DamageFactor; - } - if (SaveVersion > 2036) - { - arc << WeaveIndexXY << WeaveIndexZ; - } - else - { - int index; - - if (SaveVersion < 2036) - { - index = special2; - } - else - { - arc << index; - } - // A_BishopMissileWeave and A_CStaffMissileSlither stored the weaveXY - // value in different parts of the index. - if (this->IsKindOf(PClass::FindClass("BishopFX"))) - { - WeaveIndexXY = index >> 16; - WeaveIndexZ = index; - } - else - { - WeaveIndexXY = index; - WeaveIndexZ = 0; - } - } - if (SaveVersion >= 2450) - { - arc << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner; - arc << PoisonDamage << PoisonDuration << PoisonPeriod; - } - - // Skip past uservar array in old savegames - if (SaveVersion < 1933) - { - int foo; - for (int i = 0; i < 10; ++i) - arc << foo; - } - - if (SaveVersion > 2560) - { - arc << ConversationRoot << Conversation; - } - else // old code which uses relative indexing. - { - int convnum; - - convnum = arc.ReadCount(); - if (GetConversation(GetClass()->TypeName) == -1) - { - Conversation = NULL; - ConversationRoot = -1; - } - else - { - // This cannot be restored anymore. - I_Error("Cannot load old savegames with active dialogues"); - } - } + << Tag + << lastpush << lastbump + << PainThreshold + << DamageFactor + << WeaveIndexXY << WeaveIndexZ + << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner + << PoisonDamage << PoisonDuration << PoisonPeriod + << ConversationRoot << Conversation; if (arc.IsLoading ()) { diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index a30e2d736..606acdb26 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -861,15 +861,7 @@ void P_MovePsprites (player_t *player) FArchive &operator<< (FArchive &arc, pspdef_t &def) { - arc << def.state << def.tics << def.sx << def.sy; - if (SaveVersion >= 2295) - { - arc << def.sprite << def.frame; - } - else - { - def.sprite = def.state->sprite; - def.frame = def.state->Frame; - } + arc << def.state << def.tics << def.sx << def.sy + << def.sprite << def.frame; return arc; } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index c7ad7125c..ebc975ff8 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -347,16 +347,8 @@ void P_SerializeWorld (FArchive &arc) << sec->interpolations[0] << sec->interpolations[1] << sec->interpolations[2] - << sec->interpolations[3]; - - if (SaveVersion < 2492) - { - sec->SeqName = NAME_None; - } - else - { - arc << sec->SeqName; - } + << sec->interpolations[3] + << sec->SeqName; sec->e->Serialize(arc); if (arc.IsStoring ()) @@ -444,15 +436,7 @@ FArchive &operator<< (FArchive &arc, sector_t::splane &p) { arc << p.xform.xoffs << p.xform.yoffs << p.xform.xscale << p.xform.yscale << p.xform.angle << p.xform.base_yoffs << p.xform.base_angle - << p.Flags << p.Light << p.Texture << p.TexZ; - if (SaveVersion >= 2992) - { - arc << p.alpha; - } - else - { - p.alpha = FRACUNIT; - } + << p.Flags << p.Light << p.Texture << p.TexZ << p.alpha; return arc; } @@ -614,15 +598,6 @@ void P_SerializeSubsectors(FArchive &arc) } else { - if (SaveVersion < 2609) - { - if (hasglnodes) - { - RecalculateDrawnSubsectors(); - } - return; - } - arc << num_verts << num_subs << num_nodes; if (num_verts != numvertexes || num_subs != numsubsectors || diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 622fd6394..6e468e41d 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -56,18 +56,19 @@ class DActiveButton : public DThinker DECLARE_CLASS (DActiveButton, DThinker) public: DActiveButton (); - DActiveButton (side_t *, int, WORD switchnum, fixed_t x, fixed_t y, bool flippable); + DActiveButton (side_t *, int, FSwitchDef *, fixed_t x, fixed_t y, bool flippable); void Serialize (FArchive &arc); void Tick (); - side_t *m_Side; - SBYTE m_Part; - WORD m_SwitchDef; - WORD m_Frame; - WORD m_Timer; - bool bFlippable; - fixed_t m_X, m_Y; // Location of timer sound + side_t *m_Side; + SBYTE m_Part; + bool bFlippable; + bool bReturning; + FSwitchDef *m_SwitchDef; + SDWORD m_Frame; + DWORD m_Timer; + fixed_t m_X, m_Y; // Location of timer sound protected: bool AdvanceFrame (); @@ -81,8 +82,7 @@ protected: // //========================================================================== -static bool P_StartButton (side_t *side, int Where, int switchnum, - fixed_t x, fixed_t y, bool useagain) +static bool P_StartButton (side_t *side, int Where, FSwitchDef *Switch, fixed_t x, fixed_t y, bool useagain) { DActiveButton *button; TThinkerIterator iterator; @@ -97,7 +97,7 @@ static bool P_StartButton (side_t *side, int Where, int switchnum, } } - new DActiveButton (side, Where, switchnum, x, y, useagain); + new DActiveButton (side, Where, Switch, x, y, useagain); return true; } @@ -168,15 +168,15 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) if (open.range <= 0) goto onesided; - if ((TexMan.FindSwitch (side->GetTexture(side_t::top))) != -1) + if ((TexMan.FindSwitch (side->GetTexture(side_t::top))) != NULL) { return (user->z + user->height >= open.top); } - else if ((TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != -1) + else if ((TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != NULL) { return (user->z <= open.bottom); } - else if ((line->flags & (ML_3DMIDTEX)) || (TexMan.FindSwitch (side->GetTexture(side_t::mid))) != -1) + else if ((line->flags & (ML_3DMIDTEX)) || (TexMan.FindSwitch (side->GetTexture(side_t::mid))) != NULL) { // 3DMIDTEX lines will force a mid texture check if no switch is found on this line // to keep compatibility with Eternity's implementation. @@ -202,18 +202,17 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques { int texture; int sound; - int i; FSwitchDef *Switch; - if ((i = TexMan.FindSwitch (side->GetTexture(side_t::top))) != -1) + if ((Switch = TexMan.FindSwitch (side->GetTexture(side_t::top))) != NULL) { texture = side_t::top; } - else if ((i = TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != -1) + else if ((Switch = TexMan.FindSwitch (side->GetTexture(side_t::bottom))) != NULL) { texture = side_t::bottom; } - else if ((i = TexMan.FindSwitch (side->GetTexture(side_t::mid))) != -1) + else if ((Switch = TexMan.FindSwitch (side->GetTexture(side_t::mid))) != NULL) { texture = side_t::mid; } @@ -225,7 +224,6 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques } return false; } - Switch = TexMan.GetSwitch(i); // EXIT SWITCH? if (Switch->Sound != 0) @@ -252,10 +250,10 @@ bool P_ChangeSwitchTexture (side_t *side, int useAgain, BYTE special, bool *ques pt[0] = line->v1->x + (line->dx >> 1); pt[1] = line->v1->y + (line->dy >> 1); - side->SetTexture(texture, Switch->u[0].Texture); + side->SetTexture(texture, Switch->frames[0].Texture); if (useAgain || Switch->NumFrames > 1) { - playsound = P_StartButton (side, texture, i, pt[0], pt[1], !!useAgain); + playsound = P_StartButton (side, texture, Switch, pt[0], pt[1], !!useAgain); } else { @@ -289,9 +287,11 @@ DActiveButton::DActiveButton () m_X = 0; m_Y = 0; bFlippable = false; + bReturning = false; + m_Frame = 0; } -DActiveButton::DActiveButton (side_t *side, int Where, WORD switchnum, +DActiveButton::DActiveButton (side_t *side, int Where, FSwitchDef *Switch, fixed_t x, fixed_t y, bool useagain) { m_Side = side; @@ -299,9 +299,10 @@ DActiveButton::DActiveButton (side_t *side, int Where, WORD switchnum, m_X = x; m_Y = y; bFlippable = useagain; + bReturning = false; - m_SwitchDef = switchnum; - m_Frame = 65535; + m_SwitchDef = Switch; + m_Frame = -1; AdvanceFrame (); } @@ -313,18 +314,8 @@ DActiveButton::DActiveButton (side_t *side, int Where, WORD switchnum, void DActiveButton::Serialize (FArchive &arc) { - SDWORD sidenum; - Super::Serialize (arc); - if (arc.IsStoring ()) - { - sidenum = m_Side ? SDWORD(m_Side - sides) : -1; - } - arc << sidenum << m_Part << m_SwitchDef << m_Frame << m_Timer << bFlippable << m_X << m_Y; - if (arc.IsLoading ()) - { - m_Side = sidenum >= 0 ? sides + sidenum : NULL; - } + arc << m_Side << m_Part << m_SwitchDef << m_Frame << m_Timer << bFlippable << m_X << m_Y << bReturning; } //========================================================================== @@ -335,16 +326,23 @@ void DActiveButton::Serialize (FArchive &arc) void DActiveButton::Tick () { + if (m_SwitchDef == NULL) + { + // We lost our definition due to a bad savegame. + Destroy(); + return; + } + + FSwitchDef *def = bReturning? m_SwitchDef->PairDef : m_SwitchDef; if (--m_Timer == 0) { - FSwitchDef *def = TexMan.GetSwitch(m_SwitchDef); if (m_Frame == def->NumFrames - 1) { - m_SwitchDef = def->PairIndex; - if (m_SwitchDef != 65535) + bReturning = true; + def = m_SwitchDef->PairDef; + if (def != NULL) { - def = TexMan.GetSwitch(def->PairIndex); - m_Frame = 65535; + m_Frame = -1; S_Sound (m_X, m_Y, 0, CHAN_VOICE|CHAN_LISTENERZ, def->Sound != 0 ? FSoundID(def->Sound) : FSoundID("switches/normbutn"), 1, ATTN_STATIC); @@ -358,7 +356,7 @@ void DActiveButton::Tick () } bool killme = AdvanceFrame (); - m_Side->SetTexture(m_Part, def->u[m_Frame].Texture); + m_Side->SetTexture(m_Part, def->frames[m_Frame].Texture); if (killme) { @@ -376,7 +374,7 @@ void DActiveButton::Tick () bool DActiveButton::AdvanceFrame () { bool ret = false; - FSwitchDef *def = TexMan.GetSwitch(m_SwitchDef); + FSwitchDef *def = bReturning? m_SwitchDef->PairDef : m_SwitchDef; if (++m_Frame == def->NumFrames - 1) { @@ -391,17 +389,10 @@ bool DActiveButton::AdvanceFrame () } else { - if (def->u[m_Frame].Time & 0xffff0000) + m_Timer = def->frames[m_Frame].TimeMin; + if (def->frames[m_Frame].TimeRnd != 0) { - int t = pr_switchanim(); - - m_Timer = (WORD)((((t | (pr_switchanim() << 8)) - % def->u[m_Frame].Time) >> 16) - + (def->u[m_Frame].Time & 0xffff)); - } - else - { - m_Timer = (WORD)def->u[m_Frame].Time; + m_Timer += pr_switchanim(def->frames[m_Frame].TimeRnd); } } return ret; diff --git a/src/p_user.cpp b/src/p_user.cpp index 836b665d6..a9c04ad49 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -432,10 +432,6 @@ void APlayerPawn::Serialize (FArchive &arc) << MorphWeapon << DamageFade << PlayerFlags; - if (SaveVersion < 2435) - { - DamageFade.a = 255; - } } //=========================================================================== @@ -2554,33 +2550,9 @@ void player_t::Serialize (FArchive &arc) << poisoncount << poisoner << attacker - << extralight; - if (SaveVersion < 1858) - { - int fixedmap; - arc << fixedmap; - fixedcolormap = NOFIXEDCOLORMAP; - fixedlightlevel = -1; - if (fixedmap >= NUMCOLORMAPS) - { - fixedcolormap = fixedmap - NUMCOLORMAPS; - } - else if (fixedmap > 0) - { - fixedlightlevel = fixedmap; - } - } - else if (SaveVersion < 1893) - { - int ll; - arc << fixedcolormap << ll; - fixedlightlevel = ll; - } - else - { - arc << fixedcolormap << fixedlightlevel; - } - arc << morphTics + << extralight + << fixedcolormap << fixedlightlevel + << morphTics << MorphedPlayerClass << MorphStyle << MorphExitFlash diff --git a/src/r_interpolate.cpp b/src/r_interpolate.cpp index f2fba2f8c..f145347a5 100644 --- a/src/r_interpolate.cpp +++ b/src/r_interpolate.cpp @@ -826,16 +826,7 @@ void DPolyobjInterpolation::Serialize(FArchive &arc) arc << po << oldverts; poly = polyobjs + po; - if (SaveVersion >= 2448) - { - arc << oldcx << oldcy; - } - else - { - // This will glitch if an old savegame is loaded but at least it'll allow loading it. - oldcx = poly->CenterSpot.x; - oldcy = poly->CenterSpot.y; - } + arc << oldcx << oldcy; if (arc.IsLoading()) bakverts.Resize(oldverts.Size()); } diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c194d7448..32c2f3222 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2016,10 +2016,6 @@ void AAmbientSound::Serialize (FArchive &arc) { Super::Serialize (arc); arc << bActive << NextCheck; - if (SaveVersion < 2798) - { - NextCheck += level.maptime; - } } //========================================================================== diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp index cb1b5ae8a..ca8253203 100644 --- a/src/textures/anim_switches.cpp +++ b/src/textures/anim_switches.cpp @@ -63,8 +63,6 @@ void FTextureManager::InitSwitchList () { const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; int lump = Wads.CheckNumForName ("SWITCHES"); - FSwitchDef **origMap; - int i, j; if (lump != -1) { @@ -87,46 +85,19 @@ void FTextureManager::InitSwitchList () { def1 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); - def1->PreTexture = def2->u[0].Texture = CheckForTexture (list_p /* .name1 */, FTexture::TEX_Wall, texflags); - def2->PreTexture = def1->u[0].Texture = CheckForTexture (list_p + 9, FTexture::TEX_Wall, texflags); + def1->PreTexture = def2->frames[0].Texture = CheckForTexture (list_p /* .name1 */, FTexture::TEX_Wall, texflags); + def2->PreTexture = def1->frames[0].Texture = CheckForTexture (list_p + 9, FTexture::TEX_Wall, texflags); def1->Sound = def2->Sound = 0; def1->NumFrames = def2->NumFrames = 1; - def1->u[0].Time = def2->u[0].Time = 0; - def2->PairIndex = AddSwitchDef (def1); - def1->PairIndex = AddSwitchDef (def2); + def1->frames[0].TimeMin = def2->frames[0].TimeMin = 0; + def1->frames[0].TimeRnd = def2->frames[0].TimeRnd = 0; + AddSwitchPair(def1, def2); } } } mSwitchDefs.ShrinkToFit (); - - // Sort mSwitchDefs for quick searching - origMap = new FSwitchDef *[mSwitchDefs.Size ()]; - for (i = 0; i < (int)mSwitchDefs.Size (); i++) - { - origMap[i] = mSwitchDefs[i]; - } - - qsort (&mSwitchDefs[0], i, sizeof(FSwitchDef *), SortSwitchDefs); - - // Correct the PairIndex of each switch def, since the sorting broke them - for (i = (int)(mSwitchDefs.Size () - 1); i >= 0; i--) - { - FSwitchDef *def = mSwitchDefs[i]; - if (def->PairIndex != 65535) - { - for (j = (int)(mSwitchDefs.Size () - 1); j >= 0; j--) - { - if (mSwitchDefs[j] == origMap[def->PairIndex]) - { - def->PairIndex = (WORD)j; - break; - } - } - } - } - - delete[] origMap; + qsort (&mSwitchDefs[0], mSwitchDefs.Size(), sizeof(FSwitchDef *), SortSwitchDefs); } //========================================================================== @@ -228,18 +199,18 @@ void FTextureManager::ProcessSwitchDef (FScanner &sc) def2 = (FSwitchDef *)M_Malloc (sizeof(FSwitchDef)); def2->Sound = def1->Sound; def2->NumFrames = 1; - def2->u[0].Time = 0; - def2->u[0].Texture = picnum; + def2->frames[0].TimeMin = 0; + def2->frames[0].TimeRnd = 0; + def2->frames[0].Texture = picnum; } def1->PreTexture = picnum; - def2->PreTexture = def1->u[def1->NumFrames-1].Texture; + def2->PreTexture = def1->frames[def1->NumFrames-1].Texture; if (def1->PreTexture == def2->PreTexture) { sc.ScriptError ("The on state for switch %s must end with a texture other than %s", picname.GetChars(), picname.GetChars()); } - def2->PairIndex = AddSwitchDef (def1); - def1->PairIndex = AddSwitchDef (def2); + AddSwitchPair(def1, def2); def1->QuestPanel = def2->QuestPanel = quest; } @@ -286,7 +257,8 @@ FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) if (sc.Compare ("tics")) { sc.MustGetNumber (); - thisframe.Time = sc.Number & 65535; + thisframe.TimeMin = sc.Number & 65535; + thisframe.TimeRnd = 0; } else if (sc.Compare ("rand")) { @@ -300,11 +272,13 @@ FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) { swapvalues (min, max); } - thisframe.Time = ((max - min + 1) << 16) | min; + thisframe.TimeMin = min; + thisframe.TimeRnd = (max - min + 1); } else { - thisframe.Time = 0; // Shush, GCC. + thisframe.TimeMin = 0; // Shush, GCC. + thisframe.TimeRnd = 0; sc.ScriptError ("Must specify a duration for switch frame"); } frames.Push(thisframe); @@ -324,11 +298,11 @@ FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) return NULL; } - def = (FSwitchDef *)M_Malloc (myoffsetof (FSwitchDef, u[0]) + frames.Size()*sizeof(frames[0])); + def = (FSwitchDef *)M_Malloc (myoffsetof (FSwitchDef, frames[0]) + frames.Size()*sizeof(frames[0])); def->Sound = sound; def->NumFrames = frames.Size(); - memcpy (&def->u[0], &frames[0], frames.Size() * sizeof(frames[0])); - def->PairIndex = 65535; + memcpy (&def->frames[0], &frames[0], frames.Size() * sizeof(frames[0])); + def->PairDef = NULL; return def; } @@ -338,20 +312,54 @@ FSwitchDef *FTextureManager::ParseSwitchDef (FScanner &sc, bool ignoreBad) // //========================================================================== -WORD FTextureManager::AddSwitchDef (FSwitchDef *def) +void FTextureManager::AddSwitchPair (FSwitchDef *def1, FSwitchDef *def2) { unsigned int i; + FSwitchDef *sw1 = NULL; + FSwitchDef *sw2 = NULL; + unsigned int index1 = -1, index2 = -1; for (i = mSwitchDefs.Size (); i-- > 0; ) { - if (mSwitchDefs[i]->PreTexture == def->PreTexture) + if (mSwitchDefs[i]->PreTexture == def1->PreTexture) { - M_Free (mSwitchDefs[i]); - mSwitchDefs[i] = def; - return (WORD)i; + index1 = i; + sw1 = mSwitchDefs[index1]; + if (index2 != -1) break; + } + if (mSwitchDefs[i]->PreTexture == def2->PreTexture) + { + index2 = i; + sw2 = mSwitchDefs[index2]; + if (index1 != -1) break; } } - return (WORD)mSwitchDefs.Push (def); + + def1->PairDef = def2; + def2->PairDef = def1; + + if (sw1 != NULL && sw2 != NULL && sw1->PairDef == sw2 && sw2->PairDef == sw1) + { + //We are replacing an existing pair so we can safely delete the old definitions + M_Free(sw1); + M_Free(sw2); + mSwitchDefs[index1] = def1; + mSwitchDefs[index2] = def2; + } + else + { + // This new switch will not or only partially replace an existing pair. + // We should not break up an old pair if the new one only redefined one + // of the two textures. These paired definitions will only be used + // as the return animation so their names don't matter. Better clear them to be safe. + if (sw1 != NULL) sw1->PreTexture.SetInvalid(); + if (sw2 != NULL) sw2->PreTexture.SetInvalid(); + sw1 = NULL; + sw2 = NULL; + unsigned int pos = mSwitchDefs.Reserve(2); + mSwitchDefs[pos] = def1; + mSwitchDefs[pos+1] = def2; + } } //========================================================================== @@ -360,7 +368,7 @@ WORD FTextureManager::AddSwitchDef (FSwitchDef *def) // //========================================================================== -int FTextureManager::FindSwitch (FTextureID texture) +FSwitchDef *FTextureManager::FindSwitch (FTextureID texture) { int mid, low, high; @@ -373,7 +381,7 @@ int FTextureManager::FindSwitch (FTextureID texture) mid = (high + low) / 2; if (mSwitchDefs[mid]->PreTexture == texture) { - return mid; + return mSwitchDefs[mid]; } else if (texture < mSwitchDefs[mid]->PreTexture) { @@ -385,6 +393,27 @@ int FTextureManager::FindSwitch (FTextureID texture) } } while (low <= high); } - return -1; + return NULL; +} + +//========================================================================== +// +// operator<< +// +//========================================================================== + +template<> FArchive &operator<< (FArchive &arc, FSwitchDef* &Switch) +{ + if (arc.IsStoring()) + { + arc << Switch->PreTexture; + } + else + { + FTextureID tex; + arc << tex; + Switch = TexMan.FindSwitch(tex); + } + return arc; } diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 3a75334ae..65405e5c8 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -903,3 +903,24 @@ void FTextureManager::UpdateAnimations (DWORD mstime) } } +//========================================================================== +// +// operator<< +// +//========================================================================== + +template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &Doorani) +{ + if (arc.IsStoring()) + { + arc << Doorani->BaseTexture; + } + else + { + FTextureID tex; + arc << tex; + Doorani = TexMan.FindAnimatedDoor(tex); + } + return arc; +} + diff --git a/src/textures/textures.h b/src/textures/textures.h index 21a57d226..4937e02ae 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -85,15 +85,16 @@ struct FAnimDef struct FSwitchDef { FTextureID PreTexture; // texture to switch from - WORD PairIndex; // switch def to use to return to PreTexture + FSwitchDef *PairDef; // switch def to use to return to PreTexture WORD NumFrames; // # of animation frames - int Sound; // sound to play at start of animation. Changed to int to avoiud having to include s_sound here. bool QuestPanel; // Special texture for Strife mission + int Sound; // sound to play at start of animation. Changed to int to avoiud having to include s_sound here. struct frame // Array of times followed by array of textures { // actual length of each array is - DWORD Time; + WORD TimeMin; + WORD TimeRnd; FTextureID Texture; - } u[1]; + } frames[1]; }; struct FDoorAnimation @@ -397,12 +398,7 @@ public: void UpdateAnimations (DWORD mstime); int GuesstimateNumTextures (); - int FindSwitch (FTextureID texture); - FSwitchDef *GetSwitch (unsigned int index) - { - if (index < mSwitchDefs.Size()) return mSwitchDefs[index]; - else return NULL; - } + FSwitchDef *FindSwitch (FTextureID texture); FDoorAnimation *FindAnimatedDoor (FTextureID picnum); private: @@ -440,7 +436,7 @@ private: void InitSwitchList (); void ProcessSwitchDef (FScanner &sc); FSwitchDef *ParseSwitchDef (FScanner &sc, bool ignoreBad); - WORD AddSwitchDef (FSwitchDef *def); + void AddSwitchPair (FSwitchDef *def1, FSwitchDef *def2); struct TextureHash { diff --git a/src/version.h b/src/version.h index 6a99a02ad..32cd44b39 100644 --- a/src/version.h +++ b/src/version.h @@ -75,7 +75,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 1848 +#define MINSAVEVER 3030 #if SVN_REVISION_NUMBER < MINSAVEVER // If we don't know the current revision write something very high to ensure that @@ -85,8 +85,11 @@ static inline const char *MakeSaveSig() { static char foo[] = { 'Z','D','O','O','M','S','A','V','E', +#if SAVEVER > 99999 + '0' + (SAVEVER / 100000), +#endif #if SAVEVER > 9999 - '0' + (SAVEVER / 10000), + '0' + ((SAVEVER / 10000) % 10), #endif #if SAVEVER > 999 '0' + ((SAVEVER / 1000) % 10), From 572bc4620aa8d0d3bc7769e5d22a9e8a0ce7cf45 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 17:54:10 +0000 Subject: [PATCH 190/815] - changed automap initialization so that static data only gets initialized once upon startup instead of each time a level starts. - initialize AUTOPAGE only once when the level starts, not each time the automap is switched on. SVN r3031 (trunk) --- src/am_map.cpp | 68 ++++++++++++++++----------------- src/am_map.h | 3 ++ src/d_main.cpp | 1 + src/textures/texturemanager.cpp | 3 -- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index cf8876157..d3fbc1140 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -64,6 +64,12 @@ #include "po_man.h" #include "a_keys.h" +//============================================================================= +// +// Automap colors +// +//============================================================================= + struct AMColor { int Index; @@ -119,6 +125,12 @@ static BYTE RavenPaletteVals[11*3] = 0, 0, 0, 0, 0, 0, }; +//============================================================================= +// +// globals +// +//============================================================================= + #define MAPBITS 12 #define MapDiv SafeDivScale12 #define MapMul MulScale12 @@ -287,28 +299,28 @@ struct islope_t // A line drawing of the player pointing right, // starting from the middle. // -TArray MapArrow; -TArray CheatMapArrow; -TArray CheatKey; +static TArray MapArrow; +static TArray CheatMapArrow; +static TArray CheatKey; #define R (MAPUNIT) // [RH] Avoid lots of warnings without compiler-specific #pragmas #define L(a,b,c,d) { {(fixed_t)((a)*R),(fixed_t)((b)*R)}, {(fixed_t)((c)*R),(fixed_t)((d)*R)} } -mline_t triangle_guy[] = { +static mline_t triangle_guy[] = { L (-.867,-.5, .867,-.5), L (.867,-.5, 0,1), L (0,1, -.867,-.5) }; #define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) -mline_t thintriangle_guy[] = { +static mline_t thintriangle_guy[] = { L (-.5,-.7, 1,0), L (1,0, -.5,.7), L (-.5,.7, -.5,-.7) }; #define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) -mline_t square_guy[] = { +static mline_t square_guy[] = { L (0,1,1,0), L (1,0,0,-1), L (0,-1,-1,0), @@ -332,8 +344,6 @@ CUSTOM_CVAR (Int, am_cheat, 0, 0) static int grid = 0; -static int leveljuststarted = 1; // kluge until AM_LevelInit() is called - bool automapactive = false; // location of window on screen @@ -515,16 +525,26 @@ void AM_ParseArrow(TArray &Arrow, const char *lumpname) } } -void AM_InitArrows() +void AM_StaticInit() { - MapArrow.Clear(); CheatMapArrow.Clear(); + CheatKey.Clear(); if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); AM_ParseArrow(CheatKey, "maparrows/key.txt"); if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); + + char namebuf[9]; + + for (int i = 0; i < 10; i++) + { + mysnprintf (namebuf, countof(namebuf), "AMMNUM%d", i); + marknums[i] = TexMan.CheckForTexture (namebuf, FTexture::TEX_MiscPatch); + } + markpointnum = 0; + mapback.SetInvalid(); } //============================================================================= @@ -1004,28 +1024,6 @@ static void AM_initColors (bool overlayed) lastpal = palette; } -//============================================================================= -// -// -// -//============================================================================= - -void AM_loadPics () -{ - int i; - char namebuf[9]; - - for (i = 0; i < 10; i++) - { - mysnprintf (namebuf, countof(namebuf), "AMMNUM%d", i); - marknums[i] = TexMan.CheckForTexture (namebuf, FTexture::TEX_MiscPatch); - } - - const char *autopage = level.info->mapbg[0] == 0? "AUTOPAGE" : (const char*)&level.info->mapbg[0]; - - mapback = TexMan.CheckForTexture(autopage, FTexture::TEX_MiscPatch); -} - //============================================================================= // // @@ -1048,9 +1046,8 @@ bool AM_clearMarks () void AM_LevelInit () { - if (MapArrow.Size() == 0) AM_InitArrows(); - - leveljuststarted = 0; + const char *autopage = level.info->mapbg[0] == 0? "AUTOPAGE" : (const char*)&level.info->mapbg[0]; + mapback = TexMan.CheckForTexture(autopage, FTexture::TEX_MiscPatch); AM_clearMarks(); @@ -1088,7 +1085,6 @@ void AM_Start () if (!stopped) AM_Stop(); stopped = false; AM_initVariables(); - AM_loadPics(); } diff --git a/src/am_map.h b/src/am_map.h index 20da6b96b..e4fe50856 100644 --- a/src/am_map.h +++ b/src/am_map.h @@ -25,6 +25,9 @@ struct event_t; class FArchive; + +void AM_StaticInit(); + // Called by main loop. bool AM_Responder (event_t* ev, bool last); diff --git a/src/d_main.cpp b/src/d_main.cpp index fd03a5c4a..d1fb87382 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2226,6 +2226,7 @@ void D_DoomMain (void) Printf ("P_Init: Init Playloop state.\n"); StartScreen->LoadingStatus ("Init game engine", 0x3f); + AM_StaticInit(); P_Init (); P_SetupWeapons_ntohton(); diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index fc49d07d8..72eb59f29 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -48,9 +48,6 @@ #include "cmdlib.h" #include "g_level.h" -extern void R_InitBuildTiles(); - - FTextureManager TexMan; //========================================================================== From ececec1c65ec1cab0ffed65cee34c6a44209c50d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 21:09:16 +0000 Subject: [PATCH 191/815] - init bot specific actor properties righr after parsing DECORATE, not when spawning the first bot (which is too late.) SVN r3032 (trunk) --- src/b_bot.cpp | 5 ----- src/b_game.cpp | 3 --- src/info.cpp | 2 ++ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index cee4086ae..eaa71c0e1 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -105,11 +105,6 @@ FArchive &operator<< (FArchive &arc, botskill_t &skill) // This is intentionally not in the weapon definition anymore. void InitBotStuff() { - static bool done = false; - - if (done) return; - done = true; - static struct BotInit { const char *type; diff --git a/src/b_game.cpp b/src/b_game.cpp index f517f27ee..2ff74a285 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -63,8 +63,6 @@ Everything that is changed is marked (maybe commented) with "Added by MC" static FRandom pr_botspawn ("BotSpawn"); -void InitBotStuff(); - //Externs FCajunMaster bglobal; @@ -321,7 +319,6 @@ bool FCajunMaster::SpawnBot (const char *name, int color) waitingforspawn[playernumber] = true; - InitBotStuff(); Net_WriteByte (DEM_ADDBOT); Net_WriteByte (playernumber); { diff --git a/src/info.cpp b/src/info.cpp index 1b49ce7dd..988c175df 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -52,6 +52,7 @@ #include "g_level.h" extern void LoadActors (); +extern void InitBotStuff(); //========================================================================== @@ -121,6 +122,7 @@ void FActorInfo::StaticInit () Printf ("LoadActors: Load actor definitions.\n"); LoadActors (); + InitBotStuff(); } //========================================================================== From 99b5fe29f74fc3ea90eeb0d235a2403f887682f3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Dec 2010 23:52:00 +0000 Subject: [PATCH 192/815] - cleaned up D_DoomMain a little. It's still far too large though. - added explicit initialization of console background texture instead of letting C_InitConsole doing it as needed. - added 'clearaliases' CCMD. SVN r3033 (trunk) --- src/c_console.cpp | 44 ++++---- src/c_console.h | 1 + src/c_dispatch.cpp | 24 +++++ src/c_dispatch.h | 1 + src/compatibility.cpp | 3 + src/d_main.cpp | 238 ++++++++++++++++++++++++------------------ 6 files changed, 188 insertions(+), 123 deletions(-) diff --git a/src/c_console.cpp b/src/c_console.cpp index 324fad660..c19ff3f2a 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -94,7 +94,7 @@ extern FBaseCVar *CVars; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; int ConCols, PhysRows; -bool vidactive = false, gotconback = false; +bool vidactive = false; bool cursoron = false; int ConBottom, ConScroll, RowAdjust; int CursorTicker; @@ -232,7 +232,7 @@ CUSTOM_CVAR (Int, msgmidcolor2, 4, CVAR_ARCHIVE) static void maybedrawnow (bool tick, bool force) { // FIXME: Does not work right with hw2d - if (ConsoleDrawing || !gotconback || screen == NULL || screen->IsLocked () || screen->Accel2D) + if (ConsoleDrawing || screen == NULL || screen->IsLocked () || screen->Accel2D || ConFont == NULL) { return; } @@ -297,32 +297,28 @@ void DequeueConsoleText () EnqueuedTextTail = &EnqueuedText; } +void C_InitConback() +{ + conback = TexMan.CheckForTexture ("CONBACK", FTexture::TEX_MiscPatch); + + if (!conback.isValid()) + { + conback = TexMan.GetTexture (gameinfo.titlePage, FTexture::TEX_MiscPatch); + conshade = MAKEARGB(175,0,0,0); + conline = true; + } + else + { + conshade = 0; + conline = false; + } +} + void C_InitConsole (int width, int height, bool ingame) { - if ( (vidactive = ingame) ) - { - if (!gotconback) - { - conback = TexMan.CheckForTexture ("CONBACK", FTexture::TEX_MiscPatch); - - if (!conback.isValid()) - { - conback = TexMan.GetTexture (gameinfo.titlePage, FTexture::TEX_MiscPatch); - conshade = MAKEARGB(175,0,0,0); - conline = true; - } - else - { - conshade = 0; - conline = false; - } - - gotconback = true; - } - } - int cwidth, cheight; + vidactive = ingame; if (ConFont != NULL) { cwidth = ConFont->GetCharWidth ('M'); diff --git a/src/c_console.h b/src/c_console.h index e1434c051..8560d6779 100644 --- a/src/c_console.h +++ b/src/c_console.h @@ -53,6 +53,7 @@ extern int ConBottom; // Initialize the console void C_InitConsole (int width, int height, bool ingame); void C_DeinitConsole (); +void C_InitConback(); // Adjust the console for a new screen mode void C_NewModeAdjust (void); diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 413b8d64c..1a2b31588 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -1211,6 +1211,30 @@ void C_ArchiveAliases (FConfigFile *f) } } +void C_ClearAliases () +{ + int bucket; + FConsoleCommand *alias; + + for (bucket = 0; bucket < FConsoleCommand::HASH_SIZE; bucket++) + { + alias = Commands[bucket]; + while (alias) + { + FConsoleCommand *next = alias->m_Next; + if (alias->IsAlias()) + static_cast(alias)->SafeDelete(); + alias = next; + } + } +} + +CCMD(clearaliases) +{ + C_ClearAliases(); +} + + // This is called only by the ini parser. void C_SetAlias (const char *name, const char *cmd) { diff --git a/src/c_dispatch.h b/src/c_dispatch.h index d93709c90..ac8685855 100644 --- a/src/c_dispatch.h +++ b/src/c_dispatch.h @@ -58,6 +58,7 @@ int C_ExecFile (const char *cmd, bool usePullin); void C_ArchiveAliases (FConfigFile *f); void C_SetAlias (const char *name, const char *cmd); +void C_ClearAliases (); // build a single string out of multiple strings FString BuildString (int argc, char **argv); diff --git a/src/compatibility.cpp b/src/compatibility.cpp index bfd5ae325..cd74f5e34 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -143,6 +143,9 @@ void ParseCompatibility() int i, x; unsigned int j; + BCompatMap.Clear(); + CompatParams.Clear(); + // The contents of this file are not cumulative, as it should not // be present in user-distributed maps. FScanner sc(Wads.GetNumForFullName("compatibility.txt")); diff --git a/src/d_main.cpp b/src/d_main.cpp index d1fb87382..ffe4afdbd 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -926,6 +926,7 @@ void D_DoomLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; + Page = Advisory = NULL; vid_cursor.Callback(); @@ -1818,20 +1819,12 @@ static void SetMapxxFlag() //========================================================================== // -// D_DoomMain +// Initialize // //========================================================================== -void D_DoomMain (void) +static void D_DoomInit() { - int p, flags; - const char *v; - const char *wad; - DArgs *execFiles; - TArray pwads; - FString *args; - int argcount; - // Set the FPU precision to 53 significant bits. This is the default // for Visual C++, but not for GCC, so some slight math variances // might crop up if we leave it alone. @@ -1859,7 +1852,6 @@ void D_DoomMain (void) Args->CollectFiles("-playdemo", ".lmp"); Args->CollectFiles("-file", NULL); // anything left goes after -file - PClass::StaticInit (); atterm (C_DeinitConsole); gamestate = GS_STARTUP; @@ -1872,30 +1864,16 @@ void D_DoomMain (void) Printf ("M_LoadDefaults: Load system defaults.\n"); M_LoadDefaults (); // load before initing other systems - // [RH] Make sure zdoom.pk3 is always loaded, - // as it contains magic stuff we need. +} - wad = BaseFileSearch (BASEWAD, NULL, true); - if (wad == NULL) - { - I_FatalError ("Cannot find " BASEWAD); - } - FString basewad = wad; - - // Load zdoom.pk3 alone so that we can get access to the internal gameinfos before - // the IWAD is known. - - GetCmdLineFiles(pwads); - FString iwad = CheckGameInfo(pwads); - - FIWadManager *iwad_man = new FIWadManager; - const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad); - gameinfo.gametype = iwad_info->gametype; - gameinfo.flags = iwad_info->flags; - gameinfo.ConfigName = iwad_info->Configname; - - GameConfig->DoGameSetup (gameinfo.ConfigName); +//========================================================================== +// +// AddAutoloadFiles +// +//========================================================================== +static void AddAutoloadFiles(const char *gamesection) +{ if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload")) { FString file; @@ -1905,7 +1883,7 @@ void D_DoomMain (void) // voices. I never got around to writing the utility to do it, though. // And I probably never will now. But I know at least one person uses // it for something else, so this gets to stay here. - wad = BaseFileSearch ("zvox.wad", NULL); + const char *wad = BaseFileSearch ("zvox.wad", NULL); if (wad) D_AddFile (allwads, wad); @@ -1932,66 +1910,28 @@ void D_DoomMain (void) D_AddConfigWads (allwads, file); // Add IWAD-specific wads - if (iwad_info->Autoname != NULL) + if (gamesection != NULL) { - file = iwad_info->Autoname; + file = gamesection; file += ".Autoload"; D_AddConfigWads(allwads, file); } } +} - // Run automatically executed files - execFiles = new DArgs; - GameConfig->AddAutoexec (execFiles, gameinfo.ConfigName); - D_MultiExec (execFiles, true); +//========================================================================== +// +// CheckCmdLine +// +//========================================================================== - // Run .cfg files at the start of the command line. - execFiles = Args->GatherFiles ("-exec"); - D_MultiExec (execFiles, true); +static void CheckCmdLine() +{ + int flags = dmflags; + int p; + const char *v; - C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - - CopyFiles(allwads, pwads); - - // Since this function will never leave we must delete this array here manually. - pwads.Clear(); - pwads.ShrinkToFit(); - - Printf ("W_Init: Init WADfiles.\n"); - Wads.InitMultipleFiles (allwads); - allwads.Clear(); - allwads.ShrinkToFit(); - SetMapxxFlag(); - - // [RH] Initialize localizable strings. - GStrings.LoadStrings (false); - - V_InitFontColors (); - - // [RH] Moved these up here so that we can do most of our - // startup output in a fullscreen console. - - CT_Init (); - - Printf ("I_Init: Setting up machine state.\n"); - I_Init (); - - Printf ("V_Init: allocate screen.\n"); - V_Init (); - - // Base systems have been inited; enable cvar callbacks - FBaseCVar::EnableCallbacks (); - - Printf ("S_Init: Setting up sound.\n"); - S_Init (); - - Printf ("ST_Init: Init startup screen.\n"); - StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); - - ParseCompatibility(); - - Printf ("P_Init: Checking cmd-line parameters...\n"); - flags = dmflags; + Printf ("Checking cmd-line parameters...\n"); if (Args->CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS; if (Args->CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN; if (Args->CheckParm ("-fast")) flags |= DF_FAST_MONSTERS; @@ -2126,6 +2066,104 @@ void D_DoomMain (void) temp.Format ("Warp to map %s, Skill %d ", startmap.GetChars(), gameskill + 1); StartScreen->AppendStatusLine(temp); } +} + +//========================================================================== +// +// D_DoomMain +// +//========================================================================== + +void D_DoomMain (void) +{ + int p; + const char *v; + const char *wad; + DArgs *execFiles; + TArray pwads; + FString *args; + int argcount; + + D_DoomInit(); + PClass::StaticInit (); + + // [RH] Make sure zdoom.pk3 is always loaded, + // as it contains magic stuff we need. + + wad = BaseFileSearch (BASEWAD, NULL, true); + if (wad == NULL) + { + I_FatalError ("Cannot find " BASEWAD); + } + FString basewad = wad; + + // Load zdoom.pk3 alone so that we can get access to the internal gameinfos before + // the IWAD is known. + + GetCmdLineFiles(pwads); + FString iwad = CheckGameInfo(pwads); + + FIWadManager *iwad_man = new FIWadManager; + const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad); + gameinfo.gametype = iwad_info->gametype; + gameinfo.flags = iwad_info->flags; + gameinfo.ConfigName = iwad_info->Configname; + + GameConfig->DoGameSetup (gameinfo.ConfigName); + + AddAutoloadFiles(iwad_info->Autoname); + + // Run automatically executed files + execFiles = new DArgs; + GameConfig->AddAutoexec (execFiles, gameinfo.ConfigName); + D_MultiExec (execFiles, true); + + // Run .cfg files at the start of the command line. + execFiles = Args->GatherFiles ("-exec"); + D_MultiExec (execFiles, true); + + C_ExecCmdLineParams (); // [RH] do all +set commands on the command line + + CopyFiles(allwads, pwads); + + // Since this function will never leave we must delete this array here manually. + pwads.Clear(); + pwads.ShrinkToFit(); + + Printf ("W_Init: Init WADfiles.\n"); + Wads.InitMultipleFiles (allwads); + allwads.Clear(); + allwads.ShrinkToFit(); + SetMapxxFlag(); + + // [RH] Initialize localizable strings. + GStrings.LoadStrings (false); + + V_InitFontColors (); + + // [RH] Moved these up here so that we can do most of our + // startup output in a fullscreen console. + + CT_Init (); + + Printf ("I_Init: Setting up machine state.\n"); + I_Init (); + + Printf ("V_Init: allocate screen.\n"); + V_Init (); + + // Base systems have been inited; enable cvar callbacks + FBaseCVar::EnableCallbacks (); + + Printf ("S_Init: Setting up sound.\n"); + S_Init (); + + Printf ("ST_Init: Init startup screen.\n"); + StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); + + ParseCompatibility(); + + CheckCmdLine(); // [RH] Load sound environments S_ParseReverbDef (); @@ -2141,6 +2179,7 @@ void D_DoomMain (void) Printf ("Texman.Init: Init texture manager.\n"); TexMan.Init(); + C_InitConback(); // [CW] Parse any TEAMINFO lumps. Printf ("ParseTeamInfo: Load team definitions.\n"); @@ -2199,20 +2238,8 @@ void D_DoomMain (void) FActorInfo::StaticSetActorNums (); - // [RH] User-configurable startup strings. Because BOOM does. - static const char *startupString[5] = { - "STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5" - }; - for (p = 0; p < 5; ++p) - { - const char *str = GStrings[startupString[p]]; - if (str != NULL && str[0] != '\0') - { - Printf ("%s\n", str); - } - } - //Added by MC: + bglobal.getspawned.Clear(); argcount = Args->CheckParmList("-bots", &args); for (p = 0; p < argcount; ++p) { @@ -2234,6 +2261,19 @@ void D_DoomMain (void) //SBarInfo support. SBarInfo::Load(); + // [RH] User-configurable startup strings. Because BOOM does. + static const char *startupString[5] = { + "STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5" + }; + for (p = 0; p < 5; ++p) + { + const char *str = GStrings[startupString[p]]; + if (str != NULL && str[0] != '\0') + { + Printf ("%s\n", str); + } + } + Printf ("D_CheckNetGame: Checking network game status.\n"); StartScreen->LoadingStatus ("Checking network game status.", 0x3f); D_CheckNetGame (); From c6525a227177f877619bb02da9d2b8e3f6e51699 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 13 Dec 2010 10:02:45 +0000 Subject: [PATCH 193/815] - changed some data init code to delete the data it wants to initialize first. - The 'savebuffer' variable still existed? - Changed AInventory::Destroy to NULL SendItemUse and SendItemDrop if they point to the destroyed object. Although unlikely it can't be ruled out completely that this can happen with delayed CCMDs. - fixed: Starting a new game did not clear the hub statistics array. SVN r3034 (trunk) --- src/d_iwad.cpp | 1 + src/decallib.cpp | 9 +++++++++ src/g_game.cpp | 2 -- src/g_game.h | 2 ++ src/g_hub.cpp | 7 ++++++- src/g_level.cpp | 2 +- src/g_level.h | 1 + src/g_mapinfo.cpp | 5 +++++ src/g_shared/a_pickups.cpp | 5 +++++ src/info.cpp | 1 + 10 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index b02407b15..0c7cc3bf2 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -549,6 +549,7 @@ int FIWadManager::IdentifyVersion (TArray &wadfiles, const char *iwad, exit (0); // zdoom.pk3 must always be the first file loaded and the IWAD second. + wadfiles.Clear(); D_AddFile (wadfiles, zdoom_wad); if (mIWads[wads[pickwad].Type].preload >= 0) diff --git a/src/decallib.cpp b/src/decallib.cpp index bfcc1ac14..072262da9 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -347,6 +347,15 @@ void FDecalLib::ReadAllDecals () int lump, lastlump = 0; unsigned int i; + for(unsigned i=0;i hubdata; +static TArray hubdata; void G_LeavingHub(int mode, cluster_info_t * cluster, wbstartstruct_t * wbs) { @@ -182,3 +182,8 @@ void G_ReadHubInfo (PNGHandle *png) G_SerializeHub(arc); } } + +void G_ClearHubInfo() +{ + hubdata.Clear(); +} \ No newline at end of file diff --git a/src/g_level.cpp b/src/g_level.cpp index e3321f884..a962d5196 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -119,7 +119,6 @@ bool savegamerestore; extern int mousex, mousey; extern bool sendpause, sendsave, sendturn180, SendLand; -extern const AInventory *SendItemUse, *SendItemDrop; void *statcopy; // for statistics driver @@ -309,6 +308,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) bool wantFast; int i; + G_ClearHubInfo(); if (!savegamerestore) { G_ClearSnapshots (); diff --git a/src/g_level.h b/src/g_level.h index d7add35b5..6cb9d8441 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -514,6 +514,7 @@ void G_UnSnapshotLevel (bool keepPlayers); struct PNGHandle; void G_ReadSnapshots (PNGHandle *png); void G_WriteSnapshots (FILE *file); +void G_ClearHubInfo(); enum ESkillProperty { diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index adf0b2cd0..bc1a19d48 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1803,7 +1803,11 @@ void DeinitIntermissions(); static void ClearMapinfo() { + wadclusterinfos.Clear(); + wadlevelinfos.Clear(); ClearEpisodes(); + AllSkills.Clear(); + DefaultSkill = -1; DeinitIntermissions(); } @@ -1820,6 +1824,7 @@ void G_ParseMapInfo (const char *basemapinfo) int lump, lastlump = 0; level_info_t gamedefaults; + ClearMapinfo(); atterm(ClearMapinfo); // Parse the default MAPINFO for the current game. This lump *MUST* come from zdoom.pk3. diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 524daee77..f059d8d3a 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -16,6 +16,7 @@ #include "a_specialspot.h" #include "thingdef/thingdef.h" #include "g_level.h" +#include "g_game.h" #include "doomstat.h" static FRandom pr_restore ("RestorePos"); @@ -1033,6 +1034,10 @@ void AInventory::Destroy () } Inventory = NULL; Super::Destroy (); + + // Although contrived it can theoretically happen that these variables still got a pointer to this item + if (SendItemUse == this) SendItemUse = NULL; + if (SendItemDrop == this) SendItemDrop = NULL; } //=========================================================================== diff --git a/src/info.cpp b/src/info.cpp index 988c175df..f345a6e7f 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -101,6 +101,7 @@ int GetSpriteIndex(const char * spritename) void FActorInfo::StaticInit () { + sprites.Clear(); if (sprites.Size() == 0) { spritedef_t temp; From ee20d0ea7a57358edfec892686957356913f700d Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 13 Dec 2010 17:09:35 +0000 Subject: [PATCH 194/815] - Fixed: GCC compiler warnings. - zipdir will no longer store files ending in '~' on Linux. - Added st_oldouch which restores the old ouch face behavior of only showing when health increases by 20 while taking damage. SVN r3035 (trunk) --- src/g_shared/sbar_mugshot.cpp | 8 +++++--- src/textures/anim_switches.cpp | 4 ++-- tools/zipdir/zipdir.c | 14 ++++++++++---- wadsrc/static/menudef.txt | 1 + 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index e48a047bb..02b72167a 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -335,6 +335,7 @@ bool FMugShot::SetState(const char *state_name, bool wait_till_done, bool reset) // //=========================================================================== +CVAR(Bool,st_oldouch,false,CVAR_ARCHIVE) int FMugShot::UpdateState(player_t *player, StateFlags stateflags) { int i; @@ -357,9 +358,10 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) } } + bool ouch = (!st_oldouch && FaceHealth - player->health > ST_MUCHPAIN) || (st_oldouch && player->health - FaceHealth > ST_MUCHPAIN); if (player->damagecount && // Now go in if pain is disabled but we think ouch will be shown (and ouch is not disabled!) - (!(stateflags & DISABLEPAIN) || (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)))) + (!(stateflags & DISABLEPAIN) || (((FaceHealth != -1 && ouch) || bOuchActive) && !(stateflags & DISABLEOUCH)))) { int damage_angle = 1; if (player->attacker && player->attacker != player->mo) @@ -391,7 +393,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) } } bool use_ouch = false; - if (((FaceHealth != -1 && FaceHealth - player->health > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)) + if (((FaceHealth != -1 && ouch) || bOuchActive) && !(stateflags & DISABLEOUCH)) { use_ouch = true; full_state_name = "ouch."; @@ -418,7 +420,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) else { bool use_ouch = false; - if (((FaceHealth != -1 && player->health - FaceHealth > ST_MUCHPAIN) || bOuchActive) && !(stateflags & DISABLEOUCH)) + if (((FaceHealth != -1 && ouch) || bOuchActive) && !(stateflags & DISABLEOUCH)) { use_ouch = true; full_state_name = "ouch."; diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp index ca8253203..b065f0b96 100644 --- a/src/textures/anim_switches.cpp +++ b/src/textures/anim_switches.cpp @@ -325,13 +325,13 @@ void FTextureManager::AddSwitchPair (FSwitchDef *def1, FSwitchDef *def2) { index1 = i; sw1 = mSwitchDefs[index1]; - if (index2 != -1) break; + if (index2 != -1u) break; } if (mSwitchDefs[i]->PreTexture == def2->PreTexture) { index2 = i; sw2 = mSwitchDefs[index2]; - if (index1 != -1) break; + if (index1 != -1u) break; } } diff --git a/tools/zipdir/zipdir.c b/tools/zipdir/zipdir.c index 01a997958..ae4738b04 100644 --- a/tools/zipdir/zipdir.c +++ b/tools/zipdir/zipdir.c @@ -610,6 +610,7 @@ dir_tree_t *add_dirs(char **argv) { // Skip hidden directories. (Prevents SVN bookkeeping // info from being included.) + // [BL] Also skip backup files. fts_set(fts, ent, FTS_SKIP); } if (ent->fts_info == FTS_D && ent->fts_level == 0) @@ -628,6 +629,11 @@ dir_tree_t *add_dirs(char **argv) // We're only interested in remembering files. continue; } + else if(ent->fts_name[strlen(ent->fts_name)-1] == '~') + { + // Don't remember backup files. + continue; + } file = alloc_file_entry("", ent->fts_path, ent->fts_statp->st_mtime); if (file == NULL) { @@ -1030,10 +1036,10 @@ int append_to_zip(FILE *zip_file, file_sorted_t *filep, FILE *ozip, BYTE *odir) if (compbuf[0] != NULL) { free(compbuf[0]); - } - if (compbuf[1] != NULL) - { - free(compbuf[1]); + } + if (compbuf[1] != NULL) + { + free(compbuf[1]); } return 1; } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 5097106e3..8446ea00c 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -736,6 +736,7 @@ OptionMenu "HUDOptions" Option "Display nametags", "displaynametags", "DisplayTagsTypes" Option "Nametag color", "nametagcolor", "TextColors", "displaynametags" Option "Stretch status bar", "st_scale", "OnOff" + Option "Use old ouch mug shot formula", "st_oldouch", "OnOff" } //------------------------------------------------------------------------------------------- From f0f17e531cb5f3be44b985311dccb6c260eb3916 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Dec 2010 00:50:02 +0000 Subject: [PATCH 195/815] - move static AM color initialization into the AM_StaticInit function. - move D_LoadWadSettings to keysections.cpp. - made some more data reloadable. - data structures filled by P_SetupLevel should be cleared before loading the level. They can remain non-empty in case of an error. There's probably more to fix here... - fixed: MidiDevices and MusicAliases were not cleared before reloading local SNDINFOs. - fixed signed/unsigned warnings in AddSwitchPair for real (GCC really allows -1u? MSVC prints a warning for that.) SVN r3036 (trunk) --- src/actor.h | 7 +++- src/am_map.cpp | 35 +++++++--------- src/d_main.cpp | 75 +-------------------------------- src/decallib.cpp | 1 + src/g_mapinfo.cpp | 1 + src/gi.cpp | 4 +- src/info.cpp | 2 + src/keysections.cpp | 76 ++++++++++++++++++++++++++++++++++ src/menu/menu.cpp | 1 - src/p_conversation.cpp | 5 +++ src/p_mobj.cpp | 3 +- src/p_setup.cpp | 4 ++ src/p_states.cpp | 1 - src/p_terrain.cpp | 2 + src/r_things.cpp | 1 + src/s_advsound.cpp | 5 ++- src/s_sound.cpp | 4 +- src/s_sound.h | 2 +- src/statistics.cpp | 1 + src/teaminfo.cpp | 1 + src/textures/anim_switches.cpp | 6 +-- src/thingdef/thingdef.cpp | 1 + src/thingdef/thingdef_data.cpp | 3 ++ 23 files changed, 134 insertions(+), 107 deletions(-) diff --git a/src/actor.h b/src/actor.h index 79358148f..13425e48a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -529,7 +529,12 @@ struct FDropItem class FDropItemPtrArray : public TArray { public: - ~FDropItemPtrArray(); + ~FDropItemPtrArray() + { + Clear(); + } + + void Clear(); }; extern FDropItemPtrArray DropItemList; diff --git a/src/am_map.cpp b/src/am_map.cpp index d3fbc1140..5eb759ee3 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -545,6 +545,21 @@ void AM_StaticInit() } markpointnum = 0; mapback.SetInvalid(); + + static DWORD *lastpal = NULL; + //static int lastback = -1; + DWORD *palette; + + palette = (DWORD *)GPalette.BaseColors; + + int i, j; + + for (i = j = 0; i < 11; i++, j += 3) + { + DoomColors[i].FromRGB(DoomPaletteVals[j], DoomPaletteVals[j+1], DoomPaletteVals[j+2]); + StrifeColors[i].FromRGB(StrifePaletteVals[j], StrifePaletteVals[j+1], StrifePaletteVals[j+2]); + RavenColors[i].FromRGB(RavenPaletteVals[j], RavenPaletteVals[j+1], RavenPaletteVals[j+2]); + } } //============================================================================= @@ -878,24 +893,6 @@ void AM_initVariables () static void AM_initColors (bool overlayed) { - static DWORD *lastpal = NULL; - //static int lastback = -1; - DWORD *palette; - - palette = (DWORD *)GPalette.BaseColors; - - if (lastpal != palette) - { - int i, j; - - for (i = j = 0; i < 11; i++, j += 3) - { - DoomColors[i].FromRGB(DoomPaletteVals[j], DoomPaletteVals[j+1], DoomPaletteVals[j+2]); - StrifeColors[i].FromRGB(StrifePaletteVals[j], StrifePaletteVals[j+1], StrifePaletteVals[j+2]); - RavenColors[i].FromRGB(RavenPaletteVals[j], RavenPaletteVals[j+1], RavenPaletteVals[j+2]); - } - } - if (overlayed) { YourColor.FromCVar (am_ovyourcolor); @@ -1020,8 +1017,6 @@ static void AM_initColors (bool overlayed) break; } - - lastpal = palette; } //============================================================================= diff --git a/src/d_main.cpp b/src/d_main.cpp index ffe4afdbd..2eaabf76d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -131,6 +131,7 @@ void D_ProcessEvents (); void G_BuildTiccmd (ticcmd_t* cmd); void D_DoAdvanceDemo (); void D_AddWildFile (TArray &wadfiles, const char *pattern); +void D_LoadWadSettings (); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -1563,80 +1564,6 @@ bool ConsiderPatches (const char *arg) return argc > 0; } -//========================================================================== -// -// D_LoadWadSettings -// -// Parses any loaded KEYCONF lumps. These are restricted console scripts -// that can only execute the alias, defaultbind, addkeysection, -// addmenukey, weaponsection, and addslotdefault commands. -// -//========================================================================== - -void D_LoadWadSettings () -{ - char cmd[4096]; - int lump, lastlump = 0; - - ParsingKeyConf = true; - - while ((lump = Wads.FindLump ("KEYCONF", &lastlump)) != -1) - { - FMemLump data = Wads.ReadLump (lump); - const char *eof = (char *)data.GetMem() + Wads.LumpLength (lump); - const char *conf = (char *)data.GetMem(); - - while (conf < eof) - { - size_t i; - - // Fetch a line to execute - for (i = 0; conf + i < eof && conf[i] != '\n'; ++i) - { - cmd[i] = conf[i]; - } - cmd[i] = 0; - conf += i; - if (*conf == '\n') - { - conf++; - } - - // Comments begin with // - char *stop = cmd + i - 1; - char *comment = cmd; - int inQuote = 0; - - if (*stop == '\r') - *stop-- = 0; - - while (comment < stop) - { - if (*comment == '\"') - { - inQuote ^= 1; - } - else if (!inQuote && *comment == '/' && *(comment + 1) == '/') - { - break; - } - comment++; - } - if (comment == cmd) - { // Comment at line beginning - continue; - } - else if (comment < stop) - { // Comment in middle of line - *comment = 0; - } - - AddCommandString (cmd); - } - } - ParsingKeyConf = false; -} - //========================================================================== // // D_MultiExec diff --git a/src/decallib.cpp b/src/decallib.cpp index 072262da9..6bbf0f74d 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -353,6 +353,7 @@ void FDecalLib::ReadAllDecals () } Animators.Clear(); FDecalCombinerAnim::AnimatorList.Clear(); + DecalTranslations.Clear(); DecalLibrary.Clear(); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index bc1a19d48..4199305d3 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1809,6 +1809,7 @@ static void ClearMapinfo() AllSkills.Clear(); DefaultSkill = -1; DeinitIntermissions(); + level.info = NULL; } //========================================================================== diff --git a/src/gi.cpp b/src/gi.cpp index 705f9b801..0e010724a 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -77,7 +77,7 @@ static gameborder_t StrifeBorder = // Custom GAMEINFO ------------------------------------------------------------ -const char* GameInfoBoarders[] = +const char* GameInfoBorders[] = { "DoomBorder", "HereticBorder", @@ -219,7 +219,7 @@ void FMapInfoParser::ParseGameInfo() { if(sc.CheckToken(TK_Identifier)) { - switch(sc.MustMatchString(GameInfoBoarders)) + switch(sc.MustMatchString(GameInfoBorders)) { default: gameinfo.border = &DoomBorder; diff --git a/src/info.cpp b/src/info.cpp index f345a6e7f..32aa92a91 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -53,6 +53,7 @@ extern void LoadActors (); extern void InitBotStuff(); +extern void ClearStrifeTypes(); //========================================================================== @@ -122,6 +123,7 @@ void FActorInfo::StaticInit () } Printf ("LoadActors: Load actor definitions.\n"); + ClearStrifeTypes(); LoadActors (); InitBotStuff(); } diff --git a/src/keysections.cpp b/src/keysections.cpp index 6ef0538f6..bfec4060e 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -41,6 +41,7 @@ #include "c_bind.h" #include "c_dispatch.h" #include "gameconfigfile.h" +#include "w_wad.h" TArray KeySections; @@ -141,3 +142,78 @@ CCMD (addmenukey) } } +//========================================================================== +// +// D_LoadWadSettings +// +// Parses any loaded KEYCONF lumps. These are restricted console scripts +// that can only execute the alias, defaultbind, addkeysection, +// addmenukey, weaponsection, and addslotdefault commands. +// +//========================================================================== + +void D_LoadWadSettings () +{ + char cmd[4096]; + int lump, lastlump = 0; + + ParsingKeyConf = true; + KeySections.Clear(); + + while ((lump = Wads.FindLump ("KEYCONF", &lastlump)) != -1) + { + FMemLump data = Wads.ReadLump (lump); + const char *eof = (char *)data.GetMem() + Wads.LumpLength (lump); + const char *conf = (char *)data.GetMem(); + + while (conf < eof) + { + size_t i; + + // Fetch a line to execute + for (i = 0; conf + i < eof && conf[i] != '\n'; ++i) + { + cmd[i] = conf[i]; + } + cmd[i] = 0; + conf += i; + if (*conf == '\n') + { + conf++; + } + + // Comments begin with // + char *stop = cmd + i - 1; + char *comment = cmd; + int inQuote = 0; + + if (*stop == '\r') + *stop-- = 0; + + while (comment < stop) + { + if (*comment == '\"') + { + inQuote ^= 1; + } + else if (!inQuote && *comment == '/' && *(comment + 1) == '/') + { + break; + } + comment++; + } + if (comment == cmd) + { // Comment at line beginning + continue; + } + else if (comment < stop) + { // Comment in middle of line + *comment = 0; + } + + AddCommandString (cmd); + } + } + ParsingKeyConf = false; +} + diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 0bdc622fc..c8728515b 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -70,7 +70,6 @@ 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; diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 9b1119e0d..39da6b65a 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -145,6 +145,11 @@ void SetStrifeType(int convid, const PClass *Class) StrifeTypes[convid] = Class; } +void ClearStrifeTypes() +{ + StrifeTypes.Clear(); +} + void SetConversation(int convid, const PClass *Class, int dlgindex) { if (convid != -1) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index bc173b9cb..0b13c8c75 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -5586,12 +5586,13 @@ void FreeDropItemChain(FDropItem *chain) } } -FDropItemPtrArray::~FDropItemPtrArray() +void FDropItemPtrArray::Clear() { for (unsigned int i = 0; i < Size(); ++i) { FreeDropItemChain ((*this)[i]); } + TArray::Clear(); } int StoreDropItemChain(FDropItem *chain) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 7017d5c8b..4dec4a95c 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3491,6 +3491,7 @@ void P_FreeExtraLevelData() delete node; node = next; } + FBlockNode::FreeBlocks = NULL; } { msecnode_t *node = headsecnode; @@ -3528,8 +3529,11 @@ void P_SetupLevel (char *lumpname, int position) wminfo.partime = 180; + MapThingsConverted.Clear(); + linemap.Clear(); FCanvasTextureInfo::EmptyList (); R_FreePastViewers (); + P_ClearUDMFKeys(); if (!savegamerestore) { diff --git a/src/p_states.cpp b/src/p_states.cpp index 15ff37117..21dd1537d 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -861,7 +861,6 @@ bool FStateDefinitions::AddStates(FState *state, const char *framechars) int FStateDefinitions::FinishStates (FActorInfo *actor, AActor *defaults) { - static int c=0; int count = StateArray.Size(); if (count > 0) diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index 14d2bb3df..c3df86b27 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -242,6 +242,8 @@ void P_InitTerrainTypes () int lump; int size; + Splashes.Clear(); + Terrains.Clear(); size = (TexMan.NumTextures()+1); TerrainTypes.Resize(size); TerrainTypes.Clear(); diff --git a/src/r_things.cpp b/src/r_things.cpp index 4d40c04cd..6fa357434 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2357,6 +2357,7 @@ void R_InitParticles () if ( NumParticles < 100 ) NumParticles = 100; + R_DeinitParticles(); Particles = new particle_t[NumParticles]; R_ClearParticles (); atterm (R_DeinitParticles); diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 32c2f3222..3245c20fa 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -857,6 +857,8 @@ static void S_ClearSoundData() PlayerSounds.Clear(); DefPlayerClass = 0; DefPlayerClassName = ""; + MusicAliases.Clear(); + MidiDevices.Clear(); } //========================================================================== @@ -867,10 +869,11 @@ static void S_ClearSoundData() // Also registers Blood SFX files and Strife's voices. //========================================================================== -void S_ParseSndInfo () +void S_ParseSndInfo (bool redefine) { int lump; + if (!redefine) SavedPlayerSounds.Clear(); // clear skin sounds only for initial parsing. atterm (S_ClearSoundData); S_ClearSoundData(); // remove old sound data first! diff --git a/src/s_sound.cpp b/src/s_sound.cpp index bb85541c4..c09e913be 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -322,7 +322,7 @@ void S_Init () void S_InitData () { LastLocalSndInfo = LastLocalSndSeq = ""; - S_ParseSndInfo (); + S_ParseSndInfo (false); S_ParseSndSeq (-1); S_ParseMusInfo(); } @@ -408,7 +408,7 @@ void S_Start () } // Parse the global SNDINFO - S_ParseSndInfo(); + S_ParseSndInfo(true); if (*LocalSndInfo) { diff --git a/src/s_sound.h b/src/s_sound.h index ee831bddf..12e7ee3c6 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -331,7 +331,7 @@ void S_UpdateSounds (AActor *listener); void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines -void S_ParseSndInfo (); +void S_ParseSndInfo (bool redefine); void S_ParseReverbDef (); void S_UnloadReverbDef (); diff --git a/src/statistics.cpp b/src/statistics.cpp index 276090ebd..9c07ac6da 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -134,6 +134,7 @@ static void ParseStatistics(const char *fn, TArray &statlist) { FScanner sc; sc.OpenFile(fn); + statlist.Clear(); while (sc.GetString()) { diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp index c54b2f9d6..f5c3c35e2 100644 --- a/src/teaminfo.cpp +++ b/src/teaminfo.cpp @@ -131,6 +131,7 @@ void FTeam::ParseTeamInfo () { int iLump, iLastLump = 0; + Teams.Clear(); while ((iLump = Wads.FindLump ("TEAMINFO", &iLastLump)) != -1) { FScanner Scan (iLump); diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp index b065f0b96..014e879f8 100644 --- a/src/textures/anim_switches.cpp +++ b/src/textures/anim_switches.cpp @@ -317,7 +317,7 @@ void FTextureManager::AddSwitchPair (FSwitchDef *def1, FSwitchDef *def2) unsigned int i; FSwitchDef *sw1 = NULL; FSwitchDef *sw2 = NULL; - unsigned int index1 = -1, index2 = -1; + unsigned int index1 = 0xffffffff, index2 = 0xffffffff; for (i = mSwitchDefs.Size (); i-- > 0; ) { @@ -325,13 +325,13 @@ void FTextureManager::AddSwitchPair (FSwitchDef *def1, FSwitchDef *def2) { index1 = i; sw1 = mSwitchDefs[index1]; - if (index2 != -1u) break; + if (index2 != 0xffffffff) break; } if (mSwitchDefs[i]->PreTexture == def2->PreTexture) { index2 = i; sw2 = mSwitchDefs[index2]; - if (index1 != -1u) break; + if (index1 != 0xffffffff) break; } } diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 6362c62fe..1b2f844bd 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -337,6 +337,7 @@ void LoadActors () { int lastlump, lump; + DropItemList.Clear(); FScriptPosition::ResetErrorCounter(); InitThingdef(); lastlump = 0; diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 54de97aa8..30c35a0a4 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -584,6 +584,7 @@ void InitThingdef() } // Create a sorted list of properties + if (properties.Size() == 0) { FAutoSegIterator probe(GRegHead, GRegTail); @@ -596,6 +597,7 @@ void InitThingdef() } // Create a sorted list of native action functions + if (AFTable.Size() == 0) { FAutoSegIterator probe(ARegHead, ARegTail); @@ -608,6 +610,7 @@ void InitThingdef() } // Create a sorted list of native variables + if (variables.Size() == 0) { FAutoSegIterator probe(MRegHead, MRegTail); From 79c5080ddae1e6fd9bbbebaabb8cfdb1d4d047fd Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 14 Dec 2010 00:56:44 +0000 Subject: [PATCH 196/815] - added DavidPH's submission for specifying vertex heights directly in UDMF. SVN r3037 (trunk) --- specs/udmf_zdoom.txt | 11 ++++++++++- src/namedef.h | 2 ++ src/p_setup.cpp | 4 ++++ src/p_slopes.cpp | 21 +++++++++++++++++++++ src/p_udmf.cpp | 25 +++++++++++++++++++++++-- src/r_defs.h | 10 ++++++++++ src/r_state.h | 2 ++ 7 files changed, 72 insertions(+), 3 deletions(-) diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 1a4dbeffa..6c2fde912 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -1,5 +1,5 @@ =============================================================================== -Universal Doom Map Format ZDoom extensions v1.10 - 25.04.2010 +Universal Doom Map Format ZDoom extensions v1.15 - 14.12.2010 Copyright (c) 2008 Christoph Oelckers. @@ -84,6 +84,12 @@ field to 'strifeally', even for the 'Doom' namespace. In addition to the standard fields, ZDoom defines the following: Note: All fields default to false unless mentioned otherwise. + vertex + { + zfloor = ; // Floor height at this vertex. Only applies to triangular sectors + zceiling = ; // Ceiling height at this vertex. Only applies to triangular sectors + } + linedef { alpha = ; // Translucency of this line, default is 1.0 @@ -283,6 +289,9 @@ Added 'hidden' sector property. 1.14 19.09.2010 Added 'countsecret' actor property. +1.15 14.12.2010 +Added vertex floor and ceiling height properties + =============================================================================== EOF =============================================================================== diff --git a/src/namedef.h b/src/namedef.h index 684d447f6..d9573b747 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -280,6 +280,8 @@ xx(Communicator) // Textmap properties //xx(X) //xx(Y) +xx(ZFloor) +xx(ZCeiling) xx(Height) //xx(Tid) //xx(Angle) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 4dec4a95c..b2e385286 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -102,6 +102,8 @@ bool P_IsBuildMap(MapData *map); // int numvertexes; vertex_t* vertexes; +int numvertexdatas; +vertexdata_t* vertexdatas; int numsegs; seg_t* segs; @@ -755,6 +757,7 @@ void P_LoadVertexes (MapData * map) // Determine number of vertices: // total lump length / vertex record length. numvertexes = map->MapLumps[ML_VERTEXES].Size / sizeof(mapvertex_t); + numvertexdatas = 0; if (numvertexes == 0) { @@ -763,6 +766,7 @@ void P_LoadVertexes (MapData * map) // Allocate memory for buffer. vertexes = new vertex_t[numvertexes]; + vertexdatas = NULL; map->Seek(ML_VERTEXES); diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index 3ebaef144..7cf6c5fbe 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -309,6 +309,27 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt) mt->type = 0; } } + + for(int i = 0; i < numvertexdatas; i++) + { + if (vertexdatas[i].flags & VERTEXFLAG_ZCeilingEnabled) + { + vt_heights[1][i] = vertexdatas[i].zCeiling; + vt_found = true; + } + + if (vertexdatas[i].flags & VERTEXFLAG_ZFloorEnabled) + { + vt_heights[0][i] = vertexdatas[i].zFloor; + vt_found = true; + } + } + + // If vertexdata_t is ever extended for non-slope usage, this will obviously have to be deferred or removed. + delete[] vertexdatas; + vertexdatas = NULL; + numvertexdatas = 0; + if (vt_found) { for (int i = 0; i < numsectors; i++) diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 0fba825b0..72c3cb24c 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -390,6 +390,7 @@ class UDMFParser : public UDMFParserBase TArray ParsedSideTextures; TArray ParsedSectors; TArray ParsedVertices; + TArray ParsedVertexDatas; FDynamicColormap *fogMap, *normMap; @@ -1303,9 +1304,10 @@ public: // //=========================================================================== - void ParseVertex(vertex_t *vt) + void ParseVertex(vertex_t *vt, vertexdata_t *vd) { vt->x = vt->y = 0; + vd->zCeiling = vd->zFloor = vd->flags = 0; sc.MustGetStringName("{"); while (!sc.CheckString("}")) { @@ -1320,9 +1322,21 @@ public: case NAME_X: vt->x = FLOAT2FIXED(strtod(value, NULL)); break; + case NAME_Y: vt->y = FLOAT2FIXED(strtod(value, NULL)); break; + + case NAME_ZCeiling: + vd->zCeiling = FLOAT2FIXED(strtod(value, NULL)); + vd->flags |= VERTEXFLAG_ZCeilingEnabled; + break; + + case NAME_ZFloor: + vd->zFloor = FLOAT2FIXED(strtod(value, NULL)); + vd->flags |= VERTEXFLAG_ZFloorEnabled; + break; + default: break; } @@ -1520,8 +1534,10 @@ public: else if (sc.Compare("vertex")) { vertex_t vt; - ParseVertex(&vt); + vertexdata_t vd; + ParseVertex(&vt, &vd); ParsedVertices.Push(vt); + ParsedVertexDatas.Push(vd); } else { @@ -1534,6 +1550,11 @@ public: vertexes = new vertex_t[numvertexes]; memcpy(vertexes, &ParsedVertices[0], numvertexes * sizeof(*vertexes)); + // Create the real vertex datas + numvertexdatas = ParsedVertexDatas.Size(); + vertexdatas = new vertexdata_t[numvertexdatas]; + memcpy(vertexdatas, &ParsedVertexDatas[0], numvertexdatas * sizeof(*vertexdatas)); + // Create the real sectors numsectors = ParsedSectors.Size(); sectors = new sector_t[numsectors]; diff --git a/src/r_defs.h b/src/r_defs.h index 22745f54c..9d2822c33 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -67,6 +67,16 @@ extern size_t MaxDrawSegs; // Note: transformed values not buffered locally, // like some DOOM-alikes ("wt", "WebView") did. // +enum +{ + VERTEXFLAG_ZCeilingEnabled = 0x01, + VERTEXFLAG_ZFloorEnabled = 0x02 +}; +struct vertexdata_t +{ + fixed_t zCeiling, zFloor; + DWORD flags; +}; struct vertex_t { fixed_t x, y; diff --git a/src/r_state.h b/src/r_state.h index 9044da72b..652968385 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -55,6 +55,8 @@ extern DWORD NumStdSprites; extern int numvertexes; extern vertex_t* vertexes; +extern int numvertexdatas; +extern vertexdata_t* vertexdatas; extern int numsegs; extern seg_t* segs; From 22372fff252d8c322820228e369e6b14917ee35e Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Tue, 14 Dec 2010 16:05:38 +0000 Subject: [PATCH 197/815] - Fixed: "Show IWAD selection dialog" when only shown on Windows. SVN r3038 (trunk) --- wadsrc/static/menudef.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 8446ea00c..a833fb8b6 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -795,9 +795,9 @@ OptionMenu "MiscOptions" { Option "Merge left+right Alt/Ctrl/Shift", "k_mergekeys", "OnOff" Option "Alt-Enter toggles fullscreen", "k_allowfullscreentoggle", "OnOff" - Option "Show IWAD selection dialog", "queryiwad", "OnOff" - StaticText " " } + Option "Show IWAD selection dialog", "queryiwad", "OnOff" + StaticText " " Option "Enable cheats from all games", "allcheats", "OnOff" Option "Enable autosaves", "disableautosave", "OffOn" Slider "Number of autosaves", "autosavecount", 1, 32, 1, 0 From 770a879f6aa968622f05f883e375b1959d3d7c30 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 00:09:31 +0000 Subject: [PATCH 198/815] - fixed: The TouchedActors array in the Dehacked parser was not freed after parsing was done. - Initialize the alt HUD explicitly in D_DoomMain. - don't let S_UnloadReverbDef leave a broken list of sound environments behind. - Added more code to explicitly delete data before initializing it. SVN r3039 (trunk) --- src/d_dehacked.cpp | 2 ++ src/d_main.cpp | 3 ++- src/g_shared/sbar.h | 1 + src/g_shared/sbarinfo.cpp | 2 ++ src/g_shared/shared_hud.cpp | 3 --- src/g_shared/shared_sbar.cpp | 17 ++++++++++++++ src/keysections.cpp | 2 ++ src/menu/loadsavemenu.cpp | 22 +++++++++++++++++-- src/menu/menu.h | 24 ++++++++++++++++++++ src/menu/menudef.cpp | 15 +++++++++++++ src/p_setup.cpp | 6 +---- src/p_user.cpp | 1 + src/r_data.cpp | 33 +++++++++++++++------------- src/r_things.cpp | 3 +++ src/r_translate.cpp | 2 ++ src/s_environment.cpp | 7 ++++++ src/s_sound.cpp | 1 + src/statistics.cpp | 2 +- src/thingdef/thingdef.cpp | 2 ++ src/thingdef/thingdef.h | 3 ++- src/thingdef/thingdef_expression.cpp | 3 ++- src/v_font.cpp | 15 +++++++++++++ src/v_font.h | 3 ++- src/v_video.cpp | 5 +---- src/xlat/parse_xlat.cpp | 17 ++++++++++---- 25 files changed, 156 insertions(+), 38 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 225f24cab..e5fd308b5 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2880,6 +2880,8 @@ void FinishDehPatch () // Now that all Dehacked patches have been processed, it's okay to free StateMap. StateMap.Clear(); StateMap.ShrinkToFit(); + TouchedActors.Clear(); + TouchedActors.ShrinkToFit(); } void ModifyDropAmount(AInventory *inv, int dropamount); diff --git a/src/d_main.cpp b/src/d_main.cpp index 2eaabf76d..2b3e273a9 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -122,6 +122,7 @@ extern void M_SetDefaultMode (); extern void R_ExecuteSetViewSize (); extern void G_NewInit (); extern void SetupPlayerClasses (); +extern void HUD_InitHud(); const FIWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const char *basewad); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -2134,7 +2135,6 @@ void D_DoomMain (void) R_Init (); Printf ("DecalLibrary: Load decals.\n"); - DecalLibrary.Clear (); DecalLibrary.ReadAllDecals (); // [RH] Add any .deh and .bex files on the command line. @@ -2187,6 +2187,7 @@ void D_DoomMain (void) //SBarInfo support. SBarInfo::Load(); + HUD_InitHud(); // [RH] User-configurable startup strings. Because BOOM does. static const char *startupString[5] = { diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index b2f6173bf..71dc66672 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -383,6 +383,7 @@ DBaseStatusBar *CreateCustomStatusBar(int script=0); void ST_FormatMapName(FString &mapname, const char *mapnamecolor = ""); void ST_LoadCrosshair(bool alwaysload=false); +void ST_Clear(); extern FTexture *CrosshairImage; #endif /* __SBAR_H__ */ diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index ee29a3b5d..a552a3ad1 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -423,6 +423,8 @@ static void FreeSBarInfoScript() void SBarInfo::Load() { + FreeSBarInfoScript(); + MugShotStates.Clear(); if(gameinfo.statusbar.IsNotEmpty()) { int lump = Wads.CheckNumForFullName(gameinfo.statusbar, true); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index d766d8902..9901c7940 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -769,14 +769,11 @@ static void DrawCoordinates(player_t * CPlayer) // draw the overlay // //--------------------------------------------------------------------------- -void HUD_InitHud(); void DrawHUD() { player_t * CPlayer = StatusBar->CPlayer; - if (HudFont==NULL) HUD_InitHud(); - players[consoleplayer].inventorytics = 0; if (hud_althudscale && SCREENWIDTH>640) { diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 75084ae38..09fb9417b 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -197,6 +197,23 @@ void ST_LoadCrosshair(bool alwaysload) CrosshairImage = TexMan[TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch)]; } +//--------------------------------------------------------------------------- +// +// ST_Clear +// +//--------------------------------------------------------------------------- + +void ST_Clear() +{ + if (StatusBar != NULL) + { + StatusBar->Destroy(); + StatusBar = NULL; + } + CrosshairImage = NULL; + CrosshairNum = 0; +} + //--------------------------------------------------------------------------- // // Constructor diff --git a/src/keysections.cpp b/src/keysections.cpp index bfec4060e..8d23c9582 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -44,6 +44,7 @@ #include "w_wad.h" TArray KeySections; +extern TArray KeyConfWeapons; static void LoadKeys (const char *modname, bool dbl) { @@ -159,6 +160,7 @@ void D_LoadWadSettings () ParsingKeyConf = true; KeySections.Clear(); + KeyConfWeapons.Clear(); while ((lump = Wads.FindLump ("KEYCONF", &lastlump)) != -1) { diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 97bda8494..bdc5c3c30 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -53,9 +53,10 @@ class DLoadSaveMenu : public DListMenu { DECLARE_CLASS(DLoadSaveMenu, DListMenu) + friend void ClearSaveGames(); protected: - static TDeletingArray SaveGames; + static TArray SaveGames; static int LastSaved; static int LastAccessed; @@ -114,7 +115,7 @@ public: IMPLEMENT_CLASS(DLoadSaveMenu) -TDeletingArray DLoadSaveMenu::SaveGames; +TArray DLoadSaveMenu::SaveGames; int DLoadSaveMenu::LastSaved = -1; int DLoadSaveMenu::LastAccessed = -1; @@ -126,6 +127,21 @@ FSaveGameNode *quickSaveSlot; // //============================================================================= +void ClearSaveGames() +{ + for(unsigned i=0;i 0) + { + I_FatalError("You cannot add menu items to the menu default settings."); + } } else if (sc.Compare("OPTIONVALUE")) { @@ -861,6 +872,10 @@ void M_ParseMenuDefs() else if (sc.Compare("DEFAULTOPTIONMENU")) { ParseOptionMenuBody(sc, &DefaultOptionMenuSettings); + if (DefaultOptionMenuSettings.mItems.Size() > 0) + { + I_FatalError("You cannot add menu items to the menu default settings."); + } } else { diff --git a/src/p_setup.cpp b/src/p_setup.cpp index b2e385286..fa30df88f 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4074,11 +4074,7 @@ static void P_Shutdown () P_DeinitKeyMessages (); P_FreeLevelData (); P_FreeExtraLevelData (); - if (StatusBar != NULL) - { - StatusBar->Destroy(); - StatusBar = NULL; - } + ST_Clear(); } #if 0 diff --git a/src/p_user.cpp b/src/p_user.cpp index a9c04ad49..831d65dd5 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -133,6 +133,7 @@ void SetupPlayerClasses () { FPlayerClass newclass; + PlayerClasses.Clear(); for (unsigned i=0; iNext; if (!probe->Builtin) { + if (pNext != NULL) *pNext = probe->Next; delete[] const_cast(probe->Name); delete probe; } + else + { + pNext = &probe->Next; + } probe = next; } Environments = &Off; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index c09e913be..4c4053899 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -292,6 +292,7 @@ void S_Init () if (S_SoundCurve != NULL) { delete[] S_SoundCurve; + S_SoundCurve = NULL; } // Heretic and Hexen have sound curve lookup tables. Doom does not. diff --git a/src/statistics.cpp b/src/statistics.cpp index 9c07ac6da..fe72f7667 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -130,11 +130,11 @@ extern TArray wadlevelinfos; static void ParseStatistics(const char *fn, TArray &statlist) { + statlist.Clear(); try { FScanner sc; sc.OpenFile(fn); - statlist.Clear(); while (sc.GetString()) { diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 1b2f844bd..0d9dfad30 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -337,6 +337,8 @@ void LoadActors () { int lastlump, lump; + StateParams.Clear(); + GlobalSymbols.ReleaseSymbols(); DropItemList.Clear(); FScriptPosition::ResetErrorCounter(); InitThingdef(); diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 6b2124149..41925b712 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -123,7 +123,8 @@ class FStateExpressions TArray expressions; public: - ~FStateExpressions(); + ~FStateExpressions() { Clear(); } + void Clear(); int Add(FxExpression *x, const PClass *o, bool c); int Reserve(int num, const PClass *cls); void Set(int num, FxExpression *x, bool cloned = false); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 1315d70ee..5f7238e29 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -2765,7 +2765,7 @@ FStateExpressions StateParams; // //========================================================================== -FStateExpressions::~FStateExpressions() +void FStateExpressions::Clear() { for(unsigned i=0; iObjectFlags |= OF_YesReallyDelete; delete s; } - while (FFont::FirstFont != NULL) - { - delete FFont::FirstFont; - } + V_ClearFonts(); } EXTERN_CVAR (Bool, vid_tft) diff --git a/src/xlat/parse_xlat.cpp b/src/xlat/parse_xlat.cpp index 6221c5497..5edcde581 100644 --- a/src/xlat/parse_xlat.cpp +++ b/src/xlat/parse_xlat.cpp @@ -156,16 +156,23 @@ struct XlatParseContext : public FParseContext // //========================================================================== +void P_ClearTranslator() +{ + SimpleLineTranslations.Clear(); + NumBoomish = 0; + SectorTranslations.Clear(); + SectorMasks.Clear(); + memset(LineFlagTranslations, 0, sizeof(LineFlagTranslations)); + LastTranslator = ""; +} + void P_LoadTranslator(const char *lumpname) { // Only read the lump if it differs from the previous one. if (LastTranslator.CompareNoCase(lumpname)) { // Clear the old data before parsing the lump. - SimpleLineTranslations.Clear(); - NumBoomish = 0; - SectorTranslations.Clear(); - SectorMasks.Clear(); + P_ClearTranslator(); void *pParser = XlatParseAlloc(malloc); @@ -179,3 +186,5 @@ void P_LoadTranslator(const char *lumpname) LastTranslator = lumpname; } } + + From e7eb43a343974500fb41a47f2e9f21f3831ed5b3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 00:14:42 +0000 Subject: [PATCH 199/815] - added Gez's automap patch to mark trigger lines. SVN r3040 (trunk) --- src/am_map.cpp | 27 ++++++++++++++++++++++++--- src/gi.h | 4 ++-- src/p_setup.cpp | 1 + src/win32/zdoom.rc | 2 +- wadsrc/static/menudef.txt | 3 +++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 5eb759ee3..7c709019b 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -91,7 +91,7 @@ struct AMColor static AMColor Background, YourColor, WallColor, TSWallColor, FDWallColor, CDWallColor, ThingColor, ThingColor_Item, ThingColor_CountItem, ThingColor_Monster, ThingColor_Friend, - SecretWallColor, GridColor, XHairColor, + SpecialWallColor, SecretWallColor, GridColor, XHairColor, NotSeenColor, LockedColor, AlmostBackground, @@ -167,6 +167,7 @@ CVAR (Color, am_backcolor, 0x6c5440, CVAR_ARCHIVE); CVAR (Color, am_yourcolor, 0xfce8d8, CVAR_ARCHIVE); CVAR (Color, am_wallcolor, 0x2c1808, CVAR_ARCHIVE); CVAR (Color, am_secretwallcolor, 0x000000, CVAR_ARCHIVE); +CVAR (Color, am_specialwallcolor, 0xffffff, CVAR_ARCHIVE); CVAR (Color, am_tswallcolor, 0x888888, CVAR_ARCHIVE); CVAR (Color, am_fdwallcolor, 0x887058, CVAR_ARCHIVE); CVAR (Color, am_cdwallcolor, 0x4c3820, CVAR_ARCHIVE); @@ -177,6 +178,7 @@ CVAR (Color, am_notseencolor, 0x6c6c6c, CVAR_ARCHIVE); CVAR (Color, am_lockedcolor, 0x007800, CVAR_ARCHIVE); CVAR (Color, am_ovyourcolor, 0xfce8d8, CVAR_ARCHIVE); CVAR (Color, am_ovwallcolor, 0x00ff00, CVAR_ARCHIVE); +CVAR (Color, am_ovspecialwallcolor, 0xffffff, CVAR_ARCHIVE); CVAR (Color, am_ovthingcolor, 0xe88800, CVAR_ARCHIVE); CVAR (Color, am_ovotherwallscolor, 0x008844, CVAR_ARCHIVE); CVAR (Color, am_ovunseencolor, 0x00226e, CVAR_ARCHIVE); @@ -188,6 +190,7 @@ CVAR (Color, am_ovsecretsectorcolor,0x00ffff, CVAR_ARCHIVE); CVAR (Int, am_map_secrets, 1, CVAR_ARCHIVE); CVAR (Bool, am_drawmapback, true, CVAR_ARCHIVE); CVAR (Bool, am_showkeys, true, CVAR_ARCHIVE); +CVAR (Bool, am_showtriggerlines, false, CVAR_ARCHIVE); CVAR (Color, am_thingcolor_friend, 0xfcfcfc, CVAR_ARCHIVE); CVAR (Color, am_thingcolor_monster, 0xfcfcfc, CVAR_ARCHIVE); CVAR (Color, am_thingcolor_item, 0xfcfcfc, CVAR_ARCHIVE); @@ -897,6 +900,7 @@ static void AM_initColors (bool overlayed) { YourColor.FromCVar (am_ovyourcolor); WallColor.FromCVar (am_ovwallcolor); + SpecialWallColor.FromCVar(am_ovspecialwallcolor); SecretWallColor = WallColor; SecretSectorColor.FromCVar (am_ovsecretsectorcolor); ThingColor_Item.FromCVar (am_ovthingcolor_item); @@ -919,6 +923,7 @@ static void AM_initColors (bool overlayed) Background.FromCVar (am_backcolor); YourColor.FromCVar (am_yourcolor); SecretWallColor.FromCVar (am_secretwallcolor); + SpecialWallColor.FromCVar (am_specialwallcolor); WallColor.FromCVar (am_wallcolor); TSWallColor.FromCVar (am_tswallcolor); FDWallColor.FromCVar (am_fdwallcolor); @@ -960,6 +965,7 @@ static void AM_initColors (bool overlayed) AlmostBackground = DoomColors[2]; SecretSectorColor = SecretWallColor = + SpecialWallColor = WallColor = DoomColors[3]; TSWallColor = DoomColors[4]; FDWallColor = DoomColors[5]; @@ -981,6 +987,7 @@ static void AM_initColors (bool overlayed) AlmostBackground = DoomColors[2]; SecretSectorColor = SecretWallColor = + SpecialWallColor = WallColor = StrifeColors[3]; TSWallColor = StrifeColors[4]; FDWallColor = StrifeColors[5]; @@ -1002,6 +1009,7 @@ static void AM_initColors (bool overlayed) AlmostBackground = DoomColors[2]; SecretSectorColor = SecretWallColor = + SpecialWallColor = WallColor = RavenColors[3]; TSWallColor = RavenColors[4]; FDWallColor = RavenColors[5]; @@ -1836,14 +1844,16 @@ void AM_drawWalls (bool allmap) else if (lines[i].special == Door_LockedRaise || lines[i].special == ACS_LockedExecute || lines[i].special == ACS_LockedExecuteDoor || - (lines[i].special == Generic_Door && lines[i].args[4] !=0 )) + (lines[i].special == Door_Animated && lines[i].args[3] != 0) || + (lines[i].special == Generic_Door && lines[i].args[4] != 0)) { if (am_colorset == 0 || am_colorset == 3) // Raven games show door colors { int P_GetMapColorForLock(int lock); int lock; - if (lines[i].special==Door_LockedRaise) lock=lines[i].args[3]; + if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated) + lock=lines[i].args[3]; else lock=lines[i].args[4]; int color = P_GetMapColorForLock(lock); @@ -1860,6 +1870,17 @@ void AM_drawWalls (bool allmap) AM_drawMline (&l, LockedColor); // locked special } } + else if (am_showtriggerlines && am_colorset == 0 && lines[i].special != 0 + && lines[i].special != Door_Open + && lines[i].special != Door_Close + && lines[i].special != Door_CloseWaitOpen + && lines[i].special != Door_Raise + && lines[i].special != Door_Animated + && lines[i].special != Generic_Door + && (lines[i].activation & SPAC_PlayerActivate)) + { + AM_drawMline(&l, SpecialWallColor); // wall with special non-door action the player can do + } else if (lines[i].backsector == NULL) { AM_drawMline(&l, WallColor); // one-sided wall diff --git a/src/gi.h b/src/gi.h index 0af160b6b..645f5d3ba 100644 --- a/src/gi.h +++ b/src/gi.h @@ -46,8 +46,8 @@ #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 -#define GI_COMPATPORTAL 0x00000200 // same for stairbuilding +#define GI_NOTEXTCOLOR 0x00000100 // Chex Quest 3 would have everything green +#define GI_COMPATPORTAL 0x00000200 // Urban Brawl relies on the old portal code #include "gametype.h" diff --git a/src/p_setup.cpp b/src/p_setup.cpp index fa30df88f..7b45c8641 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1781,6 +1781,7 @@ void P_SetLineID (line_t *ld) { ld->id = ld->args[0]; } + ld->special = 0; break; case TranslucentLine: diff --git a/src/win32/zdoom.rc b/src/win32/zdoom.rc index b23fe14eb..55b6e9739 100644 --- a/src/win32/zdoom.rc +++ b/src/win32/zdoom.rc @@ -491,7 +491,7 @@ BEGIN VALUE "FileDescription", "ZDoom" VALUE "FileVersion", RC_FILEVERSION2 VALUE "InternalName", "ZDoom" - VALUE "LegalCopyright", "Copyright © 1993-1996 id Software, 1998-2007 Randy Heit" + VALUE "LegalCopyright", "Copyright © 1993-1996 id Software, 1998-2010 Randy Heit" VALUE "LegalTrademarks", "Doom® is a Registered Trademark of id Software, Inc." VALUE "OriginalFilename", "zdoom.exe" VALUE "ProductName", "ZDoom" diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index a833fb8b6..243029f1d 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -870,6 +870,7 @@ OptionMenu AutomapOptions Option "Show map label", "am_showmaplabel", "MaplabelTypes" Option "Draw map background", "am_drawmapback", "OnOff" Option "Show keys (cheat)", "am_showkeys", "OnOff" + Option "Show trigger lines", "am_showtriggerlines", "OnOff" } //------------------------------------------------------------------------------------------- @@ -922,6 +923,7 @@ OptionMenu MapColorMenu ColorPicker "Teleporter to the same map", "am_intralevelcolor" ColorPicker "Teleporter to a different map", "am_interlevelcolor" ColorPicker "Secret sector", "am_secretsectorcolor" + ColorPicker "Special trigger lines", "am_specialwallcolor" StaticText " " StaticText "Cheat Mode", 1 ColorPicker "Invisible 2-sided walls", "am_tswallcolor" @@ -939,6 +941,7 @@ OptionMenu MapColorMenu ColorPicker "Not-yet-seen walls", "am_ovunseencolor" ColorPicker "Teleporter", "am_ovtelecolor" ColorPicker "Secret sector", "am_ovsecretsectorcolor" + ColorPicker "Special trigger lines", "am_ovspecialwallcolor" StaticText " " StaticText "Overlay Cheat Mode", 1 ColorPicker "Actors", "am_ovthingcolor" From 772d59dff482769add2ffaa3bd12a5cf42ea2292 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 08:00:38 +0000 Subject: [PATCH 200/815] - fixed handling of missing backgrounds for intermission text screens for real this time. SVN r3041 (trunk) --- src/intermission/intermission.cpp | 7 +++++-- src/intermission/intermission_parse.cpp | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 1287f93cc..0c75575cd 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -110,8 +110,11 @@ void DIntermissionScreen::Init(FIntermissionAction *desc, bool first) { texname = GStrings[texname+1]; } - mBackground = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); - mFlatfill = desc->mFlatfill; + if (texname[0] != 0) + { + mBackground = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); + mFlatfill = desc->mFlatfill; + } S_Sound (CHAN_VOICE | CHAN_UI, desc->mSound, 1.0f, ATTN_NONE); if (desc->mPalette.IsNotEmpty() && (lumpnum = Wads.CheckNumForFullName(desc->mPalette, true)) > 0) { diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index bf6ae0ddd..183314cd7 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -833,7 +833,15 @@ void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int textscreen->mText << '$' << text; } textscreen->mTextDelay = 10; - textscreen->mBackground = flat; + if (flat != NULL && *flat != 0) + { + textscreen->mBackground = flat; + } + else + { + // force a black screen if no texture is set. + textscreen->mBackground = "-"; + } textscreen->mFlatfill = !finalePic; if (music != NULL && *music != 0) From b42952b85c438f8b975d30f40336258aa078df38 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 11:45:39 +0000 Subject: [PATCH 201/815] - added a 'restart' CCMD that allows restarting the engine with different WADs being loaded without quitting first SVN r3042 (trunk) --- src/c_cvars.cpp | 5 + src/c_cvars.h | 1 + src/d_main.cpp | 579 ++++++++++++++---------- src/d_main.h | 6 + src/dobjtype.cpp | 20 + src/dobjtype.h | 1 + src/g_game.cpp | 1 + src/g_shared/shared_sbar.cpp | 1 + src/intermission/intermission_parse.cpp | 1 + src/m_argv.cpp | 20 + src/m_argv.h | 1 + src/menu/menudef.cpp | 9 +- src/s_sndseq.cpp | 27 +- src/st_start.h | 12 +- src/v_palette.cpp | 2 + src/v_video.cpp | 59 +-- src/v_video.h | 2 +- 17 files changed, 466 insertions(+), 281 deletions(-) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 7b5387635..3afdb7c8c 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -600,6 +600,11 @@ void FBaseCVar::EnableCallbacks () } } +void FBaseCVar::DisableCallbacks () +{ + m_UseCallback = false; +} + // // Boolean cvar implementation // diff --git a/src/c_cvars.h b/src/c_cvars.h index 3b685948b..d0ac84fe5 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -117,6 +117,7 @@ public: static void EnableNoSet (); // enable the honoring of CVAR_NOSET static void EnableCallbacks (); + static void DisableCallbacks (); static void ResetColors (); // recalc color cvars' indices after screen change static void ListVars (const char *filter, bool plain); diff --git a/src/d_main.cpp b/src/d_main.cpp index 2b3e273a9..64cfd94a9 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -223,6 +223,8 @@ FTexture *Page; FTexture *Advisory; bool nospriterename; FStartupInfo DoomStartupInfo; +FString lastIWAD; +int restart = 0; cycle_t FrameCycles; @@ -890,7 +892,7 @@ void D_Display () // // D_ErrorCleanup () // -// Cleanup after a recoverable error. +// Cleanup after a recoverable error or a restart //========================================================================== void D_ErrorCleanup () @@ -2025,269 +2027,368 @@ void D_DoomMain (void) } FString basewad = wad; - // Load zdoom.pk3 alone so that we can get access to the internal gameinfos before - // the IWAD is known. - GetCmdLineFiles(pwads); - FString iwad = CheckGameInfo(pwads); + // reinit from here - FIWadManager *iwad_man = new FIWadManager; - const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad); - gameinfo.gametype = iwad_info->gametype; - gameinfo.flags = iwad_info->flags; - gameinfo.ConfigName = iwad_info->Configname; - - GameConfig->DoGameSetup (gameinfo.ConfigName); - - AddAutoloadFiles(iwad_info->Autoname); - - // Run automatically executed files - execFiles = new DArgs; - GameConfig->AddAutoexec (execFiles, gameinfo.ConfigName); - D_MultiExec (execFiles, true); - - // Run .cfg files at the start of the command line. - execFiles = Args->GatherFiles ("-exec"); - D_MultiExec (execFiles, true); - - C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - - CopyFiles(allwads, pwads); - - // Since this function will never leave we must delete this array here manually. - pwads.Clear(); - pwads.ShrinkToFit(); - - Printf ("W_Init: Init WADfiles.\n"); - Wads.InitMultipleFiles (allwads); - allwads.Clear(); - allwads.ShrinkToFit(); - SetMapxxFlag(); - - // [RH] Initialize localizable strings. - GStrings.LoadStrings (false); - - V_InitFontColors (); - - // [RH] Moved these up here so that we can do most of our - // startup output in a fullscreen console. - - CT_Init (); - - Printf ("I_Init: Setting up machine state.\n"); - I_Init (); - - Printf ("V_Init: allocate screen.\n"); - V_Init (); - - // Base systems have been inited; enable cvar callbacks - FBaseCVar::EnableCallbacks (); - - Printf ("S_Init: Setting up sound.\n"); - S_Init (); - - Printf ("ST_Init: Init startup screen.\n"); - StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); - - ParseCompatibility(); - - CheckCmdLine(); - - // [RH] Load sound environments - S_ParseReverbDef (); - - // [RH] Parse through all loaded mapinfo lumps - Printf ("G_ParseMapInfo: Load map definitions.\n"); - G_ParseMapInfo (iwad_info->MapInfo); - ReadStatistics(); - - // [RH] Parse any SNDINFO lumps - Printf ("S_InitData: Load sound definitions.\n"); - S_InitData (); - - Printf ("Texman.Init: Init texture manager.\n"); - TexMan.Init(); - C_InitConback(); - - // [CW] Parse any TEAMINFO lumps. - Printf ("ParseTeamInfo: Load team definitions.\n"); - TeamLibrary.ParseTeamInfo (); - - FActorInfo::StaticInit (); - - // [GRB] Initialize player class list - SetupPlayerClasses (); - - - // [RH] Load custom key and weapon settings from WADs - D_LoadWadSettings (); - - // [GRB] Check if someone used clearplayerclasses but not addplayerclass - if (PlayerClasses.Size () == 0) + do { - I_FatalError ("No player classes defined"); - } - - StartScreen->Progress (); - - Printf ("R_Init: Init %s refresh subsystem.\n", gameinfo.ConfigName.GetChars()); - StartScreen->LoadingStatus ("Loading graphics", 0x3f); - R_Init (); - - Printf ("DecalLibrary: Load decals.\n"); - DecalLibrary.ReadAllDecals (); - - // [RH] Add any .deh and .bex files on the command line. - // If there are none, try adding any in the config file. - // Note that the command line overrides defaults from the config. - - if ((ConsiderPatches("-deh") | ConsiderPatches("-bex")) == 0 && - gameinfo.gametype == GAME_Doom && GameConfig->SetSection ("Doom.DefaultDehacked")) - { - const char *key; - const char *value; - - while (GameConfig->NextInSection (key, value)) + if (restart) { - if (stricmp (key, "Path") == 0 && FileExists (value)) - { - Printf ("Applying patch %s\n", value); - D_LoadDehFile(value); - } + C_InitConsole(SCREENWIDTH, SCREENHEIGHT, false); } - } + nospriterename = false; - // Load embedded Dehacked patches - D_LoadDehLumps(); + // Load zdoom.pk3 alone so that we can get access to the internal gameinfos before + // the IWAD is known. - // Create replacements for dehacked pickups - FinishDehPatch(); + GetCmdLineFiles(pwads); + FString iwad = CheckGameInfo(pwads); - FActorInfo::StaticSetActorNums (); + // The IWAD selection dialogue dpes not show in fullscreen so if the + // restart is initiated without a defined IWAD assume for now that it's not going to change. + if (iwad.Len() == 0) iwad = lastIWAD; - //Added by MC: - bglobal.getspawned.Clear(); - argcount = Args->CheckParmList("-bots", &args); - for (p = 0; p < argcount; ++p) - { - bglobal.getspawned.Push(args[p]); - } - bglobal.spawn_tries = 0; - bglobal.wanted_botnum = bglobal.getspawned.Size(); + FIWadManager *iwad_man = new FIWadManager; + const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad); + gameinfo.gametype = iwad_info->gametype; + gameinfo.flags = iwad_info->flags; + gameinfo.ConfigName = iwad_info->Configname; + lastIWAD = iwad; - Printf ("M_Init: Init menus.\n"); - M_Init (); + FBaseCVar::DisableCallbacks(); + GameConfig->DoGameSetup (gameinfo.ConfigName); - Printf ("P_Init: Init Playloop state.\n"); - StartScreen->LoadingStatus ("Init game engine", 0x3f); - AM_StaticInit(); - P_Init (); + AddAutoloadFiles(iwad_info->Autoname); - P_SetupWeapons_ntohton(); + // Run automatically executed files + execFiles = new DArgs; + GameConfig->AddAutoexec (execFiles, gameinfo.ConfigName); + D_MultiExec (execFiles, true); - //SBarInfo support. - SBarInfo::Load(); - HUD_InitHud(); + // Run .cfg files at the start of the command line. + execFiles = Args->GatherFiles ("-exec"); + D_MultiExec (execFiles, true); - // [RH] User-configurable startup strings. Because BOOM does. - static const char *startupString[5] = { - "STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5" - }; - for (p = 0; p < 5; ++p) - { - const char *str = GStrings[startupString[p]]; - if (str != NULL && str[0] != '\0') - { - Printf ("%s\n", str); - } - } + C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - Printf ("D_CheckNetGame: Checking network game status.\n"); - StartScreen->LoadingStatus ("Checking network game status.", 0x3f); - D_CheckNetGame (); + CopyFiles(allwads, pwads); - // [RH] Lock any cvars that should be locked now that we're - // about to begin the game. - FBaseCVar::EnableNoSet (); + // Since this function will never leave we must delete this array here manually. + pwads.Clear(); + pwads.ShrinkToFit(); - delete iwad_man; // now we won't need this anymore - - // [RH] Run any saved commands from the command line or autoexec.cfg now. - gamestate = GS_FULLCONSOLE; - Net_NewMakeTic (); - DThinker::RunThinkers (); - gamestate = GS_STARTUP; - - // start the apropriate game based on parms - v = Args->CheckValue ("-record"); - - if (v) - { - G_RecordDemo (v); - autostart = true; - } - - delete StartScreen; - StartScreen = NULL; - - if (Args->CheckParm("-norun")) - { - throw CNoRunExit(); - } - - V_Init2(); - - v = Args->CheckValue("-playdemo"); - if (v != NULL) - { - singledemo = true; // quit after one demo - G_DeferedPlayDemo (v); - D_DoomLoop (); // never returns - } - - v = Args->CheckValue ("-timedemo"); - if (v) - { - G_TimeDemo (v); - D_DoomLoop (); // never returns - } + Printf ("W_Init: Init WADfiles.\n"); + Wads.InitMultipleFiles (allwads); + allwads.Clear(); + allwads.ShrinkToFit(); + SetMapxxFlag(); - v = Args->CheckValue ("-loadgame"); - if (v) - { - FString file(v); - FixPathSeperator (file); - DefaultExtension (file, ".zds"); - G_LoadGame (file); - } + // [RH] Initialize localizable strings. + GStrings.LoadStrings (false); - if (gameaction != ga_loadgame) - { - if (autostart || netgame) + V_InitFontColors (); + + // [RH] Moved these up here so that we can do most of our + // startup output in a fullscreen console. + + CT_Init (); + + if (!restart) { - // Do not do any screenwipes when autostarting a game. - if (!Args->CheckParm("-warpwipe")) + Printf ("I_Init: Setting up machine state.\n"); + I_Init (); + } + + Printf ("V_Init: allocate screen.\n"); + V_Init (!!restart); + + // Base systems have been inited; enable cvar callbacks + FBaseCVar::EnableCallbacks (); + + Printf ("S_Init: Setting up sound.\n"); + S_Init (); + + Printf ("ST_Init: Init startup screen.\n"); + if (!restart) StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); + else StartScreen = new FStartupScreen(0); + + ParseCompatibility(); + + CheckCmdLine(); + + // [RH] Load sound environments + S_ParseReverbDef (); + + // [RH] Parse through all loaded mapinfo lumps + Printf ("G_ParseMapInfo: Load map definitions.\n"); + G_ParseMapInfo (iwad_info->MapInfo); + ReadStatistics(); + + // [RH] Parse any SNDINFO lumps + Printf ("S_InitData: Load sound definitions.\n"); + S_InitData (); + + Printf ("Texman.Init: Init texture manager.\n"); + TexMan.Init(); + C_InitConback(); + + // [CW] Parse any TEAMINFO lumps. + Printf ("ParseTeamInfo: Load team definitions.\n"); + TeamLibrary.ParseTeamInfo (); + + FActorInfo::StaticInit (); + + // [GRB] Initialize player class list + SetupPlayerClasses (); + + + // [RH] Load custom key and weapon settings from WADs + D_LoadWadSettings (); + + // [GRB] Check if someone used clearplayerclasses but not addplayerclass + if (PlayerClasses.Size () == 0) + { + I_FatalError ("No player classes defined"); + } + + StartScreen->Progress (); + + Printf ("R_Init: Init %s refresh subsystem.\n", gameinfo.ConfigName.GetChars()); + StartScreen->LoadingStatus ("Loading graphics", 0x3f); + R_Init (); + + Printf ("DecalLibrary: Load decals.\n"); + DecalLibrary.ReadAllDecals (); + + // [RH] Add any .deh and .bex files on the command line. + // If there are none, try adding any in the config file. + // Note that the command line overrides defaults from the config. + + if ((ConsiderPatches("-deh") | ConsiderPatches("-bex")) == 0 && + gameinfo.gametype == GAME_Doom && GameConfig->SetSection ("Doom.DefaultDehacked")) + { + const char *key; + const char *value; + + while (GameConfig->NextInSection (key, value)) { - NoWipe = TICRATE; + if (stricmp (key, "Path") == 0 && FileExists (value)) + { + Printf ("Applying patch %s\n", value); + D_LoadDehFile(value); + } } - CheckWarpTransMap (startmap, true); - if (demorecording) - G_BeginRecording (startmap); - G_InitNew (startmap, false); + } + + // Load embedded Dehacked patches + D_LoadDehLumps(); + + // Create replacements for dehacked pickups + FinishDehPatch(); + + FActorInfo::StaticSetActorNums (); + + //Added by MC: + bglobal.getspawned.Clear(); + argcount = Args->CheckParmList("-bots", &args); + for (p = 0; p < argcount; ++p) + { + bglobal.getspawned.Push(args[p]); + } + bglobal.spawn_tries = 0; + bglobal.wanted_botnum = bglobal.getspawned.Size(); + + Printf ("M_Init: Init menus.\n"); + M_Init (); + + Printf ("P_Init: Init Playloop state.\n"); + StartScreen->LoadingStatus ("Init game engine", 0x3f); + AM_StaticInit(); + P_Init (); + + P_SetupWeapons_ntohton(); + + //SBarInfo support. + SBarInfo::Load(); + HUD_InitHud(); + + // [RH] User-configurable startup strings. Because BOOM does. + static const char *startupString[5] = { + "STARTUP1", "STARTUP2", "STARTUP3", "STARTUP4", "STARTUP5" + }; + for (p = 0; p < 5; ++p) + { + const char *str = GStrings[startupString[p]]; + if (str != NULL && str[0] != '\0') + { + Printf ("%s\n", str); + } + } + + if (!restart) + { + Printf ("D_CheckNetGame: Checking network game status.\n"); + StartScreen->LoadingStatus ("Checking network game status.", 0x3f); + D_CheckNetGame (); + } + + // [RH] Lock any cvars that should be locked now that we're + // about to begin the game. + FBaseCVar::EnableNoSet (); + + delete iwad_man; // now we won't need this anymore + + // [RH] Run any saved commands from the command line or autoexec.cfg now. + gamestate = GS_FULLCONSOLE; + Net_NewMakeTic (); + DThinker::RunThinkers (); + gamestate = GS_STARTUP; + + if (!restart) + { + // start the apropriate game based on parms + v = Args->CheckValue ("-record"); + + if (v) + { + G_RecordDemo (v); + autostart = true; + } + + delete StartScreen; + StartScreen = NULL; + + if (Args->CheckParm("-norun")) + { + throw CNoRunExit(); + } + + V_Init2(); + + v = Args->CheckValue("-playdemo"); + if (v != NULL) + { + singledemo = true; // quit after one demo + G_DeferedPlayDemo (v); + D_DoomLoop (); // never returns + } + + v = Args->CheckValue ("-timedemo"); + if (v) + { + G_TimeDemo (v); + D_DoomLoop (); // never returns + } + + v = Args->CheckValue ("-loadgame"); + if (v) + { + FString file(v); + FixPathSeperator (file); + DefaultExtension (file, ".zds"); + G_LoadGame (file); + } + + if (gameaction != ga_loadgame) + { + if (autostart || netgame) + { + // Do not do any screenwipes when autostarting a game. + if (!Args->CheckParm("-warpwipe")) + { + NoWipe = TICRATE; + } + CheckWarpTransMap (startmap, true); + if (demorecording) + G_BeginRecording (startmap); + G_InitNew (startmap, false); + } + else + { + D_StartTitle (); // start up intro loop + } + } + else if (demorecording) + { + G_BeginRecording (NULL); + } + + atterm (D_QuitNetGame); // killough } else { + // These calls from inside V_Init2 are still necessary + C_NewModeAdjust(); + M_InitVideoModesMenu(); D_StartTitle (); // start up intro loop + setmodeneeded = false; // This may be set to true here, but isn't needed for a restart + } + + try + { + D_DoomLoop (); // never returns + } + catch (CRestartException &ex) + { + // Music and sound should be stopped first + S_StopMusic(true); + S_StopAllChannels (); + + // clean up game state + ST_Clear(); + D_ErrorCleanup (); + P_FreeLevelData(); + P_FreeExtraLevelData(); + + M_SaveDefaults(NULL); // save config before the restart + + // delete all data that cannot be left until reinitialization + V_ClearFonts(); // must clear global font pointers + R_DeinitTranslationTables(); // some tables are initialized from outside the translation code. + gameinfo.~gameinfo_t(); + new (&gameinfo) gameinfo_t; // Reset gameinfo + S_Shutdown(); // free all channels and delete playlist + C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here + + GC::FullGC(); // perform one final garbage collection before deleting the class data + PClass::ClearRuntimeData(); // clear all runtime generated class data + restart++; } } - else if (demorecording) - { - G_BeginRecording (NULL); - } - - atterm (D_QuitNetGame); // killough + while (1); +} - D_DoomLoop (); // never returns +//========================================================================== +// +// restart the game +// +//========================================================================== + +CCMD(restart) +{ + // remove command line args that would get in the way during restart + Args->RemoveArgs("-iwad"); + Args->RemoveArgs("-deh"); + Args->RemoveArgs("-bex"); + Args->RemoveArgs("-playdemo"); + Args->RemoveArgs("-file"); + Args->RemoveArgs("-altdeath"); + Args->RemoveArgs("-deathmatch"); + Args->RemoveArgs("-skill"); + Args->RemoveArgs("-savedir"); + Args->RemoveArgs("-xlat"); + Args->RemoveArgs("-oldsprites"); + + if (argv.argc() > 1) + { + for(int i=1;iAppendArg(argv[i]); + } + } + + // initiate the restart + throw CRestartException(); } //========================================================================== @@ -2337,6 +2438,14 @@ void FStartupScreen::AppendStatusLine(const char *status) { } + +void FStartupScreen::Progress(void) {} +void FStartupScreen::NetInit(char const *,int) {} +void FStartupScreen::NetProgress(int) {} +void FStartupScreen::NetMessage(char const *,...) {} +void FStartupScreen::NetDone(void) {} +bool FStartupScreen::NetLoop(bool (*)(void *),void *) { return false; } + //========================================================================== // // STAT fps diff --git a/src/d_main.h b/src/d_main.h index 415a6b943..7dd75a107 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -36,6 +36,12 @@ struct event_t; // calls all startup code, parses command line options. // If not overrided by user input, calls N_AdvanceDemo. // + +struct CRestartException +{ + char dummy; +}; + void D_DoomMain (void); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index f232d81f2..03a8d2ded 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -80,6 +80,24 @@ void PClass::StaticInit () } } +void PClass::ClearRuntimeData () +{ + StaticShutdown(); + + m_RuntimeActors.Clear(); + m_Types.Clear(); + memset(TypeHash, 0, sizeof(TypeHash)); + bShutdown = false; + + // Immediately reinitialize the internal classes + FAutoSegIterator probe(CRegHead, CRegTail); + + while (*++probe != NULL) + { + ((ClassReg *)*probe)->RegisterClass (); + } +} + void PClass::StaticShutdown () { TArray uniqueFPs(64); @@ -105,6 +123,8 @@ void PClass::StaticShutdown () uniqueFPs.Push(const_cast(type->FlatPointers)); } } + type->FlatPointers = NULL; + // For runtime classes, this call will also delete the PClass. PClass::StaticFreeData (type); } diff --git a/src/dobjtype.h b/src/dobjtype.h index e01929901..256e52ad8 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -125,6 +125,7 @@ struct PClass static void StaticInit (); static void StaticShutdown (); static void StaticFreeData (PClass *type); + static void ClearRuntimeData(); // Per-class information ------------------------------------- FName TypeName; // this class's name diff --git a/src/g_game.cpp b/src/g_game.cpp index e10936f93..85bc45fce 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -2573,6 +2573,7 @@ bool G_CheckDemoStatus (void) C_RestoreCVars (); // [RH] Restore cvars demo might have changed M_Free (demobuffer); + demobuffer = NULL; P_SetupWeapons_ntohton(); demoplayback = false; diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 09fb9417b..3091d63e3 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -253,6 +253,7 @@ void DBaseStatusBar::Destroy () msg->Destroy(); msg = next; } + Messages = NULL; Super::Destroy(); } diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp index 183314cd7..fab4dca29 100644 --- a/src/intermission/intermission_parse.cpp +++ b/src/intermission/intermission_parse.cpp @@ -58,6 +58,7 @@ void DeinitIntermissions() delete pair->Value; pair->Value = NULL; } + IntermissionDescriptors.Clear(); } //========================================================================== diff --git a/src/m_argv.cpp b/src/m_argv.cpp index e414ca367..457a1b337 100644 --- a/src/m_argv.cpp +++ b/src/m_argv.cpp @@ -230,6 +230,26 @@ FString DArgs::TakeValue(const char *check) return out; } +//=========================================================================== +// +// DArgs :: RemoveArg +// +//=========================================================================== + +void DArgs::RemoveArgs(const char *check) +{ + int i = CheckParm(check); + + if (i > 0 && i < (int)Argv.Size() - 1) + { + do + { + RemoveArg(i); + } + while (Argv[i][0] != '+' && Argv[i][0] != '-' && i < (int)Argv.Size() - 1); + } +} + //=========================================================================== // // DArgs :: GetArg diff --git a/src/m_argv.h b/src/m_argv.h index 8b4fbf2dc..0fc107a05 100644 --- a/src/m_argv.h +++ b/src/m_argv.h @@ -54,6 +54,7 @@ public: void AppendArg(FString arg); void AppendArgs(int argc, const FString *argv); void RemoveArg(int argindex); + void RemoveArgs(const char *check); void SetArgs(int argc, char **argv); void CollectFiles(const char *param, const char *extension); DArgs *GatherFiles(const char *param) const; diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 2a8da7d88..f413a9475 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -85,6 +85,8 @@ static void DeinitMenus() pair->Value = NULL; } } + MenuDescriptors.Clear(); + OptionValues.Clear(); DMenu::CurrentMenu = NULL; DefaultListMenuSettings.mItems.Clear(); ClearSaveGames(); @@ -1236,10 +1238,11 @@ void M_CreateMenus() // THe skill menu must be refeshed each time it starts up // //============================================================================= +extern int restart; void M_StartupSkillMenu(FGameStartup *gs) { - static bool done = false; + static int done = -1; bool success = false; FMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu); if (desc != NULL) @@ -1265,9 +1268,9 @@ void M_StartupSkillMenu(FGameStartup *gs) } } - if (!done) + if (done != restart) { - done = true; + done = restart; int defskill = DefaultSkill; if ((unsigned int)defskill >= AllSkills.Size()) { diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 19076055e..da02bcab4 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -502,6 +502,24 @@ static void AssignHexenTranslations (void) } } +//========================================================================== +// +// S_ClearSndSeq +// +//========================================================================== + +void S_ClearSndSeq() +{ + for (unsigned int i = 0; i < Sequences.Size(); i++) + { + if (Sequences[i]) + { + M_Free(Sequences[i]); + } + } + Sequences.Clear(); +} + //========================================================================== // // S_ParseSndSeq @@ -523,14 +541,7 @@ void S_ParseSndSeq (int levellump) // First free the old SNDSEQ data. This allows us to reload this for each level // and specify a level specific SNDSEQ lump! - for (unsigned int i = 0; i < Sequences.Size(); i++) - { - if (Sequences[i]) - { - M_Free(Sequences[i]); - } - } - Sequences.Clear(); + S_ClearSndSeq(); // be gone, compiler warnings stopsound = 0; diff --git a/src/st_start.h b/src/st_start.h index 3b90aa06f..4ba475e73 100644 --- a/src/st_start.h +++ b/src/st_start.h @@ -42,15 +42,15 @@ public: FStartupScreen(int max_progress); virtual ~FStartupScreen(); - virtual void Progress() = 0; + virtual void Progress(); virtual void LoadingStatus(const char *message, int colors); // Used by Heretic only virtual void AppendStatusLine(const char *status); // Used by Heretic only - virtual void NetInit(const char *message, int num_players) = 0; - virtual void NetProgress(int count) = 0; - virtual void NetMessage(const char *format, ...) = 0; // cover for printf - virtual void NetDone() = 0; - virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) = 0; + virtual void NetInit(const char *message, int num_players); + virtual void NetProgress(int count); + virtual void NetMessage(const char *format, ...); // cover for printf + virtual void NetDone(); + virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata); protected: int MaxPos, CurPos, NotchPos; }; diff --git a/src/v_palette.cpp b/src/v_palette.cpp index 7761a3dec..4db1ad57c 100644 --- a/src/v_palette.cpp +++ b/src/v_palette.cpp @@ -447,6 +447,7 @@ void InitPalette () int lump; atterm (FreeSpecialLights); + FreeSpecialLights(); if ((lump = Wads.CheckNumForFullName ("palette.dat")) >= 0 && Wads.LumpLength (lump) >= 768) { @@ -780,6 +781,7 @@ static void FreeSpecialLights() delete[] colormap->Maps; delete colormap; } + NormalLight.Next = NULL; } // Builds NUMCOLORMAPS colormaps lit with the specified color diff --git a/src/v_video.cpp b/src/v_video.cpp index ae09f9b78..26632fb62 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1605,7 +1605,7 @@ CCMD (vid_setmode) // V_Init // -void V_Init (void) +void V_Init (bool restart) { const char *i; int width, height, bits; @@ -1615,40 +1615,43 @@ void V_Init (void) // [RH] Initialize palette management InitPalette (); - width = height = bits = 0; - - if ( (i = Args->CheckValue ("-width")) ) - width = atoi (i); - - if ( (i = Args->CheckValue ("-height")) ) - height = atoi (i); - - if ( (i = Args->CheckValue ("-bits")) ) - bits = atoi (i); - - if (width == 0) + if (!restart) { - if (height == 0) + width = height = bits = 0; + + if ( (i = Args->CheckValue ("-width")) ) + width = atoi (i); + + if ( (i = Args->CheckValue ("-height")) ) + height = atoi (i); + + if ( (i = Args->CheckValue ("-bits")) ) + bits = atoi (i); + + if (width == 0) { - width = vid_defwidth; - height = vid_defheight; + if (height == 0) + { + width = vid_defwidth; + height = vid_defheight; + } + else + { + width = (height * 8) / 6; + } } - else + else if (height == 0) { - width = (height * 8) / 6; + height = (width * 6) / 8; } - } - else if (height == 0) - { - height = (width * 6) / 8; + + if (bits == 0) + { + bits = vid_defbits; + } + screen = new DDummyFrameBuffer (width, height); } - if (bits == 0) - { - bits = vid_defbits; - } - - screen = new DDummyFrameBuffer (width, height); BuildTransTable (GPalette.BaseColors); } diff --git a/src/v_video.h b/src/v_video.h index f4fb6f5bf..ac7f6dea3 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -473,7 +473,7 @@ extern "C" DWORD Col2RGB8_Inverse[65][256]; // --111111111111111111111111111111 = 0x3FFFFFFF // Allocates buffer screens, call before R_Init. -void V_Init (); +void V_Init (bool restart); // Initializes graphics mode for the first time. void V_Init2 (); From 419a998bdf225b2ef2f9916e1e884c8fcdabdc7e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 18:57:39 +0000 Subject: [PATCH 202/815] - warning removal SVN r3043 (trunk) --- src/d_main.cpp | 3 ++- src/v_video.cpp | 10 ++++++++++ src/v_video.h | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 64cfd94a9..81bca294b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2328,9 +2328,10 @@ void D_DoomMain (void) { D_DoomLoop (); // never returns } - catch (CRestartException &ex) + catch (CRestartException &) { // Music and sound should be stopped first + screen->GameRestart(); S_StopMusic(true); S_StopAllChannels (); diff --git a/src/v_video.cpp b/src/v_video.cpp index 26632fb62..450650f53 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1384,6 +1384,16 @@ int DFrameBuffer::GetMaxViewPitch(bool down) return down? MAX_DN_ANGLE*ANGLE_1 : -MAX_UP_ANGLE*ANGLE_1; } +//========================================================================== +// +// DFrameBuffer :: GameRestart +// +//========================================================================== + +void DFrameBuffer::GameRestart() +{ +} + //=========================================================================== // // diff --git a/src/v_video.h b/src/v_video.h index ac7f6dea3..0fb9eaf8a 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -407,6 +407,9 @@ public: virtual void GetHitlist(BYTE *hitlist); virtual void PrecacheTexture(FTexture *tex, int cache); + // Report a game restart + virtual void GameRestart(); + // Screen wiping virtual bool WipeStartScreen(int type); virtual void WipeEndScreen(); From e33d1a989facdc949169692f862d046af9955a4c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 15 Dec 2010 23:10:08 +0000 Subject: [PATCH 203/815] - set all 'num' variables to 0 in P_FreeLevelData so that any code using them won't try to read the deleted map data. SVN r3044 (trunk) --- src/d_main.cpp | 3 ++- src/p_setup.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 81bca294b..aaeea7e3f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2317,6 +2317,8 @@ void D_DoomMain (void) } else { + // let the renderer reinitialize some stuff if needed + screen->GameRestart(); // These calls from inside V_Init2 are still necessary C_NewModeAdjust(); M_InitVideoModesMenu(); @@ -2331,7 +2333,6 @@ void D_DoomMain (void) catch (CRestartException &) { // Music and sound should be stopped first - screen->GameRestart(); S_StopMusic(true); S_StopAllChannels (); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 7b45c8641..a62f5b5fc 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3362,11 +3362,13 @@ void P_FreeLevelData () delete[] vertexes; vertexes = NULL; } + numvertexes = 0; if (segs != NULL) { delete[] segs; segs = NULL; } + numsegs = 0; if (glsegextras != NULL) { delete[] glsegextras; @@ -3377,8 +3379,8 @@ void P_FreeLevelData () delete[] sectors[0].e; delete[] sectors; sectors = NULL; - numsectors = 0; // needed for the pointer cleanup code } + numsectors = 0; if (gamenodes != NULL && gamenodes != nodes) { delete[] gamenodes; @@ -3411,11 +3413,14 @@ void P_FreeLevelData () delete[] lines; lines = NULL; } + numlines = 0; if (sides != NULL) { delete[] sides; sides = NULL; } + numsides = 0; + if (blockmaplump != NULL) { delete[] blockmaplump; @@ -3472,6 +3477,7 @@ void P_FreeLevelData () delete[] zones; zones = NULL; } + numzones = 0; P_FreeStrifeConversations (); if (level.Scrolls != NULL) { From 7e503d2f70c1a279af32ca1a152f6ac218470703 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Dec 2010 00:21:13 +0000 Subject: [PATCH 204/815] - fixed: D3DFB::Clear used the palette index even when a valid color was passed to it. SVN r3045 (trunk) --- src/win32/fb_d3d9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 7453571e5..cc036d540 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -2763,7 +2763,7 @@ void D3DFB::Clear (int left, int top, int right, int bottom, int palcolor, uint3 { return; } - if (palcolor >= 0) + if (palcolor >= 0 && color == 0) { color = GPalette.BaseColors[palcolor]; } From 19b8e15af41dc3909d0739beaf97f0c18081ba16 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Dec 2010 08:05:00 +0000 Subject: [PATCH 205/815] - added APROP_Dormant actor property for ACS (read only!) - fixed_ When performing a restart menus and intermissions need to be closed first. SVN r3048 (trunk) --- src/d_main.cpp | 3 +++ src/p_acs.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/d_main.cpp b/src/d_main.cpp index aaeea7e3f..f0a8a0c41 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2336,6 +2336,9 @@ void D_DoomMain (void) S_StopMusic(true); S_StopAllChannels (); + M_ClearMenus(); // close menu if open + F_EndFinale(); // If an intermission is active, end it now + // clean up game state ST_Clear(); D_ErrorCleanup (); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 34275da14..cd9d3c7a2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2541,6 +2541,7 @@ enum APROP_WaterLevel = 28, APROP_ScaleX = 29, APROP_ScaleY = 30, + APROP_Dormant = 31, }; // These are needed for ACS's APROP_RenderStyle @@ -2769,6 +2770,7 @@ int DLevelScript::GetActorProperty (int tid, int property) case APROP_Friendly: return !!(actor->flags & MF_FRIENDLY); case APROP_Notarget: return !!(actor->flags3 & MF3_NOTARGET); case APROP_Notrigger: return !!(actor->flags6 & MF6_NOTRIGGER); + case APROP_Dormant: return !!(actor->flags2 & MF2_DORMANT); case APROP_SpawnHealth: if (actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) { return static_cast(actor)->MaxHealth; @@ -2838,6 +2840,7 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_Friendly: case APROP_Notarget: case APROP_Notrigger: + case APROP_Dormant: return (GetActorProperty(tid, property) == (!!value)); // Strings are not covered by GetActorProperty, so make the check here From a06b88fa506e389a751a47d2e118b0444c355538 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Dec 2010 08:21:38 +0000 Subject: [PATCH 206/815] - added new IF_UNTOSSABLE flag for items that should be removable by any inventory function but not be droppable by the 'drop' CCMD. SVN r3049 (trunk) --- src/g_shared/a_pickups.cpp | 2 +- src/g_shared/a_pickups.h | 4 ++-- src/thingdef/thingdef_data.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index f059d8d3a..baa37d56c 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -628,7 +628,7 @@ AInventory *AInventory::CreateTossable () { return NULL; } - if ((ItemFlags & IF_UNDROPPABLE) || Owner == NULL || Amount <= 0) + if ((ItemFlags & (IF_UNDROPPABLE|IF_UNTOSSABLE)) || Owner == NULL || Amount <= 0) { return NULL; } diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 3afcbf5b7..23cac960a 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -116,10 +116,10 @@ enum IF_PICKUPGOOD = 1<<2, // HandlePickup wants normal pickup FX to happen IF_QUIET = 1<<3, // Don't give feedback when picking up IF_AUTOACTIVATE = 1<<4, // Automatically activate item on pickup - IF_UNDROPPABLE = 1<<5, // The player cannot manually drop the item + IF_UNDROPPABLE = 1<<5, // Item cannot be removed unless done explicitly with RemoveInventory IF_INVBAR = 1<<6, // Item appears in the inventory bar IF_HUBPOWER = 1<<7, // Powerup is kept when moving in a hub -// IF_INTERHUBSTRIP = 1<<8, // Item is removed when travelling between hubs + IF_UNTOSSABLE = 1<<8, // The player cannot manually drop the item IF_ADDITIVETIME = 1<<9, // when picked up while another item is active, time is added instead of replaced. IF_ALWAYSPICKUP = 1<<10, // For IF_AUTOACTIVATE, MaxAmount=0 items: Always "pick up", even if it doesn't do anything IF_FANCYPICKUPSOUND = 1<<11, // Play pickup sound in "surround" mode diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 30c35a0a4..f85d5cd66 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -279,12 +279,13 @@ static FFlagDef InventoryFlags[] = DEFINE_FLAG(IF, UNDROPPABLE, AInventory, ItemFlags), DEFINE_FLAG(IF, INVBAR, AInventory, ItemFlags), DEFINE_FLAG(IF, HUBPOWER, AInventory, ItemFlags), + DEFINE_FLAG(IF, UNTOSSABLE, AInventory, ItemFlags), + DEFINE_FLAG(IF, ADDITIVETIME, AInventory, ItemFlags), DEFINE_FLAG(IF, ALWAYSPICKUP, AInventory, ItemFlags), DEFINE_FLAG(IF, FANCYPICKUPSOUND, AInventory, ItemFlags), DEFINE_FLAG(IF, BIGPOWERUP, AInventory, ItemFlags), DEFINE_FLAG(IF, KEEPDEPLETED, AInventory, ItemFlags), DEFINE_FLAG(IF, IGNORESKILL, AInventory, ItemFlags), - DEFINE_FLAG(IF, ADDITIVETIME, AInventory, ItemFlags), DEFINE_FLAG(IF, NOATTENPICKUPSOUND, AInventory, ItemFlags), DEFINE_FLAG(IF, PERSISTENTPOWER, AInventory, ItemFlags), From 6d614af4f4087929c2956d9d47da663043b38469 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Dec 2010 09:13:06 +0000 Subject: [PATCH 207/815] - added a 'noautosavehint' MAPINFO option. This does not actually block the autosave from happening. It just does not increase the autosave counter so any subsequent autosave in the same session will overwrite the last one which was saved with this hint on. SVN r3050 (trunk) --- src/g_game.cpp | 20 ++++++++++++++++++-- src/g_level.h | 1 + src/g_mapinfo.cpp | 1 + src/p_lnspec.cpp | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 85bc45fce..1d735ee03 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1904,6 +1904,7 @@ FString G_BuildSaveName (const char *prefix, int slot) } CVAR (Int, autosavenum, 0, CVAR_NOSET|CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +static int nextautosave = -1; CVAR (Int, disableautosave, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CUSTOM_CVAR (Int, autosavecount, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -1924,10 +1925,25 @@ void G_DoAutoSave () const char *readableTime; int count = autosavecount != 0 ? autosavecount : 1; - num.Int = (autosavenum + 1) % count; + if (nextautosave == -1) + { + nextautosave = (autosavenum + 1) % count; + } + + num.Int = nextautosave; autosavenum.ForceSet (num, CVAR_Int); - file = G_BuildSaveName ("auto", num.Int); + file = G_BuildSaveName ("auto", nextautosave); + + if (!(level.flags2 & LEVEL2_NOAUTOSAVEHINT)) + { + nextautosave = (nextautosave + 1) % count; + } + else + { + // This flag can only be used once per level + level.flags2 &= ~LEVEL2_NOAUTOSAVEHINT; + } readableTime = myasctime (); strcpy (description, "Autosave "); diff --git a/src/g_level.h b/src/g_level.h index 6cb9d8441..34c2bf5f8 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -212,6 +212,7 @@ enum ELevelFlags LEVEL2_NOSTATISTICS = 0x10000000, // This level should not have statistics collected LEVEL2_ENDGAME = 0x20000000, // This is an epilogue level that cannot be quit. + LEVEL2_NOAUTOSAVEHINT = 0x40000000, // tell the game that an autosave for this level does not need to be kept }; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 4199305d3..b4183ef2f 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1238,6 +1238,7 @@ MapFlagHandlers[] = { "resethealth", MITYPE_SETFLAG2, LEVEL2_RESETHEALTH, 0 }, { "endofgame", MITYPE_SETFLAG2, LEVEL2_ENDGAME, 0 }, { "nostatistics", MITYPE_SETFLAG2, LEVEL2_NOSTATISTICS, 0 }, + { "noautosavehint", MITYPE_SETFLAG2, LEVEL2_NOAUTOSAVEHINT, 0 }, { "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 }, { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes { "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX}, diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index ac7520170..1b3f627a4 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2798,6 +2798,7 @@ FUNC(LS_Autosave) { if (gameaction != ga_savegame) { + level.flags2 &= ~LEVEL2_NOAUTOSAVEHINT; Net_WriteByte (DEM_CHECKAUTOSAVE); } return true; From 42de20a7e44ccad93da8fbee69328201cb6f74c4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Dec 2010 14:39:17 +0000 Subject: [PATCH 208/815] - fixed: The Floor_RaiseAndCrush types in Doom wait if blocked, therefore need Hexen crush mode. - fixed: Angle conversion in P_SpawnMapThing did not work for negative values due to use of an unsigned multiplication. SVN r3053 (trunk) --- src/p_mobj.cpp | 2 +- wadsrc/static/xlat/base.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 0b13c8c75..fbf6b28ae 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4336,7 +4336,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->tid = mthing->thingid; mobj->AddToHash (); - mobj->PrevAngle = mobj->angle = (DWORD)((mthing->angle * UCONST64(0x100000000)) / 360); + mobj->PrevAngle = mobj->angle = (DWORD)((mthing->angle * CONST64(0x100000000)) / 360); // Check if this actor's mapthing has a conversation defined if (mthing->Conversation > 0) diff --git a/wadsrc/static/xlat/base.txt b/wadsrc/static/xlat/base.txt index c4e0cc083..56fa88448 100644 --- a/wadsrc/static/xlat/base.txt +++ b/wadsrc/static/xlat/base.txt @@ -54,8 +54,8 @@ include "xlat/defines.i" 52 = WALK, Exit_Normal (0) 53 = WALK, Plat_PerpetualRaiseLip (tag, P_SLOW, PLATWAIT, 0) 54 = WALK, Plat_Stop (tag) - 55 = USE, Floor_RaiseAndCrush (tag, F_SLOW, 10) - 56 = WALK, Floor_RaiseAndCrush (tag, F_SLOW, 10) + 55 = USE, Floor_RaiseAndCrush (tag, F_SLOW, 10, 2) + 56 = WALK, Floor_RaiseAndCrush (tag, F_SLOW, 10, 2) 57 = WALK, Ceiling_CrushStop (tag) 58 = WALK, Floor_RaiseByValue (tag, F_SLOW, 24) 59 = WALK, Floor_RaiseByValueTxTy (tag, F_SLOW, 24) @@ -64,7 +64,7 @@ include "xlat/defines.i" 62 = USE|REP, Plat_DownWaitUpStayLip (tag, P_FAST, PLATWAIT, 0) 63 = USE|REP, Door_Raise (tag, D_SLOW, VDOORWAIT) 64 = USE|REP, Floor_RaiseToLowestCeiling (tag, F_SLOW) - 65 = USE|REP, Floor_RaiseAndCrush (tag, F_SLOW, 10) + 65 = USE|REP, Floor_RaiseAndCrush (tag, F_SLOW, 10, 2) 66 = USE|REP, Plat_UpByValueStayTx (tag, P_SLOW/2, 3) 67 = USE|REP, Plat_UpByValueStayTx (tag, P_SLOW/2, 4) 68 = USE|REP, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) @@ -93,7 +93,7 @@ include "xlat/defines.i" 91 = WALK|REP, Floor_RaiseToLowestCeiling (tag, F_SLOW) 92 = WALK|REP, Floor_RaiseByValue (tag, F_SLOW, 24) 93 = WALK|REP, Floor_RaiseByValueTxTy (tag, F_SLOW, 24) - 94 = WALK|REP, Floor_RaiseAndCrush (tag, F_SLOW, 10) + 94 = WALK|REP, Floor_RaiseAndCrush (tag, F_SLOW, 10, 2) 95 = WALK|REP, Plat_RaiseAndStayTx0 (tag, P_SLOW/2) 96 = WALK|REP, Floor_RaiseByTexture (tag, F_SLOW) 97 = WALK|REP|MONST, Teleport (0, tag) From c7c4377600bbe4ccb2a10c9ac4120b001fe678ca Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 17 Dec 2010 22:30:47 +0000 Subject: [PATCH 209/815] - clamp parameter to P_GiveBody to prevent overflows. SVN r3058 (trunk) --- src/g_shared/a_pickups.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index baa37d56c..f15da2f3a 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -184,6 +184,7 @@ bool P_GiveBody (AActor *actor, int num) int max; player_t *player = actor->player; + num = clamp(num, -65536, 65536); // prevent overflows for bad values if (player != NULL) { max = static_cast(actor)->GetMaxHealth() + player->stamina; From bd0378a44bdef0e64bf0b8661cfe53498b1b3fc4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 20 Dec 2010 10:07:44 +0000 Subject: [PATCH 210/815] - added Gez's Hexenarmor on AltHud submission. SVN r3061 (trunk) --- src/g_shared/shared_hud.cpp | 59 ++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 9901c7940..72e780f52 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -283,16 +283,42 @@ static void DrawHealth(int health, int x, int y) //=========================================================================== // // Draw Armor. -// very similar to drawhealth. +// very similar to drawhealth, but adapted to handle Hexen armor too // //=========================================================================== -static void DrawArmor(AInventory * armor, int x, int y) +static void DrawArmor(ABasicArmor * barmor, AHexenArmor * harmor, int x, int y) { - if (armor) - { - int ap=armor->Amount; + int ap = 0; + int bestslot = 4; + if (harmor) + { + int ac = (harmor->Slots[0] + harmor->Slots[1] + harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]); + ac >>= FRACBITS; + ap += ac; + + if (ac) + { + // Find the part of armor that protects the most + bestslot = 0; + for (int i = 1; i < 4; ++i) + { + if (harmor->Slots[i] > harmor->Slots[bestslot]) + { + bestslot = i; + } + } + } + } + + if (barmor) + { + ap += barmor->Amount; + } + + if (ap) + { // decide on color int fontcolor = ap < hud_armor_red ? CR_RED : @@ -301,11 +327,23 @@ static void DrawArmor(AInventory * armor, int x, int y) CR_BLUE; - if (ap) + // Use the sprite of one of the predefined Hexen armor bonuses. + // This is not a very generic approach, but it is not possible + // to truly create new types of Hexen armor bonus items anyway. + if (harmor && bestslot < 4) { - DrawImageToBox(TexMan[armor->Icon], x, y, 31, 17); - DrawHudNumber(HudFont, fontcolor, ap, x + 33, y + 17); + char icon[] = "AR_1A0"; + switch (bestslot) + { + case 1: icon[3] = '2'; break; + case 2: icon[3] = '3'; break; + case 3: icon[3] = '4'; break; + default: break; + } + DrawImageToBox(TexMan.FindTexture(icon, FTexture::TEX_Sprite), x, y, 31, 17); } + else if (barmor) DrawImageToBox(TexMan[barmor->Icon], x, y, 31, 17); + DrawHudNumber(HudFont, fontcolor, ap, x + 33, y + 17); } } @@ -815,9 +853,8 @@ void DrawHUD() DrawFrags(CPlayer, 5, hudheight-70); } DrawHealth(CPlayer->health, 5, hudheight-45); - // Yes, that doesn't work properly for Hexen but frankly, I have no - // idea how to make a meaningful value out of Hexen's armor system! - DrawArmor(CPlayer->mo->FindInventory(RUNTIME_CLASS(ABasicArmor)), 5, hudheight-20); + DrawArmor(CPlayer->mo->FindInventory(), + CPlayer->mo->FindInventory(), 5, hudheight-20); i=DrawKeys(CPlayer, hudwidth-4, hudheight-10); i=DrawAmmo(CPlayer, hudwidth-5, i); DrawWeapons(CPlayer, hudwidth-5, i); From fb7a45efe4f03dd76e2569443cbef3f9b9421f3d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 20 Dec 2010 19:13:12 +0000 Subject: [PATCH 211/815] - backport ACS CheckActorProperty fix from GZDoom. SVN r3064 (trunk) --- src/p_acs.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index cd9d3c7a2..63c3f0a6c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2829,6 +2829,9 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) case APROP_MasterTID: case APROP_TargetTID: case APROP_TracerTID: + case APROP_WaterLevel: + case APROP_ScaleX: + case APROP_ScaleY: return (GetActorProperty(tid, property) == value); // Boolean values need to compare to a binary version of value From bada44a291facbd89a133c72304749bfe9563a86 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Mon, 20 Dec 2010 22:29:15 +0000 Subject: [PATCH 212/815] - Fixed: The background on some bars was cliped incorrectly. SVN r3065 (trunk) --- src/g_shared/sbarinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index a552a3ad1..c469bb736 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1190,7 +1190,7 @@ public: } if(clearDontDraw) - screen->Clear(static_cast(MAX(dx, dcx)), static_cast(MAX(dy, dcy)), static_cast(dcr), static_cast(dcb), GPalette.BlackIndex, 0); + screen->Clear(static_cast(MAX(dx, dcx)), static_cast(MAX(dy, dcy)), static_cast(MIN(dcr,w+MAX(dx, dcx))), static_cast(MIN(dcb,MAX(dy, dcy)+h)), GPalette.BlackIndex, 0); else { if(alphaMap) From f6c1f359e154f0fc1c423db32478bd9596cd9c97 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 21 Dec 2010 08:39:58 +0000 Subject: [PATCH 213/815] - fixed: CreateCachedNodes did not free its file buffer. SVN r3070 (trunk) --- src/p_glnodes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_glnodes.cpp b/src/p_glnodes.cpp index c48d72e8f..4d34c7047 100644 --- a/src/p_glnodes.cpp +++ b/src/p_glnodes.cpp @@ -1223,6 +1223,7 @@ static void CreateCachedNodes(MapData *map) FILE *f = fopen(path, "wb"); fwrite(compressed, 1, outlen+offset, f); fclose(f); + delete [] compressed; } From e27179afd0ee7363062341bb6384d07e87754472 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 21 Dec 2010 14:15:19 +0000 Subject: [PATCH 214/815] - added Chris's crashcatcher improvements. SVN r3071 (trunk) --- src/sdl/crashcatcher.c | 91 ++++++++---------------------------------- 1 file changed, 17 insertions(+), 74 deletions(-) diff --git a/src/sdl/crashcatcher.c b/src/sdl/crashcatcher.c index 7dc928407..53f3c6a67 100644 --- a/src/sdl/crashcatcher.c +++ b/src/sdl/crashcatcher.c @@ -97,8 +97,7 @@ static void gdb_info(pid_t pid) strcpy(respfile, "gdb-respfile-XXXXXX"); if((fd = mkstemp(respfile)) >= 0 && (f = fdopen(fd, "w"))) { - fprintf(f, "signal SIGCHLD\n" - "shell echo \"\"\n" + fprintf(f, "shell echo \"\"\n" "shell echo \"* Loaded Libraries\"\n" "info sharedlibrary\n" "shell echo \"\"\n" @@ -115,36 +114,8 @@ static void gdb_info(pid_t pid) "x/x $eip-3\nx/x $eip\n" "shell echo \"\"\n" "shell echo \"* Backtrace\"\n" - "backtrace full\n" -#if 0 /* This sorta works to print out the core, but is too slow and skips 0's.. */ - "shell echo \"\"\n" - "shell echo \"* Stack\"\n" - "set var $_sp = $esp\n" - "while $_sp <= $ebp - 12\n" - " printf \"%%08x: \", $_sp\n" - " set var $_i = $_sp\n" - " while $_i < $_sp + 16\n" - " printf \"%%08x \", {int} $_i\n" - " set $_i += 4\n" - " end\n" - " set var $_i = $_sp\n" - " while $_i < $_sp + 16\n" - " printf \"%%c\", {int} $_i\n" - " set ++$_i\n" - " end\n" - " set var $_sp += 16\n" - " printf \"\\n\"\n" - "end\n" - "if $_sp <= $ebp\n" - " printf \"%%08x: \", $esp\n" - " while $_sp <= $ebp\n" - " printf \"%%08x \", {int} $_i\n" - " set $_sp += 4\n" - " end\n" - " printf \"\\n\"\n" - "end\n" -#endif - "kill\n" + "thread apply all backtrace full\n" + "detach\n" "quit\n"); fclose(f); @@ -152,8 +123,8 @@ static void gdb_info(pid_t pid) snprintf(buf, sizeof(buf), "gdb --quiet --batch --command=%s --pid=%i", respfile, pid); printf("Executing: %s\n", buf); fflush(stdout); - system(buf); + system(buf); /* Clean up */ remove(respfile); } @@ -168,6 +139,7 @@ static void gdb_info(pid_t pid) printf("Could not create gdb command file\n"); } fflush(stdout); + kill(pid, SIGKILL); } @@ -267,8 +239,8 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) /* Make sure the effective uid is the real uid */ if (getuid() != geteuid()) { - fprintf(stderr, "%s (signal %i)\ngetuid() does not match geteuid().\n", sigdesc, signum); - _exit(-1); + raise(signum); + return; } #endif @@ -325,48 +297,18 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) } gdb_info(pid); -#if 0 /* Why won't this work? */ - if(ucontext) - { - unsigned char *ptr = ucontext->uc_stack.ss_sp; - size_t len; - - fprintf(f, "\n* Stack\n"); - for(len = ucontext->uc_stack.ss_size/4;len > 0; len -= 4) - { - fprintf(f, "0x%08x:", (int)ptr); - for(i = 0;i < ((len < 4) ? len : 4);++i) - { - fprintf(f, " %02x%02x%02x%02x", ptr[i*4 + 0], ptr[i*4 + 1], - ptr[i*4 + 2], ptr[i*4 + 3]); - } - fputc(' ', f); - fflush(f); - for(i = 0;i < ((len < 4) ? len : 4);++i) - { - fprintf(f, "%c", *(ptr++)); - fprintf(f, "%c", *(ptr++)); - fprintf(f, "%c", *(ptr++)); - fprintf(f, "%c", *(ptr++)); - } - fputc('\n', f); - fflush(f); - } - } -#endif - if(f != stderr) { fclose(f); #if (defined __unix__) if(cc_logfile) { - char buf[256]; + char buf[512]; snprintf(buf, sizeof(buf), - "if (which gxmessage > /dev/null 2>&1);" - "then gxmessage -buttons \"Damn it:0\" -center -title \"Very Fatal Error\" -file %s;" - "elif (which xmessage > /dev/null 2>&1);" - "then xmessage -buttons \"Damn it:0\" -center -file %s -geometry 600x400;" + "if (which gxmessage > /dev/null 2>&1) ; then\n" + " gxmessage -buttons \"Damn it:0\" -center -title \"Very Fatal Error\" -file %s\n" + "elif (which xmessage > /dev/null 2>&1) ; then\n" + " xmessage -buttons \"Damn it:0\" -center -file %s -geometry 600x400\n" "fi", cc_logfile, cc_logfile); system(buf); } @@ -376,8 +318,8 @@ static void crash_catcher(int signum, siginfo_t *siginfo, void *context) _exit(0); default: - /* Wait and let the child attach gdb */ - waitpid(dbg_pid, NULL, 0); + /* Wait indefinitely; we'll be killed when gdb is done */ + while(1) usleep(1000000); } } @@ -388,12 +330,13 @@ int cc_install_handlers(int num_signals, int *signals, const char *logfile, int memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crash_catcher; - -#if !defined(__FreeBSD__) && !defined(__APPLE__) + +#ifdef SA_ONESHOT sa.sa_flags = SA_ONESHOT | SA_NODEFER | SA_SIGINFO; #else sa.sa_flags = SA_NODEFER | SA_SIGINFO; #endif + sigemptyset(&sa.sa_mask); cc_logfile = logfile; cc_user_info = user_info; From e46183d8364df1ec4c233746e2d11859ad1fac64 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 24 Dec 2010 13:43:36 +0000 Subject: [PATCH 215/815] - removed all portal fudging that was necessary to make thing based portals work the same as line based portals. Using an actor flag on the skybox thing the visplane code now checks what kind of portal is used and uses the proper logic accordingly. As a result the "Portals" compatibility flag no longer exists. SVN r3072 (trunk) --- src/compatibility.cpp | 7 ---- src/d_iwad.cpp | 1 - src/gi.h | 1 - src/p_spec.cpp | 78 -------------------------------------- src/r_plane.cpp | 2 +- wadsrc/static/iwadinfo.txt | 1 - 6 files changed, 1 insertion(+), 89 deletions(-) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index cd74f5e34..718bfecee 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -90,7 +90,6 @@ static FCompatOption Options[] = { "resetplayerspeed", 0, BCOMPATF_RESETPLAYERSPEED }, { "vileghosts", 0, BCOMPATF_VILEGHOSTS }, { "ignoreteleporttags", 0, BCOMPATF_BADTELEPORTERS }, - { "oldportals", 0, BCOMPATF_BADPORTALS }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, 0 }, @@ -284,12 +283,6 @@ void CheckCompatibility(MapData *map) ib_compatflags = 0; ii_compatparams = -1; } - else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPORTAL)) - { - ii_compatflags = 0; - ib_compatflags = BCOMPATF_BADPORTALS; - ii_compatparams = -1; - } else { map->GetChecksum(md5.Bytes); diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 0c7cc3bf2..0999a77ea 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -174,7 +174,6 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize) else if(sc.Compare("Extended")) iwad->flags |= GI_MENUHACK_EXTENDED; else if(sc.Compare("Shorttex")) iwad->flags |= GI_COMPATSHORTTEX; else if(sc.Compare("Stairs")) iwad->flags |= GI_COMPATSTAIRS; - else if(sc.Compare("Portals")) iwad->flags |= GI_COMPATPORTAL; else sc.ScriptError(NULL); } while (sc.CheckString(",")); diff --git a/src/gi.h b/src/gi.h index 645f5d3ba..647db1a9b 100644 --- a/src/gi.h +++ b/src/gi.h @@ -47,7 +47,6 @@ #define GI_COMPATPOLY1 0x00000040 // Hexen's MAP36 needs old polyobject drawing #define GI_COMPATPOLY2 0x00000080 // so does HEXDD's MAP47 #define GI_NOTEXTCOLOR 0x00000100 // Chex Quest 3 would have everything green -#define GI_COMPATPORTAL 0x00000200 // Urban Brawl relies on the old portal code #include "gametype.h" diff --git a/src/p_spec.cpp b/src/p_spec.cpp index dd3e0ca11..cf97c16fc 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -921,39 +921,6 @@ static bool SpreadCeilingPortal(AStackPoint *pt, fixed_t alpha, sector_t *sector return fail; } -static bool SpreadFloorPortal(AStackPoint *pt, fixed_t alpha, sector_t *sector) -{ - bool fail = false; - sector->validcount = validcount; - for(int i=0; ilinecount; i++) - { - line_t *line = sector->lines[i]; - sector_t *backsector = sector == line->frontsector? line->backsector : line->frontsector; - if (line->backsector == line->frontsector) continue; - if (backsector == NULL) { fail = true; continue; } - if (backsector->validcount == validcount) continue; - if (backsector->FloorSkyBox == pt) continue; - - // Check if the backside would map to the same visplane - if (backsector->FloorSkyBox != NULL) { fail = true; continue; } - if (backsector->floorplane != sector->ceilingplane) { fail = true; continue; } - if (backsector->lightlevel != sector->lightlevel) { fail = true; continue; } - if (backsector->GetTexture(sector_t::floor) != sector->GetTexture(sector_t::floor)) { fail = true; continue; } - if (backsector->GetXOffset(sector_t::floor) != sector->GetXOffset(sector_t::floor)) { fail = true; continue; } - if (backsector->GetYOffset(sector_t::floor) != sector->GetYOffset(sector_t::floor)) { fail = true; continue; } - if (backsector->GetXScale(sector_t::floor) != sector->GetXScale(sector_t::floor)) { fail = true; continue; } - if (backsector->GetYScale(sector_t::floor) != sector->GetYScale(sector_t::floor)) { fail = true; continue; } - if (backsector->GetAngle(sector_t::floor) != sector->GetAngle(sector_t::floor)) { fail = true; continue; } - if (SpreadFloorPortal(pt, alpha, backsector)) { fail = true; continue; } - } - if (!fail) - { - sector->FloorSkyBox = pt; - sector->SetAlpha(sector_t::floor, alpha); - } - return fail; -} - void P_SetupPortals() { TThinkerIterator it; @@ -974,51 +941,6 @@ void P_SetupPortals() pt->special1 = 0; points.Push(pt); } - - // Maps using undefined portal hacks may not benefit from portal optimizations. - if (ib_compatflags & BCOMPATF_BADPORTALS) return; - - for(unsigned i=0;ispecial1 == 0 && points[i]->Mate != NULL) - { - for(unsigned j=1;jspecial1 == 0 && points[j]->Mate != NULL && points[i]->GetClass() == points[j]->GetClass()) - { - fixed_t deltax1 = points[i]->Mate->x - points[i]->x; - fixed_t deltay1 = points[i]->Mate->y - points[i]->y; - fixed_t deltax2 = points[j]->Mate->x - points[j]->x; - fixed_t deltay2 = points[j]->Mate->y - points[j]->y; - - if (deltax1 == deltax2 && deltay1 == deltay2) - { - if (points[j]->Sector->FloorSkyBox == points[j]->Mate) - points[j]->Sector->FloorSkyBox = points[i]->Mate; - - if (points[j]->Sector->CeilingSkyBox == points[j]->Mate) - points[j]->Sector->CeilingSkyBox = points[i]->Mate; - - points[j]->special1 = 1; - } - } - } - } - } - validcount++; - // Some fudging to preserve an unintended 'portal bleeding' effect caused by incomplete checks in the rendering code. - // Due to the addition of linedef-based portals this effect no longer works. - for(int i=0;ibAlways && sectors[i].validcount != validcount) - { - SpreadCeilingPortal(barrier_cast(sectors[i].CeilingSkyBox), sectors[i].GetAlpha(sector_t::ceiling), §ors[i]); - } - if (sectors[i].FloorSkyBox != NULL && sectors[i].FloorSkyBox->bAlways && sectors[i].validcount != validcount) - { - SpreadFloorPortal(barrier_cast(sectors[i].FloorSkyBox), sectors[i].GetAlpha(sector_t::floor), §ors[i]); - } - } } inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t alpha) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 3fa6bdf73..99872001a 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -584,7 +584,7 @@ visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightl check->viewz == stacked_viewz && ( // headache inducing logic... :( - (ib_compatflags & BCOMPATF_BADPORTALS) || + (!(skybox->flags & MF_JUSTATTACKED)) || ( check->alpha == alpha && (alpha == 0 || // if alpha is > 0 everything needs to be checked diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 7a94f244a..efdf836a5 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -26,7 +26,6 @@ IWad Game = "Doom" Config = "UrbanBrawl" Mapinfo = "mapinfo/urbanbrawl.txt" - Compatibility = "Portals" MustContain = "MAP01", "AD2LIB" BannerColors = "a8 a8 00", "a8 00 00" } From 231e7a1c6d32d1c92fe2177fe8a7aa3b759ba886 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 25 Dec 2010 23:27:26 +0000 Subject: [PATCH 216/815] - added a new render style 'Shadow'. Essentially it's just a black translucent stencil with an alpha of 0.3. The purpose of this style is to be used as a software renderer approximation of GZDoom's spectre effect. - allow setting 'Shadow' as default fuzz effect - changed CVAR conversion that strings 'false' and 'true' get evaluated as integers 0 and 1 respectively so that changing boolean CVARs to int does not destroy their values. SVN r3076 (trunk) --- src/c_cvars.cpp | 18 ++++++++-- src/g_shared/a_artifacts.cpp | 2 -- src/p_mobj.cpp | 1 - src/r_blend.h | 4 +++ src/r_draw.cpp | 49 ++++++++++++++++++++++++---- src/r_things.cpp | 2 +- src/thingdef/thingdef_properties.cpp | 4 +-- src/win32/fb_d3d9.cpp | 8 +++++ wadsrc/static/menudef.txt | 9 ++++- 9 files changed, 81 insertions(+), 16 deletions(-) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 3afdb7c8c..51413c37d 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -221,7 +221,16 @@ int FBaseCVar::ToInt (UCVarValue value, ECVarType type) #else case CVAR_Float: res = (int)value.Float; break; #endif - case CVAR_String: res = strtol (value.String, NULL, 0); break; + case CVAR_String: + { + if (stricmp (value.String, "true") == 0) + res = 1; + else if (stricmp (value.String, "false") == 0) + res = 0; + else + res = strtol (value.String, NULL, 0); + break; + } case CVAR_GUID: res = 0; break; default: res = 0; break; } @@ -444,7 +453,12 @@ UCVarValue FBaseCVar::FromString (const char *value, ECVarType type) break; case CVAR_Int: - ret.Int = strtol (value, NULL, 0); + if (stricmp (value, "true") == 0) + ret.Int = 1; + else if (stricmp (value, "false") == 0) + ret.Int = 0; + else + ret.Int = strtol (value, NULL, 0); break; case CVAR_Float: diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index c4f0f9d6a..0370e226c 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -36,8 +36,6 @@ static FRandom pr_torch ("Torch"); #define TIMEFREEZE_TICS ( 12 * TICRATE ) */ -EXTERN_CVAR (Bool, r_drawfuzz); - IMPLEMENT_CLASS (APowerup) // Powerup-Giver ------------------------------------------------------------- diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index fbf6b28ae..39a17bf76 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -81,7 +81,6 @@ static void PlayerLandedOnThing (AActor *mo, AActor *onmobj); extern cycle_t BotSupportCycles; extern int BotWTG; -EXTERN_CVAR (Bool, r_drawfuzz); EXTERN_CVAR (Int, cl_rockettrails) // PRIVATE DATA DEFINITIONS ------------------------------------------------ diff --git a/src/r_blend.h b/src/r_blend.h index 132d09507..f9fefc9d1 100644 --- a/src/r_blend.h +++ b/src/r_blend.h @@ -48,6 +48,7 @@ enum ERenderStyle STYLE_Add, // Draw additive STYLE_Shaded, // Treat patch data as alpha values for alphacolor STYLE_TranslucentStencil, + STYLE_Shadow, STYLE_Count }; @@ -63,6 +64,9 @@ enum ERenderOp STYLEOP_FuzzOrAdd, // Draw fuzzy or add, based on user preference STYLEOP_FuzzOrSub, // Draw fuzzy or subtract, based on user preference STYLEOP_FuzzOrRevSub, // Draw fuzzy or reverse subtract, based on user preference + + // special styles + STYLEOP_Shadow, }; enum ERenderAlpha diff --git a/src/r_draw.cpp b/src/r_draw.cpp index ba77ad2d8..8fb3efe36 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -141,6 +141,7 @@ FRenderStyle LegacyRenderStyles[STYLE_Count] = /* STYLE_Add */ {{ STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_One, 0 }}, /* STYLE_Shaded */ {{ STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_RedIsAlpha | STYLEF_ColorIsFixed }}, /* STYLE_TranslucentStencil */{{ STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_ColorIsFixed }}, + /* STYLE_Shadow */{{ STYLEOP_Shadow, 0, 0, 0 }}, }; #else FRenderStyle LegacyRenderStyles[STYLE_Count]; @@ -157,6 +158,7 @@ static const BYTE Styles[STYLE_Count * 4] = STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_One, 0, STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_RedIsAlpha | STYLEF_ColorIsFixed, STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_ColorIsFixed, + STYLEOP_Shadow, 0, 0, 0 }; static struct LegacyInit @@ -2068,7 +2070,7 @@ void R_InitColumnDrawers () } // [RH] Choose column drawers in a single place -EXTERN_CVAR (Bool, r_drawfuzz) +EXTERN_CVAR (Int, r_drawfuzz) EXTERN_CVAR (Float, transsouls) CVAR (Bool, r_drawtrans, true, 0) @@ -2235,6 +2237,13 @@ ESPSResult R_SetPatchStyle (FRenderStyle style, fixed_t alpha, int translation, style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = FRACUNIT*3/10; + color = 0; + } + if (style.Flags & STYLEF_TransSoulsAlpha) { alpha = fixed_t(transsouls * FRACUNIT); @@ -2390,16 +2399,42 @@ bool FRenderStyle::IsVisible(fixed_t alpha) const throw() void FRenderStyle::CheckFuzz() { - if (BlendOp == STYLEOP_FuzzOrAdd) + switch (BlendOp) { - BlendOp = (r_drawfuzz || !r_drawtrans) ? STYLEOP_Fuzz : STYLEOP_Add; + default: + return; + + case STYLEOP_FuzzOrAdd: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_Add; + return; + } + break; + + case STYLEOP_FuzzOrSub: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_Sub; + return; + } + break; + + case STYLEOP_FuzzOrRevSub: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_RevSub; + return; + } + break; } - else if (BlendOp == STYLEOP_FuzzOrSub) + + if (r_drawfuzz == 2) { - BlendOp = (r_drawfuzz || !r_drawtrans) ? STYLEOP_Fuzz : STYLEOP_Sub; + BlendOp = STYLEOP_Shadow; } - else if (BlendOp == STYLEOP_FuzzOrRevSub) + else { - BlendOp = (r_drawfuzz || !r_drawtrans) ? STYLEOP_Fuzz : STYLEOP_RevSub; + BlendOp = STYLEOP_Fuzz; } } diff --git a/src/r_things.cpp b/src/r_things.cpp index be25d3153..eff99e531 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -61,7 +61,7 @@ extern fixed_t globaluclip, globaldclip; #define BASEYCENTER (100) EXTERN_CVAR (Bool, st_scale) -CVAR (Bool, r_drawfuzz, true, CVAR_ARCHIVE) +CVAR (Int, r_drawfuzz, 1, CVAR_ARCHIVE) // diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index d41d79891..ad7bde72d 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -601,11 +601,11 @@ DEFINE_PROPERTY(renderstyle, S, Actor) { PROP_STRING_PARM(str, 0); static const char * renderstyles[]={ - "NONE","NORMAL","FUZZY","SOULTRANS","OPTFUZZY","STENCIL","TRANSLUCENT", "ADD","SHADED", NULL}; + "NONE","NORMAL","FUZZY","SOULTRANS","OPTFUZZY","STENCIL","TRANSLUCENT", "ADD", "SHADED", "SHADOW", NULL}; static const int renderstyle_values[]={ STYLE_None, STYLE_Normal, STYLE_Fuzzy, STYLE_SoulTrans, STYLE_OptFuzzy, - STYLE_TranslucentStencil, STYLE_Translucent, STYLE_Add, STYLE_Shaded}; + STYLE_TranslucentStencil, STYLE_Translucent, STYLE_Add, STYLE_Shaded, STYLE_Shadow}; // make this work for old style decorations, too. if (!strnicmp(str, "style_", 6)) str+=6; diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index cc036d540..b0f25bbe1 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -3677,6 +3677,14 @@ bool D3DFB::SetStyle(D3DTex *tex, DrawParms &parms, D3DCOLOR &color0, D3DCOLOR & alpha = clamp (parms.alpha, 0, FRACUNIT) / 65536.f; } + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + // FIXME: Fuzz effect is not written if (style.BlendOp == STYLEOP_FuzzOrAdd || style.BlendOp == STYLEOP_Fuzz) { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 243029f1d..4ddf8dfb6 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -640,6 +640,13 @@ OptionValue Contrast 2.0, "Smooth" } +OptionValue Fuzziness +{ + 0.0, "Translucent" + 1.0, "Fuzz" + 2.0, "Shadow" +} + OptionMenu "VideoOptions" { Title "DISPLAY OPTIONS" @@ -662,7 +669,7 @@ OptionMenu "VideoOptions" } Option "Stretch short skies", "r_stretchsky", "OnOff" - Option "Use fuzz effect", "r_drawfuzz", "YesNo" + Option "Use fuzz effect", "r_drawfuzz", "Fuzziness" Slider "Lost Soul translucency", "transsouls", 0.25, 1.0, 0.05, 2 Option "Use fake contrast", "r_fakecontrast", "Contrast" Option "Rocket Trails", "cl_rockettrails", "RocketTrailTypes" From cc287d35a64592ab39389040bd4104dc068095b5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Dec 2010 00:21:19 +0000 Subject: [PATCH 217/815] - cleaned up p_ceiling.cpp SVN r3077 (trunk) --- src/p_ceiling.cpp | 523 +++++++++++++++++++++++++--------------------- src/p_spec.h | 7 +- 2 files changed, 283 insertions(+), 247 deletions(-) diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index ec569735c..3cb60bcbb 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -30,9 +30,11 @@ #include "r_state.h" #include "gi.h" +//============================================================================ // // CEILINGS // +//============================================================================ IMPLEMENT_CLASS (DCeiling) @@ -40,6 +42,12 @@ DCeiling::DCeiling () { } +//============================================================================ +// +// +// +//============================================================================ + void DCeiling::Serialize (FArchive &arc) { Super::Serialize (arc); @@ -59,6 +67,12 @@ void DCeiling::Serialize (FArchive &arc) << m_Hexencrush; } +//============================================================================ +// +// +// +//============================================================================ + void DCeiling::PlayCeilingSound () { if (m_Sector->seqType >= 0) @@ -80,9 +94,12 @@ void DCeiling::PlayCeilingSound () } } +//============================================================================ // -// T_MoveCeiling +// DCeiling :: Tick // +//============================================================================ + void DCeiling::Tick () { EResult res; @@ -176,6 +193,12 @@ void DCeiling::Tick () } } +//============================================================================ +// +// +// +//============================================================================ + DCeiling::DCeiling (sector_t *sec) : DMovingCeiling (sec) { @@ -191,11 +214,258 @@ DCeiling::DCeiling (sector_t *sec, fixed_t speed1, fixed_t speed2, int silent) m_Silent = silent; } +//============================================================================ +// +// +// +//============================================================================ + +DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, + fixed_t speed, fixed_t speed2, fixed_t height, + int crush, int silent, int change, bool hexencrush) +{ + fixed_t targheight = 0; // Silence, GCC + + // if ceiling already moving, don't start a second function on it + if (sec->PlaneMoving(sector_t::ceiling)) + { + return NULL; + } + + // new door thinker + DCeiling *ceiling = new DCeiling (sec, speed, speed2, silent); + vertex_t *spot = sec->lines[0]->v1; + + switch (type) + { + case ceilCrushAndRaise: + case ceilCrushRaiseAndStay: + ceiling->m_TopHeight = sec->ceilingplane.d; + case ceilLowerAndCrush: + case ceilLowerAndCrushDist: + targheight = sec->FindHighestFloorPoint (&spot); + if (type == ceilLowerAndCrush) + { + targheight += 8*FRACUNIT; + } + else if (type == ceilLowerAndCrushDist) + { + targheight += height; + } + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseToHighest: + targheight = sec->FindHighestCeilingSurrounding (&spot); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilLowerByValue: + targheight = sec->ceilingplane.ZatPoint (spot) - height; + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseByValue: + targheight = sec->ceilingplane.ZatPoint (spot) + height; + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilMoveToValue: + { + int diff = height - sec->ceilingplane.ZatPoint (spot); + + targheight = height; + if (diff < 0) + { + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, height); + ceiling->m_Direction = -1; + } + else + { + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, height); + ceiling->m_Direction = 1; + } + } + break; + + case ceilLowerToHighestFloor: + targheight = sec->FindHighestFloorSurrounding (&spot); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseToHighestFloor: + targheight = sec->FindHighestFloorSurrounding (&spot); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilLowerInstant: + targheight = sec->ceilingplane.ZatPoint (spot) - height; + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + ceiling->m_Speed = height; + break; + + case ceilRaiseInstant: + targheight = sec->ceilingplane.ZatPoint (spot) + height; + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + ceiling->m_Speed = height; + break; + + case ceilLowerToNearest: + targheight = sec->FindNextLowestCeiling (&spot); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseToNearest: + targheight = sec->FindNextHighestCeiling (&spot); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilLowerToLowest: + targheight = sec->FindLowestCeilingSurrounding (&spot); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseToLowest: + targheight = sec->FindLowestCeilingSurrounding (&spot); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilLowerToFloor: + targheight = sec->FindHighestFloorPoint (&spot); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseToFloor: // [RH] What's this for? + targheight = sec->FindHighestFloorPoint (&spot); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + case ceilLowerToHighest: + targheight = sec->FindHighestCeilingSurrounding (&spot); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilLowerByTexture: + targheight = sec->ceilingplane.ZatPoint (spot) - sec->FindShortestUpperAround (); + ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = -1; + break; + + case ceilRaiseByTexture: + targheight = sec->ceilingplane.ZatPoint (spot) + sec->FindShortestUpperAround (); + ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); + ceiling->m_Direction = 1; + break; + + default: + break; // Silence GCC + } + + ceiling->m_Tag = tag; + ceiling->m_Type = type; + ceiling->m_Crush = crush; + ceiling->m_Hexencrush = hexencrush; + + // Do not interpolate instant movement ceilings. + // Note for ZDoomGL: Check to make sure that you update the sector + // after the ceiling moves, because it hasn't actually moved yet. + fixed_t movedist; + + if (ceiling->m_Direction < 0) + { + movedist = sec->ceilingplane.d - ceiling->m_BottomHeight; + } + else + { + movedist = ceiling->m_TopHeight - sec->ceilingplane.d; + } + if (ceiling->m_Speed >= movedist) + { + ceiling->StopInterpolation(); + } + + // set texture/type change properties + if (change & 3) // if a texture change is indicated + { + if (change & 4) // if a numeric model change + { + sector_t *modelsec; + + //jff 5/23/98 find model with floor at target height if target + //is a floor type + modelsec = (/*type == ceilRaiseToHighest ||*/ + type == ceilRaiseToFloor || + /*type == ceilLowerToHighest ||*/ + type == ceilLowerToFloor) ? + sec->FindModelFloorSector (targheight) : + sec->FindModelCeilingSector (targheight); + if (modelsec != NULL) + { + ceiling->m_Texture = modelsec->GetTexture(sector_t::ceiling); + switch (change & 3) + { + case 1: // type is zeroed + ceiling->m_NewSpecial = 0; + ceiling->m_Type = genCeilingChg0; + break; + case 2: // type is copied + ceiling->m_NewSpecial = sec->special; + ceiling->m_Type = genCeilingChgT; + break; + case 3: // type is left alone + ceiling->m_Type = genCeilingChg; + break; + } + } + } + else if (line) // else if a trigger model change + { + ceiling->m_Texture = line->frontsector->GetTexture(sector_t::ceiling); + switch (change & 3) + { + case 1: // type is zeroed + ceiling->m_NewSpecial = 0; + ceiling->m_Type = genCeilingChg0; + break; + case 2: // type is copied + ceiling->m_NewSpecial = line->frontsector->special; + ceiling->m_Type = genCeilingChgT; + break; + case 3: // type is left alone + ceiling->m_Type = genCeilingChg; + break; + } + } + } + + ceiling->PlayCeilingSound (); + return ceiling; +} + +//============================================================================ // // EV_DoCeiling // Move a ceiling up/down and all around! // // [RH] Added tag, speed, speed2, height, crush, silent, change params +// +//============================================================================ + bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, fixed_t speed, fixed_t speed2, fixed_t height, int crush, int silent, int change, bool hexencrush) @@ -203,10 +473,6 @@ bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int secnum; bool rtn; sector_t* sec; - DCeiling* ceiling; - bool manual = false; - fixed_t targheight = 0; // Silence, GCC - vertex_t* spot; rtn = false; @@ -216,11 +482,10 @@ bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, if (!line || !(sec = line->backsector)) return rtn; secnum = (int)(sec-sectors); - manual = true; // [RH] Hack to let manual crushers be retriggerable, too tag ^= secnum | 0x1000000; P_ActivateInStasisCeiling (tag); - goto manual_ceiling; + return !!DCeiling::Create(sec, type, line, tag, speed, speed2, height, crush, silent, change, hexencrush); } // Reactivate in-stasis ceilings...for certain types. @@ -234,252 +499,19 @@ bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, // affects all sectors with the same tag as the linedef while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0) { - sec = §ors[secnum]; -manual_ceiling: - // if ceiling already moving, don't start a second function on it - if (sec->PlaneMoving(sector_t::ceiling)) - { - if (!manual) - continue; - else - return false; - } - - // new door thinker - rtn = 1; - ceiling = new DCeiling (sec, speed, speed2, silent); - spot = sec->lines[0]->v1; - - switch (type) - { - case DCeiling::ceilCrushAndRaise: - case DCeiling::ceilCrushRaiseAndStay: - ceiling->m_TopHeight = sec->ceilingplane.d; - case DCeiling::ceilLowerAndCrush: - case DCeiling::ceilLowerAndCrushDist: - targheight = sec->FindHighestFloorPoint (&spot); - if (type == DCeiling::ceilLowerAndCrush) - { - targheight += 8*FRACUNIT; - } - else if (type == DCeiling::ceilLowerAndCrushDist) - { - targheight += height; - } - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseToHighest: - targheight = sec->FindHighestCeilingSurrounding (&spot); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilLowerByValue: - targheight = sec->ceilingplane.ZatPoint (spot) - height; - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseByValue: - targheight = sec->ceilingplane.ZatPoint (spot) + height; - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilMoveToValue: - { - int diff = height - sec->ceilingplane.ZatPoint (spot); - - targheight = height; - if (diff < 0) - { - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, height); - ceiling->m_Direction = -1; - } - else - { - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, height); - ceiling->m_Direction = 1; - } - } - break; - - case DCeiling::ceilLowerToHighestFloor: - targheight = sec->FindHighestFloorSurrounding (&spot); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseToHighestFloor: - targheight = sec->FindHighestFloorSurrounding (&spot); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilLowerInstant: - targheight = sec->ceilingplane.ZatPoint (spot) - height; - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - ceiling->m_Speed = height; - break; - - case DCeiling::ceilRaiseInstant: - targheight = sec->ceilingplane.ZatPoint (spot) + height; - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - ceiling->m_Speed = height; - break; - - case DCeiling::ceilLowerToNearest: - targheight = sec->FindNextLowestCeiling (&spot); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseToNearest: - targheight = sec->FindNextHighestCeiling (&spot); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilLowerToLowest: - targheight = sec->FindLowestCeilingSurrounding (&spot); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseToLowest: - targheight = sec->FindLowestCeilingSurrounding (&spot); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilLowerToFloor: - targheight = sec->FindHighestFloorPoint (&spot); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseToFloor: // [RH] What's this for? - targheight = sec->FindHighestFloorPoint (&spot); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - case DCeiling::ceilLowerToHighest: - targheight = sec->FindHighestCeilingSurrounding (&spot); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilLowerByTexture: - targheight = sec->ceilingplane.ZatPoint (spot) - sec->FindShortestUpperAround (); - ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = -1; - break; - - case DCeiling::ceilRaiseByTexture: - targheight = sec->ceilingplane.ZatPoint (spot) + sec->FindShortestUpperAround (); - ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight); - ceiling->m_Direction = 1; - break; - - default: - break; // Silence GCC - } - - ceiling->m_Tag = tag; - ceiling->m_Type = type; - ceiling->m_Crush = crush; - ceiling->m_Hexencrush = hexencrush; - - // Do not interpolate instant movement ceilings. - // Note for ZDoomGL: Check to make sure that you update the sector - // after the ceiling moves, because it hasn't actually moved yet. - fixed_t movedist; - - if (ceiling->m_Direction < 0) - { - movedist = sec->ceilingplane.d - ceiling->m_BottomHeight; - } - else - { - movedist = ceiling->m_TopHeight - sec->ceilingplane.d; - } - if (ceiling->m_Speed >= movedist) - { - ceiling->StopInterpolation(); - } - - // set texture/type change properties - if (change & 3) // if a texture change is indicated - { - if (change & 4) // if a numeric model change - { - sector_t *modelsec; - - //jff 5/23/98 find model with floor at target height if target - //is a floor type - modelsec = (/*type == DCeiling::ceilRaiseToHighest ||*/ - type == DCeiling::ceilRaiseToFloor || - /*type == DCeiling::ceilLowerToHighest ||*/ - type == DCeiling::ceilLowerToFloor) ? - sec->FindModelFloorSector (targheight) : - sec->FindModelCeilingSector (targheight); - if (modelsec != NULL) - { - ceiling->m_Texture = modelsec->GetTexture(sector_t::ceiling); - switch (change & 3) - { - case 1: // type is zeroed - ceiling->m_NewSpecial = 0; - ceiling->m_Type = DCeiling::genCeilingChg0; - break; - case 2: // type is copied - ceiling->m_NewSpecial = sec->special; - ceiling->m_Type = DCeiling::genCeilingChgT; - break; - case 3: // type is left alone - ceiling->m_Type = DCeiling::genCeilingChg; - break; - } - } - } - else if (line) // else if a trigger model change - { - ceiling->m_Texture = line->frontsector->GetTexture(sector_t::ceiling); - switch (change & 3) - { - case 1: // type is zeroed - ceiling->m_NewSpecial = 0; - ceiling->m_Type = DCeiling::genCeilingChg0; - break; - case 2: // type is copied - ceiling->m_NewSpecial = line->frontsector->special; - ceiling->m_Type = DCeiling::genCeilingChgT; - break; - case 3: // type is left alone - ceiling->m_Type = DCeiling::genCeilingChg; - break; - } - } - } - - ceiling->PlayCeilingSound (); - - if (manual) - return rtn; + rtn |= !!DCeiling::Create(§ors[secnum], type, line, tag, speed, speed2, height, crush, silent, change, hexencrush); } return rtn; } +//============================================================================ // // Restart a ceiling that's in-stasis // [RH] Passed a tag instead of a line and rewritten to use a list // +//============================================================================ + void P_ActivateInStasisCeiling (int tag) { DCeiling *scan; @@ -495,11 +527,14 @@ void P_ActivateInStasisCeiling (int tag) } } +//============================================================================ // // EV_CeilingCrushStop // Stop a ceiling from crushing! // [RH] Passed a tag instead of a line and rewritten to use a list // +//============================================================================ + bool EV_CeilingCrushStop (int tag) { bool rtn = false; diff --git a/src/p_spec.h b/src/p_spec.h index 21427626e..8656e4247 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -677,6 +677,10 @@ public: void Serialize (FArchive &arc); void Tick (); + static DCeiling *Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, + fixed_t speed, fixed_t speed2, fixed_t height, + int crush, int silent, int change, bool hexencrush); + protected: ECeiling m_Type; fixed_t m_BottomHeight; @@ -702,9 +706,6 @@ protected: private: DCeiling (); - friend bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, - int tag, fixed_t speed, fixed_t speed2, fixed_t height, - int crush, int silent, int change, bool hexencrush); friend bool EV_CeilingCrushStop (int tag); friend void P_ActivateInStasisCeiling (int tag); }; From f35b3b84af09838de8d7764440f4fa419ffb0f99 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 26 Dec 2010 09:01:36 +0000 Subject: [PATCH 218/815] - fixed: The player setup menu set the playerlass CVAR to the actual class name, not the display name as expected. SVN r3078 (trunk) --- src/menu/playermenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/playermenu.cpp b/src/menu/playermenu.cpp index 03300af14..de0292f7a 100644 --- a/src/menu/playermenu.cpp +++ b/src/menu/playermenu.cpp @@ -912,7 +912,7 @@ void DPlayerMenu::ClassChanged (FListMenuItem *li) players[consoleplayer].userinfo.PlayerClass = sel-1; PickPlayerClass(); - cvar_set ("playerclass", sel == 0 ? "Random" : PlayerClass->Type->TypeName); + cvar_set ("playerclass", sel == 0 ? "Random" : PlayerClass->Type->Meta.GetMetaString (APMETA_DisplayName)); UpdateSkins(); UpdateColorsets(); From 898b0d679d6d33eba1286723ba9f526dc0d2aed5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 27 Dec 2010 16:14:26 +0000 Subject: [PATCH 219/815] - added a new 'playertype' command for SBARINFO that checks by class type not display name. - fixed: Status bar display for Hexen's fourth weapons only worked when they were obtained by picking up the weapon pieces. SVN r3080 (trunk) --- src/g_shared/sbarinfo_commands.cpp | 57 +++++++++++++- src/p_doors.cpp | 119 ++++++++++++++++++++--------- wadsrc/static/sbarinfo/hexen.txt | 91 ++++++++++------------ 3 files changed, 178 insertions(+), 89 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index fcc5a6e1e..c63b009e5 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -2660,6 +2660,58 @@ class CommandPlayerClass : public SBarInfoCommandFlowControl //////////////////////////////////////////////////////////////////////////////// +class CommandPlayerType : public SBarInfoCommandFlowControl +{ + public: + CommandPlayerType(SBarInfo *script) : SBarInfoCommandFlowControl(script) + { + } + + void Parse(FScanner &sc, bool fullScreenOffsets) + { + sc.MustGetToken(TK_Identifier); + do + { + bool foundClass = false; + const PClass *cls = PClass::FindClass(sc.String); + if (cls != NULL) + { + foundClass = true; + classes.Push(cls); + } + /* + if(!foundClass) + sc.ScriptError("Unkown PlayerClass '%s'.", sc.String); + */ + if(!sc.CheckToken(',')) + break; + } + while(sc.CheckToken(TK_Identifier)); + SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets); + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged); + + if(statusBar->CPlayer->cls == NULL) + return; //No class so we can not continue + + for(unsigned int i = 0;i < classes.Size();i++) + { + if (statusBar->CPlayer->cls->IsDescendantOf(classes[i])) + { + SetTruth(true, block, statusBar); + return; + } + } + SetTruth(false, block, statusBar); + } + protected: + TArray classes; +}; + +//////////////////////////////////////////////////////////////////////////////// + class CommandHasWeaponPiece : public SBarInfoCommandFlowControl { public: @@ -3082,7 +3134,7 @@ static const char *SBarInfoCommandNames[] = "drawmugshot", "drawselectedinventory", "drawinventorybar", "drawbar", "drawgem", "drawshader", "drawstring", "drawkeybar", - "gamemode", "playerclass", "aspectratio", + "gamemode", "playerclass", "playertype", "aspectratio", "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", @@ -3095,7 +3147,7 @@ enum SBarInfoCommands SBARINFO_DRAWMUGSHOT, SBARINFO_DRAWSELECTEDINVENTORY, SBARINFO_DRAWINVENTORYBAR, SBARINFO_DRAWBAR, SBARINFO_DRAWGEM, SBARINFO_DRAWSHADER, SBARINFO_DRAWSTRING, SBARINFO_DRAWKEYBAR, - SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_ASPECTRATIO, + SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_PLAYERTYPE, SBARINFO_ASPECTRATIO, SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, @@ -3126,6 +3178,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_ASPECTRATIO: return new CommandAspectRatio(script); case SBARINFO_ISSELECTED: return new CommandIsSelected(script); case SBARINFO_PLAYERCLASS: return new CommandPlayerClass(script); + case SBARINFO_PLAYERTYPE: return new CommandPlayerType(script); case SBARINFO_HASWEAPONPIECE: return new CommandHasWeaponPiece(script); case SBARINFO_WEAPONAMMO: return new CommandWeaponAmmo(script); case SBARINFO_ININVENTORY: return new CommandInInventory(script); diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 557928c55..c01abab6a 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -36,6 +36,12 @@ #include "sc_man.h" #include "cmdlib.h" +//============================================================================ +// +// VERTICAL DOORS +// +//============================================================================ + IMPLEMENT_CLASS (DDoor) DDoor::DDoor () @@ -55,14 +61,12 @@ void DDoor::Serialize (FArchive &arc) << m_LightTag; } - -// -// VERTICAL DOORS -// - +//============================================================================ // // T_VerticalDoor // +//============================================================================ + void DDoor::Tick () { EResult res; @@ -215,7 +219,12 @@ void DDoor::Tick () } } +//============================================================================ +// // [RH] DoorSound: Plays door sound depending on direction and speed +// +//============================================================================ + void DDoor::DoorSound (bool raise) const { int choice; @@ -309,10 +318,12 @@ DDoor::DDoor (sector_t *sector) { } -// [RH] Merged EV_VerticalDoor and EV_DoLockedDoor into EV_DoDoor -// and made them more general to support the new specials. - +//============================================================================ +// // [RH] SpawnDoor: Helper function for EV_DoDoor +// +//============================================================================ + DDoor::DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int lightTag) : DMovingCeiling (sec), m_Type (type), m_Speed (speed), m_TopWait (delay), m_LightTag (lightTag) @@ -371,6 +382,13 @@ DDoor::DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int lightTa m_OldFloorDist = sec->floorplane.d; } +//============================================================================ +// +// [RH] Merged EV_VerticalDoor and EV_DoLockedDoor into EV_DoDoor +// and made them more general to support the new specials. +// +//============================================================================ + bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, int tag, int speed, int delay, int lock, int lightTag) { @@ -464,10 +482,12 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, return rtn; } - +//============================================================================ // // Spawn a door that closes after 30 seconds // +//============================================================================ + void P_SpawnDoorCloseIn30 (sector_t *sec) { fixed_t height; @@ -487,18 +507,56 @@ void P_SpawnDoorCloseIn30 (sector_t *sec) door->m_LightTag = 0; } +//============================================================================ // // Spawn a door that opens after 5 minutes // +//============================================================================ + void P_SpawnDoorRaiseIn5Mins (sector_t *sec) { sec->special = 0; new DDoor (sec, DDoor::doorRaiseIn5Mins, 2*FRACUNIT, TICRATE*30/7, 0); } -// EV_SlidingDoor : slide a door horizontally -// (animate midtexture, then set noblocking line) + +//============================================================================ // +// animated doors +// +//============================================================================ + +IMPLEMENT_CLASS (DAnimatedDoor) + +DAnimatedDoor::DAnimatedDoor () +{ +} + +DAnimatedDoor::DAnimatedDoor (sector_t *sec) + : DMovingCeiling (sec) +{ +} + +void DAnimatedDoor::Serialize (FArchive &arc) +{ + Super::Serialize (arc); + + arc << m_Line1 << m_Line2 + << m_Frame + << m_Timer + << m_BotDist + << m_Status + << m_Speed + << m_Delay + << m_DoorAnim + << m_SetBlocking1 << m_SetBlocking2; +} + +//============================================================================ +// +// Starts a closing action on an animated door +// +//============================================================================ bool DAnimatedDoor::StartClosing () { @@ -528,6 +586,12 @@ bool DAnimatedDoor::StartClosing () return true; } +//============================================================================ +// +// +// +//============================================================================ + void DAnimatedDoor::Tick () { if (m_DoorAnim == NULL) @@ -624,31 +688,11 @@ void DAnimatedDoor::Tick () } } -IMPLEMENT_CLASS (DAnimatedDoor) - -DAnimatedDoor::DAnimatedDoor () -{ -} - -DAnimatedDoor::DAnimatedDoor (sector_t *sec) - : DMovingCeiling (sec) -{ -} - -void DAnimatedDoor::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - - arc << m_Line1 << m_Line2 - << m_Frame - << m_Timer - << m_BotDist - << m_Status - << m_Speed - << m_Delay - << m_DoorAnim - << m_SetBlocking1 << m_SetBlocking2; -} +//============================================================================ +// +// +// +//============================================================================ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim) : DMovingCeiling (sec) @@ -706,7 +750,8 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, //============================================================================ // -// EV_SlidingDoor +// EV_SlidingDoor : slide a door horizontally +// (animate midtexture, then set noblocking line) // //============================================================================ diff --git a/wadsrc/static/sbarinfo/hexen.txt b/wadsrc/static/sbarinfo/hexen.txt index 635cb650f..e7a4c6c40 100755 --- a/wadsrc/static/sbarinfo/hexen.txt +++ b/wadsrc/static/sbarinfo/hexen.txt @@ -114,29 +114,26 @@ statusbar Normal drawimage "ARMCLS", 255, 178; drawnumber 2, HUDFONT_RAVEN, untranslated, armorclass, 275, 176, 1; - playerclass Cleric + playertype ClericPlayer { drawimage "WPSLOT1", 190, 162; - hasweaponpiece CWeapWraithverge, 1 + ininventory CWeapWraithverge { - drawimage "WPIECEC1", 190, 162; + drawimage "WPFULL1", 190, 162; } - hasweaponpiece CWeapWraithverge, 2 - { - drawimage "WPIECEC2", 212, 162; - } - hasweaponpiece CWeapWraithverge, 3 - { - drawimage "WPIECEC3", 225, 162; - } - hasweaponpiece CWeapWraithverge, 1 + else { + hasweaponpiece CWeapWraithverge, 1 + { + drawimage "WPIECEC1", 190, 162; + } hasweaponpiece CWeapWraithverge, 2 { - hasweaponpiece CWeapWraithverge, 3 - { - drawimage "WPFULL1", 190, 162; - } + drawimage "WPIECEC2", 212, 162; + } + hasweaponpiece CWeapWraithverge, 3 + { + drawimage "WPIECEC3", 225, 162; } } @@ -145,29 +142,26 @@ statusbar Normal else drawgem translatable, interpolate(6), "CHAIN2", "LIFEGMC2", -23, 49, 15, 30, 193; } - else playerclass Mage + else playertype MagePlayer { drawimage "WPSLOT2", 190, 162; - hasweaponpiece MWeapBloodscourge, 1 + ininventory MWeapBloodscourge { - drawimage "WPIECEM1", 190, 162; + drawimage "WPFULL2", 190, 162; } - hasweaponpiece MWeapBloodscourge, 2 - { - drawimage "WPIECEM2", 205, 162; - } - hasweaponpiece MWeapBloodscourge, 3 - { - drawimage "WPIECEM3", 224, 162; - } - hasweaponpiece MWeapBloodscourge, 1 + else { + hasweaponpiece MWeapBloodscourge, 1 + { + drawimage "WPIECEM1", 190, 162; + } hasweaponpiece MWeapBloodscourge, 2 { - hasweaponpiece MWeapBloodscourge, 3 - { - drawimage "WPFULL2", 190, 162; - } + drawimage "WPIECEM2", 205, 162; + } + hasweaponpiece MWeapBloodscourge, 3 + { + drawimage "WPIECEM3", 224, 162; } } @@ -179,26 +173,23 @@ statusbar Normal else { drawimage "WPSLOT0", 190, 162; - hasweaponpiece FWeapQuietus, 1 + ininventory FWeapQuietus { - drawimage "WPIECEF1", 190, 162; + drawimage "WPFULL0", 190, 162; } - hasweaponpiece FWeapQuietus, 2 - { - drawimage "WPIECEF2", 225, 162; - } - hasweaponpiece FWeapQuietus, 3 - { - drawimage "WPIECEF3", 234, 162; - } - hasweaponpiece FWeapQuietus, 1 + else { + hasweaponpiece FWeapQuietus, 1 + { + drawimage "WPIECEF1", 190, 162; + } hasweaponpiece FWeapQuietus, 2 { - hasweaponpiece FWeapQuietus, 3 - { - drawimage "WPFULL0", 190, 162; - } + drawimage "WPIECEF2", 225, 162; + } + hasweaponpiece FWeapQuietus, 3 + { + drawimage "WPIECEF3", 234, 162; } } @@ -222,21 +213,21 @@ statusbar Automap drawimage hexenarmor amulet, "ARMSLOT4", 243, 164; // Also draw the life gem here - playerclass Fighter + playertype FighterPlayer { gamemode singleplayer drawgem interpolate(6), "CHAIN", "LIFEGMF2", -23, 49, 15, 30, 193; else drawgem translatable, interpolate(6), "CHAIN", "LIFEGMF2", -23, 49, 15, 30, 193; } - else playerclass Cleric + else playertype ClericPlayer { gamemode singleplayer drawgem interpolate(6), "CHAIN2", "LIFEGMC2", -23, 49, 15, 30, 193; else drawgem translatable, interpolate(6), "CHAIN2", "LIFEGMC2", -23, 49, 15, 30, 193; } - else playerclass Mage + else playertype MagePlayer { gamemode singleplayer drawgem interpolate(6), "CHAIN3", "LIFEGMM2", -23, 49, 15, 30, 193; From 2b0262d1110d065692dd9a7e3b270aa929ddca07 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 1 Jan 2011 09:24:56 +0000 Subject: [PATCH 220/815] - added Chris's KDE IWAD picker submission. SVN r3083 (trunk) --- src/sdl/i_system.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 6fc29665c..af717324e 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -608,6 +608,57 @@ int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad) return defaultiwad; } +#if !defined(__APPLE__) + const char *str; + if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0) + { + FString cmd("kdialog --title \""GAMESIG" "DOTVERSIONSTR": Select an IWAD to use\"" + " --menu \"ZDoom found more than one IWAD\n" + "Select from the list below to determine which one to use:\""); + + for(i = 0; i < numwads; ++i) + { + const char *filepart = strrchr(wads[i].Path, '/'); + if(filepart == NULL) + filepart = wads[i].Path; + else + filepart++; + // Menu entries are specified in "tag" "item" pairs, where when a + // particular item is selected (and the Okay button clicked), its + // corresponding tag is printed to stdout for identification. + cmd.AppendFormat(" \"%d\" \"%s (%s)\"", i, wads[i].Name.GetChars(), filepart); + } + + if(defaultiwad >= 0 && defaultiwad < numwads) + { + const char *filepart = strrchr(wads[defaultiwad].Path, '/'); + if(filepart == NULL) + filepart = wads[defaultiwad].Path; + else + filepart++; + cmd.AppendFormat(" --default \"%s (%s)\"", wads[defaultiwad].Name.GetChars(), filepart); + } + + FILE *f = popen(cmd, "r"); + if(f != NULL) + { + char gotstr[16]; + + if(fgets(gotstr, sizeof(gotstr), f) == NULL || + sscanf(gotstr, "%d", &i) != 1) + i = -1; + + // Exit status = 1 means the selection was canceled (either by + // Cancel/Esc or the X button), not that there was an error running + // the program. In that case, nothing was printed so fgets will + // have failed. Other values can indicate an error running the app, + // so fall back to whatever else can be used. + int status = pclose(f); + if(WIFEXITED(status) && (WEXITSTATUS(status) == 0 || WEXITSTATUS(status) == 1)) + return i; + } + } +#endif #ifndef NO_GTK if (GtkAvailable) { From e90b86acce759fcecbe576e3054b4b83f4592441 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 1 Jan 2011 11:16:46 +0000 Subject: [PATCH 221/815] - added 'player.flechettetype' property so that the appropriate flechette type can be set per player class. - bumped savegame version for flechette type changes. SVN r3085 (trunk) --- src/d_player.h | 1 + src/g_game.cpp | 26 +++++++---- src/g_hexen/a_flechette.cpp | 51 ++++++++++----------- src/p_user.cpp | 3 +- src/thingdef/thingdef_properties.cpp | 13 +++++- src/version.h | 2 +- wadsrc/static/actors/hexen/clericplayer.txt | 1 + wadsrc/static/actors/hexen/mageplayer.txt | 1 + wadsrc/static/actors/shared/player.txt | 1 + 9 files changed, 57 insertions(+), 42 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 6f414e1ff..74a3b8a24 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -137,6 +137,7 @@ public: int SpawnMask; FNameNoInit MorphWeapon; fixed_t AttackZOffset; // attack height, relative to player center + const PClass *FlechetteType; // [CW] Fades for when you are being damaged. PalEntry DamageFade; diff --git a/src/g_game.cpp b/src/g_game.cpp index 1d735ee03..1c2a6e516 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -455,6 +455,8 @@ CCMD (drop) } } +const PClass *GetFlechetteType(AActor *other); + CCMD (useflechette) { // Select from one of arti_poisonbag1-3, whichever the player has static const ENamedName bagnames[3] = @@ -463,22 +465,26 @@ CCMD (useflechette) NAME_ArtiPoisonBag2, NAME_ArtiPoisonBag3 }; - int i, j; if (who == NULL) return; - if (who->IsKindOf (PClass::FindClass (NAME_ClericPlayer))) - i = 0; - else if (who->IsKindOf (PClass::FindClass (NAME_MagePlayer))) - i = 1; - else - i = 2; - - for (j = 0; j < 3; ++j) + const PClass *type = GetFlechetteType(who); + if (type != NULL) { AInventory *item; - if ( (item = who->FindInventory (bagnames[(i+j)%3])) ) + if ( (item = who->FindInventory (type) )) + { + SendItemUse = item; + return; + } + } + + // The default flechette could not be found. Try all 3 types then. + for (int j = 0; j < 3; ++j) + { + AInventory *item; + if ( (item = who->FindInventory (bagnames[j])) ) { SendItemUse = item; break; diff --git a/src/g_hexen/a_flechette.cpp b/src/g_hexen/a_flechette.cpp index ea72cb208..b99ec9793 100644 --- a/src/g_hexen/a_flechette.cpp +++ b/src/g_hexen/a_flechette.cpp @@ -141,6 +141,27 @@ bool AArtiPoisonBag3::Use (bool pickup) return false; } +//============================================================================ +// +// GetFlechetteType +// +//============================================================================ + +const PClass *GetFlechetteType(AActor *other) +{ + const PClass *spawntype = NULL; + if (other->IsKindOf(RUNTIME_CLASS(APlayerPawn))) + { + spawntype = static_cast(other)->FlechetteType; + } + if (spawntype == NULL) + { + // default fallback if nothing valid defined. + spawntype = RUNTIME_CLASS(AArtiPoisonBag3); + } + return spawntype; +} + //============================================================================ // // AArtiPoisonBag :: HandlePickup @@ -155,21 +176,7 @@ bool AArtiPoisonBag::HandlePickup (AInventory *item) return Super::HandlePickup (item); } - bool matched; - - if (Owner->IsKindOf (PClass::FindClass(NAME_ClericPlayer))) - { - matched = (GetClass() == RUNTIME_CLASS(AArtiPoisonBag1)); - } - else if (Owner->IsKindOf (PClass::FindClass(NAME_MagePlayer))) - { - matched = (GetClass() == RUNTIME_CLASS(AArtiPoisonBag2)); - } - else - { - matched = (GetClass() == RUNTIME_CLASS(AArtiPoisonBag3)); - } - if (matched) + if (GetClass() == GetFlechetteType(Owner)) { if (Amount < MaxAmount) { @@ -204,20 +211,8 @@ AInventory *AArtiPoisonBag::CreateCopy (AActor *other) } AInventory *copy; - const PClass *spawntype; - if (other->IsKindOf (PClass::FindClass(NAME_ClericPlayer))) - { - spawntype = RUNTIME_CLASS(AArtiPoisonBag1); - } - else if (other->IsKindOf (PClass::FindClass(NAME_MagePlayer))) - { - spawntype = RUNTIME_CLASS(AArtiPoisonBag2); - } - else - { - spawntype = RUNTIME_CLASS(AArtiPoisonBag3); - } + const PClass *spawntype = GetFlechetteType(other); copy = static_cast(Spawn (spawntype, 0, 0, 0, NO_REPLACE)); copy->Amount = Amount; copy->MaxAmount = MaxAmount; diff --git a/src/p_user.cpp b/src/p_user.cpp index 831d65dd5..d18a31b6a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -432,7 +432,8 @@ void APlayerPawn::Serialize (FArchive &arc) << InvSel << MorphWeapon << DamageFade - << PlayerFlags; + << PlayerFlags + << FlechetteType; } //=========================================================================== diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index ad7bde72d..f7221c435 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -601,11 +601,11 @@ DEFINE_PROPERTY(renderstyle, S, Actor) { PROP_STRING_PARM(str, 0); static const char * renderstyles[]={ - "NONE","NORMAL","FUZZY","SOULTRANS","OPTFUZZY","STENCIL","TRANSLUCENT", "ADD", "SHADED", "SHADOW", NULL}; + "NONE","NORMAL","FUZZY","SOULTRANS","OPTFUZZY","STENCIL","TRANSLUCENT", "ADD","SHADED", NULL}; static const int renderstyle_values[]={ STYLE_None, STYLE_Normal, STYLE_Fuzzy, STYLE_SoulTrans, STYLE_OptFuzzy, - STYLE_TranslucentStencil, STYLE_Translucent, STYLE_Add, STYLE_Shaded, STYLE_Shadow}; + STYLE_TranslucentStencil, STYLE_Translucent, STYLE_Add, STYLE_Shaded}; // make this work for old style decorations, too. if (!strnicmp(str, "style_", 6)) str+=6; @@ -2070,6 +2070,15 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, morphweapon, S, PlayerPawn) defaults->MorphWeapon = FName(z); } +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY_PREFIX(player, flechettetype, S, PlayerPawn) +{ + PROP_STRING_PARM(str, 0); + defaults->FlechetteType = FindClassTentative(str, "ArtiPoisonBag"); +} + //========================================================================== // //========================================================================== diff --git a/src/version.h b/src/version.h index 32cd44b39..bc6abfa29 100644 --- a/src/version.h +++ b/src/version.h @@ -75,7 +75,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 3030 +#define MINSAVEVER 3085 #if SVN_REVISION_NUMBER < MINSAVEVER // If we don't know the current revision write something very high to ensure that diff --git a/wadsrc/static/actors/hexen/clericplayer.txt b/wadsrc/static/actors/hexen/clericplayer.txt index 73276e981..6e1658594 100644 --- a/wadsrc/static/actors/hexen/clericplayer.txt +++ b/wadsrc/static/actors/hexen/clericplayer.txt @@ -28,6 +28,7 @@ ACTOR ClericPlayer : PlayerPawn Player.WeaponSlot 2, CWeapStaff Player.WeaponSlot 3, CWeapFlame Player.WeaponSlot 4, CWeapWraithverge + Player.FlechetteType "ArtiPoisonBag1" Player.ColorRange 146, 163 Player.Colorset 0, "Blue", 146, 163, 161 diff --git a/wadsrc/static/actors/hexen/mageplayer.txt b/wadsrc/static/actors/hexen/mageplayer.txt index d843b00a9..1690cbf72 100644 --- a/wadsrc/static/actors/hexen/mageplayer.txt +++ b/wadsrc/static/actors/hexen/mageplayer.txt @@ -30,6 +30,7 @@ ACTOR MagePlayer : PlayerPawn Player.WeaponSlot 2, MWeapFrost Player.WeaponSlot 3, MWeapLightning Player.WeaponSlot 4, MWeapBloodscourge + Player.FlechetteType "ArtiPoisonBag2" Player.ColorRange 146, 163 Player.Colorset 0, "Blue", 146, 163, 161 diff --git a/wadsrc/static/actors/shared/player.txt b/wadsrc/static/actors/shared/player.txt index 194e58048..2752412e7 100644 --- a/wadsrc/static/actors/shared/player.txt +++ b/wadsrc/static/actors/shared/player.txt @@ -28,6 +28,7 @@ Actor PlayerPawn : Actor native Player.SoundClass "player" Player.DamageScreenColor "ff 00 00" Player.MugShotMaxHealth 0 + Player.FlechetteType "ArtiPoisonBag3" } Actor PlayerChunk : PlayerPawn native From 2add3fb381ea24faabb701697fa7ba17edf95987 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 2 Jan 2011 18:02:27 +0000 Subject: [PATCH 222/815] - Merge voxels back into trunk. Even if it needs further tweaking, it should at least be stable now. SVN r3086 (trunk) --- src/CMakeLists.txt | 1 + src/asm_ia32/a.asm | 263 +++++- src/asm_ia32/tmap.asm | 34 +- src/c_dispatch.h | 1 + src/memarena.cpp | 376 ++++++++ src/memarena.h | 83 ++ src/r_data.cpp | 304 ++++++- src/r_data.h | 2 + src/r_defs.h | 95 +- src/r_draw.cpp | 91 ++ src/r_draw.h | 7 +- src/r_drawt.cpp | 60 +- src/r_main.cpp | 27 +- src/r_main.h | 3 +- src/r_things.cpp | 1329 +++++++++++++++++++++++++--- src/r_things.h | 8 + src/resourcefiles/file_wad.cpp | 1 + src/resourcefiles/resourcefile.cpp | 3 +- src/tables.h | 2 +- src/textures/buildtexture.cpp | 2 + src/v_video.cpp | 56 +- src/v_video.h | 5 +- src/w_wad.h | 1 + zdoom.vcproj | 8 + 24 files changed, 2556 insertions(+), 206 deletions(-) create mode 100644 src/memarena.cpp create mode 100644 src/memarena.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 992e8bb14..1bfe9ab7c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -658,6 +658,7 @@ add_executable( zdoom WIN32 m_misc.cpp m_png.cpp m_random.cpp + memarena.cpp md5.cpp name.cpp nodebuild.cpp diff --git a/src/asm_ia32/a.asm b/src/asm_ia32/a.asm index d68fba116..eaf747455 100644 --- a/src/asm_ia32/a.asm +++ b/src/asm_ia32/a.asm @@ -28,6 +28,9 @@ %define setupmvlineasm _setupmvlineasm %define mvlineasm1 _mvlineasm1 %define mvlineasm4 _mvlineasm4 + +%define R_SetupDrawSlab _R_SetupDrawSlab +%define R_DrawSlab _R_DrawSlab %endif EXTERN ylookup ; near @@ -44,9 +47,6 @@ EXTERN dc_dest EXTERN dc_source EXTERN dc_texturefrac -mvlineasm4_counter: - dd 0 - SECTION .text ALIGN 16 @@ -59,8 +59,45 @@ setvlinebpl_: mov [fixchain2ma+2], eax mov [fixchain2mb+2], eax selfmod fixchain1a, fixchain2mb+6 + +setdrawslabbpl: + mov dword [voxbpl1+2], eax + mov dword [voxbpl2+2], eax + mov dword [voxbpl3+2], eax + mov dword [voxbpl4+2], eax + mov dword [voxbpl5+2], eax + mov dword [voxbpl6+2], eax + mov dword [voxbpl7+2], eax + mov dword [voxbpl8+2], eax + selfmod voxbpl1, voxpl8+6 ret + SECTION .data + +lastslabcolormap: + dd 4 + + SECTION .text + +GLOBAL R_SetupDrawSlab +GLOBAL @R_SetupDrawSlab@4 +R_SetupDrawSlab: + mov ecx, [esp+4] +@R_SetupDrawSlab@4: + cmp [lastslabcolormap], ecx + je .done + mov [lastslabcolormap], ecx + mov dword [voxpal1+2], ecx + mov dword [voxpal2+2], ecx + mov dword [voxpal3+2], ecx + mov dword [voxpal4+2], ecx + mov dword [voxpal5+2], ecx + mov dword [voxpal6+2], ecx + mov dword [voxpal7+2], ecx + mov dword [voxpal8+2], ecx +.done ret + + ; pass it log2(texheight) ALIGN 16 @@ -549,6 +586,226 @@ mvcase0: jmp beginmvlineasm4 align 16 + +;************************************************************************* +;***************************** Voxel Slabs ******************************* +;************************************************************************* + +GLOBAL R_DrawSlab +R_DrawSlab: + push ebx + push ebp + push esi + push edi + + mov eax, [esp+5*4+0] + mov ebx, [esp+5*4+4] + mov ecx, [esp+5*4+8] + mov edx, [esp+5*4+12] + mov esi, [esp+5*4+16] + mov edi, [esp+5*4+20] + + cmp eax, 2 + je voxbegdraw2 + ja voxskip2 + xor eax, eax +voxbegdraw1: + mov ebp, ebx + shr ebp, 16 + add ebx, edx + dec ecx + mov al, byte [esi+ebp] +voxpal1: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl1: lea edi, [edi+88888888h] + jnz voxbegdraw1 + jmp voxskipslab5 + +voxbegdraw2: + mov ebp, ebx + shr ebp, 16 + add ebx, edx + xor eax, eax + dec ecx + mov al, byte [esi+ebp] +voxpal2: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl2: lea edi, [edi+88888888h] + jnz voxbegdraw2 + jmp voxskipslab5 + +voxskip2: + cmp eax, 4 + jne voxskip4 + xor eax, eax +voxbegdraw4: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal3: mov al, byte [eax+88888888h] + mov ah, al + shl eax, 8 + mov al, ah + shl eax, 8 + mov al, ah + mov dword [edi], eax +voxbpl3: add edi, 88888888h + dec ecx + jnz voxbegdraw4 + jmp voxskipslab5 + +voxskip4: + add eax, edi + + test edi, 1 + jz voxskipslab1 + cmp edi, eax + je voxskipslab1 + + push eax + push ebx + push ecx + push edi +voxbegslab1: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal4: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl4: add edi, 88888888h + dec ecx + jnz voxbegslab1 + pop edi + pop ecx + pop ebx + pop eax + inc edi + +voxskipslab1: + push eax + test edi, 2 + jz voxskipslab2 + dec eax + cmp edi, eax + jge voxskipslab2 + + push ebx + push ecx + push edi +voxbegslab2: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal5: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl5: add edi, 88888888h + dec ecx + jnz voxbegslab2 + pop edi + pop ecx + pop ebx + add edi, 2 + +voxskipslab2: + mov eax, [esp] + + sub eax, 3 + cmp edi, eax + jge voxskipslab3 + +voxprebegslab3: + push ebx + push ecx + push edi +voxbegslab3: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal6: mov al, byte [eax+88888888h] + mov ah, al + shl eax, 8 + mov al, ah + shl eax, 8 + mov al, ah + mov dword [edi], eax +voxbpl6: add edi, 88888888h + dec ecx + jnz voxbegslab3 + pop edi + pop ecx + pop ebx + add edi, 4 + + mov eax, [esp] + + sub eax, 3 + cmp edi, eax + jl voxprebegslab3 + +voxskipslab3: + mov eax, [esp] + + dec eax + cmp edi, eax + jge voxskipslab4 + + push ebx + push ecx + push edi +voxbegslab4: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal7: mov al, byte [eax+88888888h] + mov ah, al + mov word [edi], ax +voxbpl7: add edi, 88888888h + dec ecx + jnz voxbegslab4 + pop edi + pop ecx + pop ebx + add edi, 2 + +voxskipslab4: + pop eax + + cmp edi, eax + je voxskipslab5 + +voxbegslab5: + mov ebp, ebx + add ebx, edx + shr ebp, 16 + xor eax, eax + mov al, byte [esi+ebp] +voxpal8: mov al, byte [eax+88888888h] + mov byte [edi], al +voxbpl8: add edi, 88888888h + dec ecx + jnz voxbegslab5 + +voxskipslab5: + pop edi + pop esi + pop ebp + pop ebx + ret + +align 16 + %ifdef M_TARGET_MACHO GLOBAL _rtext_a_end _rtext_a_end: diff --git a/src/asm_ia32/tmap.asm b/src/asm_ia32/tmap.asm index cbcd9f4f1..a32b1492e 100644 --- a/src/asm_ia32/tmap.asm +++ b/src/asm_ia32/tmap.asm @@ -850,8 +850,8 @@ GLOBAL R_DrawColumnHorizP_ASM align 16 @R_DrawColumnHorizP_ASM@0: -R_DrawColumnHorizP_ASM: _R_DrawColumnHorizP_ASM: +R_DrawColumnHorizP_ASM: ; count = dc_yh - dc_yl; @@ -870,8 +870,10 @@ _R_DrawColumnHorizP_ASM: inc eax ; make 0 count mean 0 pixels and edx,3 push eax - mov esi,[dc_ctspan+edx*4] - lea eax,[dc_temp+ecx*4+edx] ; eax = top of column in buffer + mov eax,[dc_temp] + mov esi,[dc_ctspan+edx*4] + add eax,edx + lea eax,[eax+ecx*4] ; eax = top of column in buffer mov ebp,[dc_yh] mov [esi],ecx mov [esi+4],ebp @@ -1102,8 +1104,9 @@ _rt_copy1col_asm: lea esi,[eax*4] inc ebx ; ebx = count mov eax,edx - lea ecx,[dc_temp+ecx+esi] ; ecx = source + add ecx,esi mov edi,[ylookup+esi] + add ecx,[dc_temp] ; ecx = source mov esi,[dc_pitch] ; esi = pitch add eax,edi ; eax = dest add eax,[dc_destorg] @@ -1169,10 +1172,11 @@ _rt_copy4cols_asm: inc ebx ; ebx = count mov eax,ecx mov esi,[ylookup+edx*4] - lea ecx,[dc_temp+edx*4] ; ecx = source - mov edx,[dc_pitch] ; edx = pitch + mov ecx,[dc_temp] add eax,esi ; eax = dest add eax,[dc_destorg] + lea ecx,[ecx+edx*4] ; ecx = source + mov edx,[dc_pitch] ; edx = pitch shr ebx,1 jnc .even @@ -1241,7 +1245,8 @@ _rt_map1col_asm: mov esi,[dc_colormap] ; esi = colormap inc ebx ; ebx = count mov eax,edx - lea ebp,[dc_temp+ecx+edi] ; ebp = source + lea ebp,[ecx+edi] ; ebp = source + add ebp,[dc_temp] mov ecx,[ylookup+edi] mov edi,[dc_pitch] ; edi = pitch add eax,ecx ; eax = dest @@ -1320,7 +1325,8 @@ _rt_map4cols_asm1: mov eax,ecx inc ebx ; ebx = count mov edi,[ylookup+edx] - lea ebp,[dc_temp+edx] ; ebp = source + mov ebp,[dc_temp] + add ebp,edx ; ebp = source add eax,edi ; eax = dest mov edi,[dc_pitch] ; edi = pitch add eax,[dc_destorg] @@ -1414,7 +1420,8 @@ _rt_map4cols_asm2: mov eax,ecx inc ebx ; ebx = count mov edi,[ylookup+edx] - lea ebp,[dc_temp+edx] ; ebp = source + mov ebp,[dc_temp] + add ebp,edx ; ebp = source add eax,edi ; eax = dest mov edi,[dc_pitch] ; edi = pitch add eax,[dc_destorg] @@ -1493,10 +1500,11 @@ _rt_shaded4cols_asm: add eax,[dc_destorg] ; eax = destination push ebx push esi + mov esi,[dc_temp] inc ebp ; ebp = count add eax,[esp+16] push edi - lea esi,[dc_temp+ecx*4] ; esi = source + lea esi,[esi+ecx*4] ; esi = source align 16 @@ -1580,10 +1588,11 @@ _rt_add4cols_asm: add eax,[dc_destorg] push ebx push esi + mov esi,[dc_temp] push ebp inc edi add eax,[esp+20] - lea esi,[dc_temp+ecx*4] + lea esi,[esi+ecx*4] align 16 a4loop: @@ -1659,10 +1668,11 @@ _rt_addclamp4cols_asm: add eax,[dc_destorg] push ebx push esi + mov esi,[dc_temp] push ebp inc edi add eax,[esp+20] - lea esi,[dc_temp+ecx*4] + lea esi,[esi+ecx*4] push edi align 16 diff --git a/src/c_dispatch.h b/src/c_dispatch.h index ac8685855..5aa3f9870 100644 --- a/src/c_dispatch.h +++ b/src/c_dispatch.h @@ -166,5 +166,6 @@ void ResetButtonStates (); // Same as above, but also clear bDown extern unsigned int MakeKey (const char *s); extern unsigned int MakeKey (const char *s, size_t len); +extern unsigned int SuperFastHash (const char *data, size_t len); #endif //__C_DISPATCH_H__ diff --git a/src/memarena.cpp b/src/memarena.cpp new file mode 100644 index 000000000..c44586dee --- /dev/null +++ b/src/memarena.cpp @@ -0,0 +1,376 @@ +/* +** memarena.cpp +** Implements memory arenas. +** +**--------------------------------------------------------------------------- +** Copyright 2010 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. +**--------------------------------------------------------------------------- +** +** A memory arena is used for efficient allocation of many small objects that +** will all be freed at once. Note that since individual destructors are not +** called, you must not use an arena to allocate any objects that use a +** destructor, either explicitly or implicitly (because they have members +** with destructors). +*/ + +#include "doomtype.h" +#include "m_alloc.h" +#include "memarena.h" +#include "c_dispatch.h" +#include "zstring.h" + +#define BLOCK_SIZE (10*1024) + +struct FMemArena::Block +{ + Block *NextBlock; + void *Limit; // End of this block + void *Avail; // Start of free space in this block + + void Reset(); + void *Alloc(size_t size); +}; + +//========================================================================== +// +// RoundPointer +// +// Rounds a pointer up to a pointer-sized boundary. +// +//========================================================================== + +static inline void *RoundPointer(void *ptr) +{ + return (void *)(((size_t)ptr + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); +} + +//========================================================================== +// +// FMemArena Constructor +// +//========================================================================== + +FMemArena::FMemArena() +{ + TopBlock = NULL; + FreeBlocks = NULL; +} + +//========================================================================== +// +// FMemArena Destructor +// +//========================================================================== + +FMemArena::~FMemArena() +{ + FreeAllBlocks(); +} + +//========================================================================== +// +// FMemArena :: Alloc +// +//========================================================================== + +void *FMemArena::Alloc(size_t size) +{ + Block *block; + + for (block = TopBlock; block != NULL; block = block->NextBlock) + { + void *res = block->Alloc(size); + if (res != NULL) + { + return res; + } + } + block = AddBlock(size); + return block->Alloc(size); +} + +//========================================================================== +// +// FMemArena :: FreeAll +// +// Moves all blocks to the free list. No system-level deallocation occurs. +// +//========================================================================== + +void FMemArena::FreeAll() +{ + for (Block *next, *block = TopBlock; block != NULL; block = next) + { + next = block->NextBlock; + block->Reset(); + block->NextBlock = FreeBlocks; + FreeBlocks = block; + } + TopBlock = NULL; +} + +//========================================================================== +// +// FMemArena :: FreeAllBlocks +// +// Frees all blocks used by this arena. +// +//========================================================================== + +void FMemArena::FreeAllBlocks() +{ + FreeBlockChain(TopBlock); + FreeBlockChain(FreeBlocks); +} + +//========================================================================== +// +// FMemArena :: FreeBlockChain +// +// Frees a chain of blocks. +// +//========================================================================== + +void FMemArena::FreeBlockChain(Block *&top) +{ + for (Block *next, *block = top; block != NULL; block = next) + { + next = block->NextBlock; + M_Free(block); + } + top = NULL; +} + +//========================================================================== +// +// FMemArena :: AddBlock +// +// Allocates a block large enough to hold at least bytes and adds it +// to the TopBlock chain. +// +//========================================================================== + +FMemArena::Block *FMemArena::AddBlock(size_t size) +{ + Block *mem, **last; + size += sizeof(Block); // Account for header size + + // Search for a free block to use + for (last = &FreeBlocks, mem = FreeBlocks; mem != NULL; last = &mem->NextBlock, mem = mem->NextBlock) + { + if ((BYTE *)mem->Limit - (BYTE *)mem >= (ptrdiff_t)size) + { + *last = mem->NextBlock; + break; + } + } + if (mem == NULL) + { + // Allocate a new block + if (size < BLOCK_SIZE) + { + size = BLOCK_SIZE; + } + else + { // Stick some free space at the end so we can use this block for + // other things. + size += BLOCK_SIZE/2; + } + mem = (Block *)M_Malloc(size); + mem->Limit = (BYTE *)mem + size; + } + mem->Reset(); + mem->NextBlock = TopBlock; + TopBlock = mem; + return mem; +} + +//========================================================================== +// +// FMemArena :: Block :: Reset +// +// Resets this block's Avail pointer. +// +//========================================================================== + +void FMemArena::Block::Reset() +{ + Avail = RoundPointer(this + sizeof(*this)); +} + +//========================================================================== +// +// FMemArena :: Block :: Alloc +// +// Allocates memory from the block if it has space. Returns NULL if not. +// +//========================================================================== + +void *FMemArena::Block::Alloc(size_t size) +{ + if ((char *)Avail + size > Limit) + { + return NULL; + } + void *res = Avail; + Avail = RoundPointer((char *)Avail + size); + return res; +} + +//========================================================================== +// +// FSharedStringArena Constructor +// +//========================================================================== + +FSharedStringArena::FSharedStringArena() +{ + memset(Buckets, 0, sizeof(Buckets)); +} + +//========================================================================== +// +// FSharedStringArena Destructor +// +//========================================================================== + +FSharedStringArena::~FSharedStringArena() +{ + FreeAll(); + // FMemArena destructor will free the blocks. +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +// Allocates a new string and initializes it with the passed string. This +// version takes an FString as a parameter, so it won't need to allocate any +// memory for the string text if it already exists in the arena. +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const FString &source) +{ + unsigned int hash; + Node *strnode; + + strnode = FindString(source, source.Len(), hash); + if (strnode == NULL) + { + strnode = (Node *)FMemArena::Alloc(sizeof(Node)); + ::new(&strnode->String) FString(source); + strnode->Hash = hash; + hash %= countof(Buckets); + strnode->Next = Buckets[hash]; + Buckets[hash] = strnode; + } + return &strnode->String; +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const char *source) +{ + return Alloc(source, strlen(source)); +} + +//========================================================================== +// +// FSharedStringArena :: Alloc +// +//========================================================================== + +FString *FSharedStringArena::Alloc(const char *source, size_t strlen) +{ + unsigned int hash; + Node *strnode; + + strnode = FindString(source, strlen, hash); + if (strnode == NULL) + { + strnode = (Node *)FMemArena::Alloc(sizeof(Node)); + ::new(&strnode->String) FString(source, strlen); + strnode->Hash = hash; + hash %= countof(Buckets); + strnode->Next = Buckets[hash]; + Buckets[hash] = strnode; + } + return &strnode->String; +} + +//========================================================================== +// +// FSharedStringArena :: FindString +// +// Finds the string if it's already in the arena. Returns NULL if not. +// +//========================================================================== + +FSharedStringArena::Node *FSharedStringArena::FindString(const char *str, size_t strlen, unsigned int &hash) +{ + hash = SuperFastHash(str, strlen); + + for (Node *node = Buckets[hash % countof(Buckets)]; node != NULL; node = node->Next) + { + if (node->Hash == hash && node->String.Len() == strlen && memcmp(&node->String[0], str, strlen) == 0) + { + return node; + } + } + return NULL; +} + +//========================================================================== +// +// FSharedStringArena :: FreeAll +// +// In addition to moving all used blocks onto the free list, all FStrings +// they contain will have their destructors called. +// +//========================================================================== + +void FSharedStringArena::FreeAll() +{ + for (Block *next, *block = TopBlock; block != NULL; block = next) + { + next = block->NextBlock; + void *limit = block->Avail; + block->Reset(); + for (Node *string = (Node *)block->Avail; string < limit; ++string) + { + string->~Node(); + } + block->NextBlock = FreeBlocks; + FreeBlocks = block; + } + memset(Buckets, 0, sizeof(Buckets)); +} diff --git a/src/memarena.h b/src/memarena.h new file mode 100644 index 000000000..e933fc072 --- /dev/null +++ b/src/memarena.h @@ -0,0 +1,83 @@ +/* +** memarena.h +** +**--------------------------------------------------------------------------- +** Copyright 2010 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "zstring.h" + +// A general purpose arena. +class FMemArena +{ +public: + FMemArena(); + ~FMemArena(); + + void *Alloc(size_t size); + void FreeAll(); + void FreeAllBlocks(); + +protected: + struct Block; + + Block *AddBlock(size_t size); + void FreeBlockChain(Block *&top); + + Block *TopBlock; + Block *FreeBlocks; +}; + +// An arena specializing in storage of FStrings. It knows how to free them, +// but this means it also should never be used for allocating anything else. +// Identical strings all return the same pointer. +class FSharedStringArena : public FMemArena +{ +public: + FSharedStringArena(); + ~FSharedStringArena(); + void FreeAll(); + + class FString *Alloc(const FString &source); + class FString *Alloc(const char *source); + class FString *Alloc(const char *source, size_t strlen); + +protected: + struct Node + { + Node *Next; + FString String; + unsigned int Hash; + }; + Node *Buckets[256]; + + Node *FindString(const char *str, size_t strlen, unsigned int &hash); +private: + void *Alloc(size_t size) { return NULL; } // No access to FMemArena::Alloc for outsiders. +}; diff --git a/src/r_data.cpp b/src/r_data.cpp index a298cc03f..8983d5e55 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -355,6 +355,304 @@ const BYTE *R_GetColumn (FTexture *tex, int col) return tex->GetColumn (col, NULL); } +//========================================================================== +// +// GetVoxelRemap +// +// Calculates a remap table for the voxel's palette. Results are cached so +// passing the same palette repeatedly will not require repeated +// recalculations. +// +//========================================================================== + +static BYTE *GetVoxelRemap(const BYTE *pal) +{ + static BYTE remap[256]; + static BYTE oldpal[768]; + static bool firsttime = true; + + if (firsttime || memcmp(oldpal, pal, 768) != 0) + { // Not the same palette as last time, so recalculate. + firsttime = false; + memcpy(oldpal, pal, 768); + for (int i = 0; i < 256; ++i) + { + // The voxel palette uses VGA colors, so we have to expand it + // from 6 to 8 bits per component. + remap[i] = BestColor((uint32 *)GPalette.BaseColors, + (oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4), + (oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4), + (oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4)); + } + } + return remap; +} + +//========================================================================== +// +// CopyVoxelSlabs +// +// Copy all the slabs in a block of slabs. +// +//========================================================================== + +static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size) +{ + while (size >= 3) + { + int slabzleng = src->zleng; + + if (3 + slabzleng > size) + { // slab is too tall + return false; + } + + dest->ztop = src->ztop; + dest->zleng = src->zleng; + dest->backfacecull = src->backfacecull; + + for (int j = 0; j < slabzleng; ++j) + { + dest->col[j] = src->col[j]; + } + slabzleng += 3; + src = (kvxslab_t *)((BYTE *)src + slabzleng); + dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + size -= slabzleng; + } + return true; +} + +//========================================================================== +// +// RemapVoxelSlabs +// +// Remaps all the slabs in a block of slabs. +// +//========================================================================== + +static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap) +{ + while (size >= 3) + { + int slabzleng = dest->zleng; + + for (int j = 0; j < slabzleng; ++j) + { + dest->col[j] = remap[dest->col[j]]; + } + slabzleng += 3; + dest = (kvxslab_t *)((BYTE *)dest + slabzleng); + size -= slabzleng; + } +} + +//========================================================================== +// +// R_LoadKVX +// +//========================================================================== + +FVoxel *R_LoadKVX(int lumpnum) +{ + const kvxslab_t *slabs[MAXVOXMIPS]; + FVoxel *voxel = new FVoxel; + const BYTE *rawmip; + int mip, maxmipsize; + int i, j, n; + + FMemLump lump = Wads.ReadLump(lumpnum); // FMemLump adds an extra 0 byte to the end. + BYTE *rawvoxel = (BYTE *)lump.GetMem(); + int voxelsize = (int)(lump.GetSize()-1); + + // Oh, KVX, why couldn't you have a proper header? We'll just go through + // and collect each MIP level, doing lots of range checking, and if the + // last one doesn't end exactly 768 bytes before the end of the file, + // we'll reject it. + + for (mip = 0, rawmip = rawvoxel, maxmipsize = voxelsize - 768 - 4; + mip < MAXVOXMIPS; + mip++) + { + int numbytes = GetInt(rawmip); + if (numbytes > maxmipsize || numbytes < 24) + { + break; + } + rawmip += 4; + + FVoxelMipLevel *mipl = &voxel->Mips[mip]; + + // Load header data. + mipl->SizeX = GetInt(rawmip + 0); + mipl->SizeY = GetInt(rawmip + 4); + mipl->SizeZ = GetInt(rawmip + 8); + mipl->PivotX = GetInt(rawmip + 12); + mipl->PivotY = GetInt(rawmip + 16); + mipl->PivotZ = GetInt(rawmip + 20); + + // How much space do we have for voxdata? + int offsetsize = (mipl->SizeX + 1) * 4 + mipl->SizeX * (mipl->SizeY + 1) * 2; + int voxdatasize = numbytes - 24 - offsetsize; + if (voxdatasize < 0) + { // Clearly, not enough. + break; + } + if (voxdatasize == 0) + { // This mip level is empty. + goto nextmip; + } + + // Allocate slab data space. + mipl->OffsetX = new int[(numbytes - 24 + 3) / 4]; + mipl->OffsetXY = (short *)(mipl->OffsetX + mipl->SizeX + 1); + mipl->SlabData = (BYTE *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1)); + + // Load x offsets. + for (i = 0, n = mipl->SizeX; i <= n; ++i) + { + // The X offsets stored in the KVX file are relative to the start of the + // X offsets array. Make them relative to voxdata instead. + mipl->OffsetX[i] = GetInt(rawmip + 24 + i * 4) - offsetsize; + } + + // The first X offset must be 0 (since we subtracted offsetsize), according to the spec: + // NOTE: xoffset[0] = (xsiz+1)*4 + xsiz*(ysiz+1)*2 (ALWAYS) + if (mipl->OffsetX[0] != 0) + { + break; + } + // And the final X offset must point just past the end of the voxdata. + if (mipl->OffsetX[mipl->SizeX] != voxdatasize) + { + break; + } + + // Load xy offsets. + i = 24 + i * 4; + for (j = 0, n *= mipl->SizeY + 1; j < n; ++j) + { + mipl->OffsetXY[j] = GetShort(rawmip + i + j * 2); + } + + // Ensure all offsets are within bounds. + for (i = 0; i < mipl->SizeX; ++i) + { + int xoff = mipl->OffsetX[i]; + for (j = 0; j < mipl->SizeY; ++j) + { + int yoff = mipl->OffsetXY[(mipl->SizeY + 1) * i + j]; + if (unsigned(xoff + yoff) > unsigned(voxdatasize)) + { + goto bad; + } + } + } + + // Record slab location for the end. + slabs[mip] = (kvxslab_t *)(rawmip + 24 + offsetsize); + + // Time for the next mip Level. +nextmip: + rawmip += numbytes; + maxmipsize -= numbytes + 4; + } + // Did we get any mip levels, and if so, does the last one leave just + // enough room for the palette after it? + if (mip == 0 || rawmip != rawvoxel + voxelsize - 768) + { +bad: delete voxel; + return NULL; + } + + // Do not count empty mips at the end. + for (; mip > 0; --mip) + { + if (voxel->Mips[mip - 1].SlabData != NULL) + break; + } + voxel->NumMips = mip; + + for (i = 0; i < mip; ++i) + { + if (!CopyVoxelSlabs((kvxslab_t *)voxel->Mips[i].SlabData, slabs[i], voxel->Mips[i].OffsetX[voxel->Mips[i].SizeX])) + { // Invalid slabs encountered. Reject this voxel. + delete voxel; + return NULL; + } + } + + voxel->LumpNum = lumpnum; + voxel->Palette = new BYTE[768]; + memcpy(voxel->Palette, rawvoxel + voxelsize - 768, 768); + + return voxel; +} + +//========================================================================== +// +// FVoxelMipLevel Constructor +// +//========================================================================== + +FVoxelMipLevel::FVoxelMipLevel() +{ + SizeZ = SizeY = SizeX = 0; + PivotZ = PivotY = PivotX = 0; + OffsetX = NULL; + OffsetXY = NULL; + SlabData = NULL; +} + +//========================================================================== +// +// FVoxelMipLevel Destructor +// +//========================================================================== + +FVoxelMipLevel::~FVoxelMipLevel() +{ + if (OffsetX != NULL) + { + delete[] OffsetX; + } +} + +//========================================================================== +// +// FVoxel Constructor +// +//========================================================================== + +FVoxel::FVoxel() +{ + Palette = NULL; +} + +FVoxel::~FVoxel() +{ + if (Palette != NULL) delete [] Palette; +} + +//========================================================================== +// +// Remap the voxel to the game palette +// +//========================================================================== + +void FVoxel::Remap() +{ + if (Palette != NULL) + { + BYTE *remap = GetVoxelRemap(Palette); + for (int i = 0; i < NumMips; ++i) + { + RemapVoxelSlabs((kvxslab_t *)Mips[i].SlabData, Mips[i].OffsetX[Mips[i].SizeX], remap); + } + delete [] Palette; + Palette = NULL; + } +} //========================================================================== // @@ -389,10 +687,4 @@ CCMD (printspans) Printf ("\n"); } } - -CCMD (picnum) -{ - //int picnum = TexMan.GetTexture (argv[1], FTexture::TEX_Any); - //Printf ("%d: %s - %s\n", picnum, TexMan[picnum]->Name, TexMan(picnum)->Name); -} #endif diff --git a/src/r_data.h b/src/r_data.h index 487860e58..d2f25dfa9 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -144,4 +144,6 @@ extern size_t numfakecmaps; int R_FindSkin (const char *name, int pclass); // [RH] Find a skin +FVoxel *R_LoadKVX(int lumpnum); + #endif diff --git a/src/r_defs.h b/src/r_defs.h index 9d2822c33..cf84c65ac 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -25,6 +25,7 @@ #include "doomdef.h" #include "templates.h" +#include "memarena.h" // Some more or less basic data types // we depend on. @@ -1051,7 +1052,6 @@ struct column_t typedef BYTE lighttable_t; // This could be wider for >8 bit display. - // A vissprite_t is a thing // that will be drawn during a refresh. // I.e. a sprite object that is partly visible. @@ -1059,8 +1059,9 @@ struct vissprite_t { short x1, x2; fixed_t cx; // for line side calculation - fixed_t gx, gy; // for drawseg and fake floor clipping - fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t gx, gy, gz; // origin in world coordinates + angle_t angle; + fixed_t gzb, gzt; // global bottom / top for silhouette clipping fixed_t startfrac; // horizontal position of x1 fixed_t xscale, yscale; fixed_t xiscale; // negative if flipped @@ -1073,12 +1074,17 @@ struct vissprite_t sector_t *sector; // [RH] sector this sprite is in fixed_t alpha; fixed_t floorclip; - FTexture *pic; + union + { + FTexture *pic; + struct FVoxel *voxel; + }; + BYTE bIsVoxel:1; // [RH] Use voxel instead of pic + BYTE bSplitSprite:1; // [RH] Sprite was split by a drawseg + BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on short renderflags; DWORD Translation; // [RH] for color translation FRenderStyle RenderStyle; - BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on - BYTE bSplitSprite; // [RH] Sprite was split by a drawseg }; enum @@ -1100,14 +1106,16 @@ enum // struct spriteframe_t { + struct FVoxelDef *Voxel;// voxel to use for this frame FTextureID Texture[16]; // texture to use for view angles 0-15 - WORD Flip; // flip (1 = flip) to use for view angles 0-15. + WORD Flip; // flip (1 = flip) to use for view angles 0-15. }; // // A sprite definition: // a number of animation frames. // + struct spritedef_t { union @@ -1140,4 +1148,77 @@ public: int namespc; // namespace for this skin }; + +// [RH] Voxels from Build + +#define MAXVOXMIPS 5 + +struct kvxslab_t +{ + BYTE ztop; // starting z coordinate of top of slab + BYTE zleng; // # of bytes in the color array - slab height + BYTE backfacecull; // low 6 bits tell which of 6 faces are exposed + BYTE col[1/*zleng*/];// color data from top to bottom +}; + +struct FVoxelMipLevel +{ + FVoxelMipLevel(); + ~FVoxelMipLevel(); + + int SizeX; + int SizeY; + int SizeZ; + fixed_t PivotX; // 24.8 fixed point + fixed_t PivotY; // "" + fixed_t PivotZ; // "" + int *OffsetX; + short *OffsetXY; + BYTE *SlabData; +}; + +struct FVoxel +{ + int LumpNum; + int NumMips; + BYTE *Palette; + FVoxelMipLevel Mips[MAXVOXMIPS]; + + FVoxel(); + ~FVoxel(); + void Remap(); +}; + +struct FVoxelDef +{ + FVoxel *Voxel; + int PlacedSpin; // degrees/sec to spin actors without MF_DROPPED set + int DroppedSpin; // degrees/sec to spin actors with MF_DROPPED set + fixed_t Scale; + angle_t AngleOffset; // added to actor's angle to compensate for wrong-facing voxels +}; + +// [RH] A c-buffer. Used for keeping track of offscreen voxel spans. + +struct FCoverageBuffer +{ + struct Span + { + Span *NextSpan; + short Start, Stop; + }; + + FCoverageBuffer(int size); + ~FCoverageBuffer(); + + void Clear(); + void InsertSpan(int listnum, int start, int stop); + Span *AllocSpan(); + + FMemArena SpanArena; + Span **Spans; // [0..NumLists-1] span lists + Span *FreeSpans; + unsigned int NumLists; +}; + #endif diff --git a/src/r_draw.cpp b/src/r_draw.cpp index 8fb3efe36..36c87fa89 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -1352,6 +1352,97 @@ void R_FillSpan (void) memset (ylookup[ds_y] + ds_x1 + dc_destorg, ds_color, ds_x2 - ds_x1 + 1); } +// Draw a voxel slab +// +// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman +// Ken Silverman's official web site: "http://www.advsys.net/ken" +// See the included license file "BUILDLIC.TXT" for license info. + +// Actually, this is just R_DrawColumn with an extra width parameter. + +#ifndef X86_ASM +static const BYTE *slabcolormap; + +extern "C" void R_SetupDrawSlab(const BYTE *colormap) +{ + slabcolormap = colormap; +} + +extern "C" void STACK_ARGS R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p) +{ + int x; + const BYTE *colormap = slabcolormap; + int pitch = dc_pitch; + + assert(dx > 0); + + if (dx == 1) + { + while (dy > 0) + { + *p = colormap[vptr[v >> FRACBITS]]; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 2) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 3) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p[2] = color; + p += pitch; + v += vi; + dy--; + } + } + else if (dx == 4) + { + while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + p[0] = color; + p[1] = color; + p[2] = color; + p[3] = color; + p += pitch; + v += vi; + dy--; + } + } + else while (dy > 0) + { + BYTE color = colormap[vptr[v >> FRACBITS]]; + // The optimizer will probably turn this into a memset call. + // Since dx is not likely to be large, I'm not sure that's a good thing, + // hence the alternatives above. + for (x = 0; x < dx; x++) + { + p[x] = color; + } + p += pitch; + v += vi; + dy--; + } +} +#endif + + /****************************************************/ /****************************************************/ diff --git a/src/r_draw.h b/src/r_draw.h index 96e58bf8d..57dfd8712 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -53,7 +53,7 @@ extern "C" BYTE* palookupoffse[4]; extern "C" const BYTE* bufplce[4]; // [RH] Temporary buffer for column drawing -extern "C" BYTE dc_temp[MAXHEIGHT*4]; +extern "C" BYTE *dc_temp; extern "C" unsigned int dc_tspans[4][MAXHEIGHT]; extern "C" unsigned int *dc_ctspan[4]; extern "C" unsigned int horizspans[4]; @@ -178,7 +178,7 @@ extern void (STACK_ARGS *rt_map4cols)(int sx, int yl, int yh); void rt_draw4cols (int sx); // [RH] Preps the temporary horizontal buffer. -void rt_initcols (void); +void rt_initcols (BYTE *buffer=NULL); void R_DrawFogBoundary (int x1, int x2, short *uclip, short *dclip); @@ -216,6 +216,9 @@ void R_FillColumnP (void); void R_FillColumnHorizP (void); void R_FillSpan (void); +extern "C" void R_SetupDrawSlab(const BYTE *colormap); +extern "C" void STACK_ARGS R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p); + extern "C" int ds_y; extern "C" int ds_x1; extern "C" int ds_x2; diff --git a/src/r_drawt.cpp b/src/r_drawt.cpp index fbfaf6307..c99bd3afb 100644 --- a/src/r_drawt.cpp +++ b/src/r_drawt.cpp @@ -52,9 +52,13 @@ // dc_temp is the buffer R_DrawColumnHoriz writes into. // dc_tspans points into it. // dc_ctspan points into dc_tspans. -// But what is horizspan, and what is its relation with dc_ctspan? +// horizspan also points into dc_tspans. -BYTE dc_temp[MAXHEIGHT*4]; +// dc_ctspan is advanced while drawing into dc_temp. +// horizspan is advanced up to dc_ctspan when drawing from dc_temp to the screen. + +BYTE dc_tempbuff[MAXHEIGHT*4]; +BYTE *dc_temp; unsigned int dc_tspans[4][MAXHEIGHT]; unsigned int *dc_ctspan[4]; unsigned int *horizspan[4]; @@ -998,10 +1002,11 @@ void rt_draw4cols (int sx) // Before each pass through a rendering loop that uses these routines, // call this function to set up the span pointers. -void rt_initcols (void) +void rt_initcols (BYTE *buff) { int y; + dc_temp = buff == NULL ? dc_tempbuff : buff; for (y = 3; y >= 0; y--) horizspan[y] = dc_ctspan[y] = &dc_tspans[y][0]; } @@ -1035,26 +1040,18 @@ void R_DrawColumnHorizP_C (void) const BYTE *source = dc_source; if (count & 1) { - *dest = source[frac>>FRACBITS]; - dest += 4; - frac += fracstep; + *dest = source[frac>>FRACBITS]; dest += 4; frac += fracstep; } if (count & 2) { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; dest += 8; } if (count & 4) { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; - dest[8] = source[frac>>FRACBITS]; - frac += fracstep; - dest[12] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; + dest[8] = source[frac>>FRACBITS]; frac += fracstep; + dest[12]= source[frac>>FRACBITS]; frac += fracstep; dest += 16; } count >>= 3; @@ -1062,22 +1059,14 @@ void R_DrawColumnHorizP_C (void) do { - dest[0] = source[frac>>FRACBITS]; - frac += fracstep; - dest[4] = source[frac>>FRACBITS]; - frac += fracstep; - dest[8] = source[frac>>FRACBITS]; - frac += fracstep; - dest[12] = source[frac>>FRACBITS]; - frac += fracstep; - dest[16] = source[frac>>FRACBITS]; - frac += fracstep; - dest[20] = source[frac>>FRACBITS]; - frac += fracstep; - dest[24] = source[frac>>FRACBITS]; - frac += fracstep; - dest[28] = source[frac>>FRACBITS]; - frac += fracstep; + dest[0] = source[frac>>FRACBITS]; frac += fracstep; + dest[4] = source[frac>>FRACBITS]; frac += fracstep; + dest[8] = source[frac>>FRACBITS]; frac += fracstep; + dest[12]= source[frac>>FRACBITS]; frac += fracstep; + dest[16]= source[frac>>FRACBITS]; frac += fracstep; + dest[20]= source[frac>>FRACBITS]; frac += fracstep; + dest[24]= source[frac>>FRACBITS]; frac += fracstep; + dest[28]= source[frac>>FRACBITS]; frac += fracstep; dest += 32; } while (--count); } @@ -1110,8 +1099,7 @@ void R_FillColumnHorizP (void) if (!(count >>= 1)) return; do { - dest[0] = color; - dest[4] = color; + dest[0] = color; dest[4] = color; dest += 8; } while (--count); } diff --git a/src/r_main.cpp b/src/r_main.cpp index 88aab962c..0ce3a54bd 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -102,7 +102,6 @@ static fixed_t MaxVisForFloor; static FRandom pr_torchflicker ("TorchFlicker"); static FRandom pr_hom; static TArray PastViewers; -static int centerxwide; static bool polyclipped; static bool r_showviewer; bool r_dontmaplines; @@ -134,6 +133,7 @@ float LastFOV; int WidescreenRatio; fixed_t GlobVis; +fixed_t viewingrangerecip; fixed_t FocalTangent; fixed_t FocalLengthX; fixed_t FocalLengthY; @@ -150,6 +150,8 @@ float WallTMapScale2; extern "C" { int centerx; int centery; +int centerxwide; + } DCanvas *RenderTarget; // [RH] canvas to render to @@ -158,6 +160,7 @@ fixed_t globaluclip, globaldclip; fixed_t centerxfrac; fixed_t centeryfrac; fixed_t yaspectmul; +fixed_t baseyaspectmul; // yaspectmul without a forced aspect ratio float iyaspectmulfloat; fixed_t InvZtoScale; @@ -457,6 +460,9 @@ void R_InitTextureMapping () FocalLengthY = Scale (centerxfrac, yaspectmul, hitan); FocalLengthXfloat = (float)FocalLengthX / 65536.f; + // This is 1/FocalTangent before the widescreen extension of FOV. + viewingrangerecip = DivScale32(1, finetangent[FINEANGLES/4+(FieldOfView/2)]); + // Now generate xtoviewangle for sky texture mapping. // [RH] Do not generate viewangletox, because texture mapping is no // longer done with trig, so it's not needed. @@ -599,7 +605,7 @@ void R_SetViewSize (int blocks) void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) { - int virtheight, virtwidth; + int virtheight, virtwidth, trueratio, virtwidth2, virtheight2; if (windowSize >= 11) { @@ -620,7 +626,7 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) } // If the screen is approximately 16:9 or 16:10, consider it widescreen. - WidescreenRatio = CheckRatio (fullWidth, fullHeight); + WidescreenRatio = CheckRatio (fullWidth, fullHeight, &trueratio); DrawFSHUD = (windowSize == 11); @@ -643,8 +649,18 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight) centerxfrac = centerx< @@ -53,7 +59,6 @@ #include "v_palette.h" #include "r_translate.h" - extern fixed_t globaluclip, globaldclip; @@ -94,6 +99,8 @@ short screenheightarray[MAXWIDTH]; CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites? +CVAR (Bool, r_drawvoxels, true, 0) + // // INITIALIZATION FUNCTIONS // @@ -104,6 +111,13 @@ TArray sprites; TArray SpriteFrames; DWORD NumStdSprites; // The first x sprites that don't belong to skins. +TDeletingArray Voxels; // used only to auto-delete voxels on exit. +TDeletingArray VoxelDefs; + +int OffscreenBufferWidth, OffscreenBufferHeight; +BYTE *OffscreenColorBuffer; +FCoverageBuffer *OffscreenCoverageBuffer; + struct spriteframewithrotate : public spriteframe_t { int rotate; @@ -112,6 +126,18 @@ sprtemp[MAX_SPRITE_FRAMES]; int maxframe; char* spritename; +struct VoxelOptions +{ + VoxelOptions() + : DroppedSpin(0), PlacedSpin(0), Scale(FRACUNIT), AngleOffset(0) + {} + + int DroppedSpin; + int PlacedSpin; + fixed_t Scale; + angle_t AngleOffset; +}; + // [RH] skin globals FPlayerSkin *skins; size_t numskins; @@ -290,7 +316,9 @@ static void R_InstallSprite (int num) { if (sprtemp[frame].rotate == -1) { - memset (&sprtemp[frame], 0, sizeof(sprtemp[0])); + memset (&sprtemp[frame].Texture, 0, sizeof(sprtemp[0].Texture)); + sprtemp[frame].Flip = 0; + sprtemp[frame].rotate = 0; } } @@ -301,6 +329,7 @@ static void R_InstallSprite (int num) { memcpy (SpriteFrames[framestart+frame].Texture, sprtemp[frame].Texture, sizeof(sprtemp[frame].Texture)); SpriteFrames[framestart+frame].Flip = sprtemp[frame].Flip; + SpriteFrames[framestart+frame].Voxel = sprtemp[frame].Voxel; } // Let the textures know about the rotations @@ -338,41 +367,100 @@ void R_InitSpriteDefs () { int Head, Next; } *hashes; - unsigned int i, max; + struct VHasher + { + int Head, Next, Name, Spin; + char Frame; + } *vhashes; + unsigned int i, j, smax, vmax; DWORD intname; // Create a hash table to speed up the process - max = TexMan.NumTextures(); - hashes = (Hasher *)alloca (sizeof(Hasher) * max); - for (i = 0; i < max; ++i) - { - hashes[i].Head = -1; - } - for (i = 0; i < max; ++i) + smax = TexMan.NumTextures(); + hashes = new Hasher[smax]; + clearbuf(hashes, sizeof(Hasher)*smax/4, -1); + for (i = 0; i < smax; ++i) { FTexture *tex = TexMan.ByIndex(i); if (tex->UseType == FTexture::TEX_Sprite && strlen(tex->Name) >= 6) { - DWORD bucket = tex->dwName % max; + size_t bucket = tex->dwName % smax; hashes[i].Next = hashes[bucket].Head; hashes[bucket].Head = i; } } + // Repeat, for voxels + vmax = Wads.GetNumLumps(); + vhashes = new VHasher[vmax]; + clearbuf(vhashes, sizeof(VHasher)*vmax/4, -1); + for (i = 0; i < vmax; ++i) + { + if (Wads.GetLumpNamespace(i) == ns_voxels) + { + char name[9]; + size_t namelen; + int spin; + int sign; + + Wads.GetLumpName(name, i); + name[8] = 0; + namelen = strlen(name); + if (namelen < 4) + { // name is too short + continue; + } + if (name[4] != '\0' && name[4] != ' ' && (name[4] < 'A' || name[4] >= 'A' + MAX_SPRITE_FRAMES)) + { // frame char is invalid + continue; + } + spin = 0; + sign = 2; // 2 to convert from deg/halfsec to deg/sec + j = 5; + if (j < namelen && name[j] == '-') + { // a minus sign is okay, but only before any digits + j++; + sign = -2; + } + for (; j < namelen; ++j) + { // the remainder to the end of the name must be digits + if (name[j] >= '0' && name[j] <= '9') + { + spin = spin * 10 + name[j] - '0'; + } + else + { + break; + } + } + if (j < namelen) + { // the spin part is invalid + continue; + } + memcpy(&vhashes[i].Name, name, 4); + vhashes[i].Frame = name[4]; + vhashes[i].Spin = spin * sign; + size_t bucket = vhashes[i].Name % vmax; + vhashes[i].Next = vhashes[bucket].Head; + vhashes[bucket].Head = i; + } + } + // scan all the lump names for each of the names, noting the highest frame letter. for (i = 0; i < sprites.Size(); ++i) { memset (sprtemp, 0xFF, sizeof(sprtemp)); - for (int j = 0; j < MAX_SPRITE_FRAMES; ++j) + for (j = 0; j < MAX_SPRITE_FRAMES; ++j) { sprtemp[j].Flip = 0; + sprtemp[j].Voxel = NULL; } maxframe = -1; intname = sprites[i].dwName; // scan the lumps, filling in the frames for whatever is found - int hash = hashes[intname % max].Head; + int hash = hashes[intname % smax].Head; while (hash != -1) { FTexture *tex = TexMan[hash]; @@ -385,9 +473,345 @@ void R_InitSpriteDefs () } hash = hashes[hash].Next; } + + // repeat, for voxels + hash = vhashes[intname % vmax].Head; + while (hash != -1) + { + VHasher *vh = &vhashes[hash]; + if (vh->Name == intname) + { + FVoxel *vox = R_LoadKVX(hash); + if (vox == NULL) + { + Printf("%s is not a valid voxel file\n", Wads.GetLumpFullName(hash)); + } + else + { + FVoxelDef *voxdef = new FVoxelDef; + + voxdef->Voxel = vox; + voxdef->Scale = FRACUNIT; + voxdef->DroppedSpin = voxdef->PlacedSpin = vh->Spin; + voxdef->AngleOffset = 0; + + Voxels.Push(vox); + VoxelDefs.Push(voxdef); + + if (vh->Frame == ' ' || vh->Frame == '\0') + { // voxel applies to every sprite frame + for (j = 0; j < MAX_SPRITE_FRAMES; ++j) + { + if (sprtemp[j].Voxel == NULL) + { + sprtemp[j].Voxel = voxdef; + } + } + maxframe = MAX_SPRITE_FRAMES-1; + } + else + { // voxel applies to a specific frame + j = vh->Frame - 'A'; + sprtemp[j].Voxel = voxdef; + maxframe = MAX(maxframe, j); + } + } + } + hash = vh->Next; + } R_InstallSprite ((int)i); } + + delete[] hashes; + delete[] vhashes; +} + +//========================================================================== +// +// R_ExtendSpriteFrames +// +// Extends a sprite so that it can hold the desired frame. +// +//========================================================================== + +static void R_ExtendSpriteFrames(spritedef_t &spr, int frame) +{ + unsigned int i, newstart; + + if (spr.numframes >= ++frame) + { // The sprite already has enough frames, so do nothing. + return; + } + + if (spr.numframes == 0 || (spr.spriteframes + spr.numframes == SpriteFrames.Size())) + { // Sprite's frames are at the end of the array, or it has no frames + // at all, so we can tack the new frames directly on to the end + // of the SpriteFrames array. + newstart = SpriteFrames.Reserve(frame - spr.numframes); + } + else + { // We need to allocate space for all the sprite's frames and copy + // the existing ones over to the new space. The old space will be + // lost. + newstart = SpriteFrames.Reserve(frame); + for (i = 0; i < spr.numframes; ++i) + { + SpriteFrames[newstart + i] = SpriteFrames[spr.spriteframes + i]; + } + spr.spriteframes = WORD(newstart); + newstart += i; + } + // Initialize all new frames to 0. + memset(&SpriteFrames[newstart], 0, sizeof(spriteframe_t)*(frame - spr.numframes)); + spr.numframes = frame; +} + +//========================================================================== +// +// VOX_ReadSpriteNames +// +// Reads a list of sprite names from a VOXELDEF lump. +// +//========================================================================== + +static bool VOX_ReadSpriteNames(FScanner &sc, TArray &vsprites) +{ + unsigned int i; + + vsprites.Clear(); + while (sc.GetString()) + { + // A sprite name list is terminated by an '=' character. + if (sc.String[0] == '=') + { + if (vsprites.Size() == 0) + { + sc.ScriptMessage("No sprites specified for voxel.\n"); + } + return true; + } + if (sc.StringLen != 4 && sc.StringLen != 5) + { + sc.ScriptMessage("Sprite name \"%s\" is wrong size.\n", sc.String); + } + else if (sc.StringLen == 5 && (sc.String[4] = toupper(sc.String[4]), sc.String[4] < 'A' || sc.String[4] >= 'A' + MAX_SPRITE_FRAMES)) + { + sc.ScriptMessage("Sprite frame %s is invalid.\n", sc.String[4]); + } + else + { + int frame = (sc.StringLen == 4) ? 255 : sc.String[4] - 'A'; + int spritename; + + for (i = 0; i < 4; ++i) + { + sc.String[i] = toupper(sc.String[i]); + } + spritename = *(int *)sc.String; + for (i = 0; i < sprites.Size(); ++i) + { + if (sprites[i].dwName == spritename) + { + break; + } + } + if (i != sprites.Size()) + { + vsprites.Push((frame << 24) | i); + } + } + } + if (vsprites.Size() != 0) + { + sc.ScriptMessage("Unexpected end of file\n"); + } + return false; +} + +//========================================================================== +// +// VOX_ReadOptions +// +// Reads a list of options from a VOXELDEF lump, terminated with a '}' +// character. The leading '{' must already be consumed +// +//========================================================================== + +static void VOX_ReadOptions(FScanner &sc, VoxelOptions &opts) +{ + while (sc.GetToken()) + { + if (sc.TokenType == '}') + { + return; + } + sc.TokenMustBe(TK_Identifier); + if (sc.Compare("scale")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_FloatConst); + opts.Scale = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare("spin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.DroppedSpin = opts.PlacedSpin = sc.Number; + } + else if (sc.Compare("placedspin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.PlacedSpin = sc.Number; + } + else if (sc.Compare("droppedspin")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + opts.DroppedSpin = sc.Number; + } + else if (sc.Compare("angleoffset")) + { + sc.MustGetToken('='); + sc.MustGetAnyToken(); + if (sc.TokenType == TK_IntConst) + { + sc.Float = sc.Number; + } + else + { + sc.TokenMustBe(TK_FloatConst); + } + opts.AngleOffset = angle_t(sc.Float * ANGLE_180 / 180.0); + } + else + { + sc.ScriptMessage("Unknown voxel option '%s'\n", sc.String); + if (sc.CheckToken('=')) + { + sc.MustGetAnyToken(); + } + } + } + sc.ScriptMessage("Unterminated voxel option block\n"); +} + +//========================================================================== +// +// VOX_GetVoxel +// +// Returns a voxel object for the given lump or NULL if it is not a valid +// voxel. If the voxel has already been loaded, it will be reused. +// +//========================================================================== + +static FVoxel *VOX_GetVoxel(int lumpnum) +{ + // Is this voxel already loaded? If so, return it. + for (unsigned i = 0; i < Voxels.Size(); ++i) + { + if (Voxels[i]->LumpNum == lumpnum) + { + return Voxels[i]; + } + } + FVoxel *vox = R_LoadKVX(lumpnum); + if (vox != NULL) + { + Voxels.Push(vox); + } + return vox; +} + +//========================================================================== +// +// VOX_AddVoxel +// +// Sets a voxel for a single sprite frame. +// +//========================================================================== + +static void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def) +{ + R_ExtendSpriteFrames(sprites[sprnum], frame); + SpriteFrames[sprites[sprnum].spriteframes + frame].Voxel = def; +} + +//========================================================================== +// +// R_InitVoxels +// +// Process VOXELDEF lumps for defining voxel options that cannot be +// condensed neatly into a sprite name format. +// +//========================================================================== + +void R_InitVoxels() +{ + int lump, lastlump = 0; + + while ((lump = Wads.FindLump("VOXELDEF", &lastlump)) != -1) + { + FScanner sc(lump); + TArray vsprites; + + while (VOX_ReadSpriteNames(sc, vsprites)) + { + FVoxel *voxeldata = NULL; + int voxelfile; + VoxelOptions opts; + + sc.SetCMode(true); + sc.MustGetToken(TK_StringConst); + voxelfile = Wads.CheckNumForFullName(sc.String, true, ns_voxels); + if (voxelfile < 0) + { + sc.ScriptMessage("Voxel \"%s\" not found.\n", sc.String); + } + else + { + voxeldata = VOX_GetVoxel(voxelfile); + if (voxeldata == NULL) + { + sc.ScriptMessage("\"%s\" is not a valid voxel file.\n", sc.String); + } + } + if (sc.CheckToken('{')) + { + VOX_ReadOptions(sc, opts); + } + sc.SetCMode(false); + if (voxeldata != NULL && vsprites.Size() != 0) + { + FVoxelDef *def = new FVoxelDef; + + def->Voxel = voxeldata; + def->Scale = opts.Scale; + def->DroppedSpin = opts.DroppedSpin; + def->PlacedSpin = opts.PlacedSpin; + def->AngleOffset = opts.AngleOffset; + VoxelDefs.Push(def); + + for (unsigned i = 0; i < vsprites.Size(); ++i) + { + int sprnum = int(vsprites[i] & 0xFFFFFF); + int frame = int(vsprites[i] >> 24); + if (frame == 255) + { // Apply voxel to all frames. + for (int j = MAX_SPRITE_FRAMES - 1; j >= 0; --j) + { + VOX_AddVoxel(sprnum, j, def); + } + } + else + { // Apply voxel to only one frame. + VOX_AddVoxel(sprnum, frame, def); + } + } + } + } + } } // [RH] @@ -882,6 +1306,7 @@ void R_InitSprites () } R_InitSpriteDefs (); + R_InitVoxels(); // [RH] Parse VOXELDEF NumStdSprites = sprites.Size(); R_InitSkins (); // [RH] Finish loading skin data @@ -956,6 +1381,19 @@ void R_DeinitSprites() spritesortersize = 0; spritesorter = NULL; } + + // Free offscreen buffer + if (OffscreenColorBuffer != NULL) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = NULL; + } + if (OffscreenCoverageBuffer != NULL) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = NULL; + } + OffscreenBufferHeight = OffscreenBufferWidth = 0; } // @@ -991,7 +1429,7 @@ vissprite_t *R_NewVisSprite (void) *p = new vissprite_t; } } - + vissprite_p++; return *(vissprite_p-1); } @@ -1171,6 +1609,75 @@ void R_DrawVisSprite (vissprite_t *vis) NetUpdate (); } +void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) +{ + ESPSResult mode; + int flags = 0; + + // Do setup for blending. + dc_colormap = spr->colormap; + mode = R_SetPatchStyle(spr->RenderStyle, spr->alpha, spr->Translation, spr->FillColor); + + if (mode == DontDraw) + { + return; + } + if (colfunc == fuzzcolfunc || colfunc == R_FillColumnP) + { + flags = DVF_OFFSCREEN | DVF_SPANSONLY; + } + else if (colfunc != basecolfunc) + { + flags = DVF_OFFSCREEN; + } + if (flags != 0) + { + R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); + } + + // Render the voxel, either directly to the screen or offscreen. + R_DrawVoxel(spr->gx, spr->gy, spr->gz, spr->angle, spr->xscale, spr->yscale, spr->voxel, spr->colormap, cliptop, clipbot, + minslabz, maxslabz, flags); + + // Blend the voxel, if that's what we need to do. + if (flags != 0) + { + for (int x = 0; x < viewwidth; ++x) + { + if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) + { + rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); + } + for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) + { + if (flags & DVF_SPANSONLY) + { + dc_x = x; + dc_yl = span->Start; + dc_yh = span->Stop - 1; + dc_count = span->Stop - span->Start; + dc_dest = ylookup[span->Start] + x + dc_destorg; + colfunc(); + } + else + { + unsigned int **tspan = &dc_ctspan[x & 3]; + (*tspan)[0] = span->Start; + (*tspan)[1] = span->Stop - 1; + *tspan += 2; + } + } + if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) + { + rt_draw4cols(x - 3); + } + } + } + + R_FinishSetPatchStyle(); + NetUpdate(); +} + // // R_ProjectSprite // Generates a vissprite for a thing if it might be visible. @@ -1186,13 +1693,14 @@ void R_ProjectSprite (AActor *thing, int fakeside) fixed_t tx, tx2; fixed_t tz; - fixed_t xscale; + fixed_t xscale, yscale; int x1; int x2; FTextureID picnum; FTexture *tex; + FVoxelDef *voxel; WORD flip; @@ -1221,26 +1729,8 @@ void R_ProjectSprite (AActor *thing, int fakeside) tz = DMulScale20 (tr_x, viewtancos, tr_y, viewtansin); - // thing is behind view plane? - if (tz < MINZ) - return; - - tx = DMulScale16 (tr_x, viewsin, -tr_y, viewcos); - - // [RH] Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = -tx; - } - tx2 = tx >> 4; - - // too far off the side? - if ((abs (tx) >> 6) > tz) - { - return; - } - - xscale = DivScale12 (centerxfrac, tz); + tex = NULL; + voxel = NULL; if (thing->picnum.isValid()) { @@ -1306,52 +1796,53 @@ void R_ProjectSprite (AActor *thing, int fakeside) picnum = sprframe->Texture[rot]; flip = sprframe->Flip & (1 << rot); tex = TexMan[picnum]; // Do not animate the rotation + if (r_drawvoxels) + { + voxel = sprframe->Voxel; + } } } - if (tex == NULL || tex->UseType == FTexture::TEX_Null) + if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null)) { return; } - // [RH] Added scaling - int scaled_to = tex->GetScaledTopOffset(); - int scaled_bo = scaled_to - tex->GetScaledHeight(); - gzt = fz + thing->scaleY * scaled_to; - gzb = fz + thing->scaleY * scaled_bo; + // thing is behind view plane? + if (voxel == NULL && tz < MINZ) + return; - // [RH] Reject sprites that are off the top or bottom of the screen - if (MulScale12 (globaluclip, tz) > viewz - gzb || - MulScale12 (globaldclip, tz) < viewz - gzt) + tx = DMulScale16 (tr_x, viewsin, -tr_y, viewcos); + + // [RH] Flip for mirrors + if (MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + tx2 = tx >> 4; + + // too far off the side? + if ((abs(tx) >> 6) > abs(tz)) { return; } - // [RH] Flip for mirrors and renderflags - if ((MirrorFlags ^ thing->renderflags) & RF_XFLIP) + if (voxel == NULL) { - flip = !flip; + // [RH] Added scaling + int scaled_to = tex->GetScaledTopOffset(); + int scaled_bo = scaled_to - tex->GetScaledHeight(); + gzt = fz + thing->scaleY * scaled_to; + gzb = fz + thing->scaleY * scaled_bo; + } + else + { + xscale = FixedMul(thing->scaleX, voxel->Scale); + yscale = FixedMul(thing->scaleY, voxel->Scale); + gzt = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ) - thing->floorclip; + gzb = fz + MulScale8(yscale, voxel->Voxel->Mips[0].PivotZ - (voxel->Voxel->Mips[0].SizeZ << 8)); + if (gzt <= gzb) + return; } - - // calculate edges of the shape - const fixed_t thingxscalemul = DivScale16(thing->scaleX, tex->xScale); - - tx -= (flip ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; - x1 = centerx + MulScale32 (tx, xscale); - - // off the right side? - if (x1 > WindowRight) - return; - - tx += tex->GetWidth() * thingxscalemul; - x2 = centerx + MulScale32 (tx, xscale); - - // off the left side or too small? - if (x2 < WindowLeft || x2 <= x1) - return; - - xscale = FixedDiv(FixedMul(thing->scaleX, xscale), tex->xScale); - iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1); - x2--; // killough 3/27/98: exclude things totally separated // from the viewer, by either water or fake ceilings @@ -1380,50 +1871,129 @@ void R_ProjectSprite (AActor *thing, int fakeside) } } - // store information in a vissprite - vis = R_NewVisSprite (); + if (voxel == NULL) + { + xscale = DivScale12 (centerxfrac, tz); + + // [RH] Reject sprites that are off the top or bottom of the screen + if (MulScale12 (globaluclip, tz) > viewz - gzb || + MulScale12 (globaldclip, tz) < viewz - gzt) + { + return; + } + + // [RH] Flip for mirrors and renderflags + if ((MirrorFlags ^ thing->renderflags) & RF_XFLIP) + { + flip = !flip; + } + + // calculate edges of the shape + const fixed_t thingxscalemul = DivScale16(thing->scaleX, tex->xScale); + + tx -= (flip ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; + x1 = centerx + MulScale32 (tx, xscale); + + // off the right side? + if (x1 > WindowRight) + return; + + tx += tex->GetWidth() * thingxscalemul; + x2 = centerx + MulScale32 (tx, xscale); + + // off the left side or too small? + if ((x2 < WindowLeft || x2 <= x1)) + return; + + xscale = FixedDiv(FixedMul(thing->scaleX, xscale), tex->xScale); + iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1); + x2--; + + fixed_t yscale = SafeDivScale16(thing->scaleY, tex->yScale); + + // store information in a vissprite + vis = R_NewVisSprite(); + + vis->xscale = xscale; + vis->yscale = Scale(InvZtoScale, yscale, tz << 4); + vis->idepth = (unsigned)DivScale32(1, tz) >> 1; // tz is 20.12, so idepth ought to be 12.20, but signed math makes it 13.19 + vis->floorclip = FixedDiv (thing->floorclip, yscale); + vis->texturemid = (tex->TopOffset << FRACBITS) - FixedDiv (viewz - fz + thing->floorclip, yscale); + vis->x1 = x1 < WindowLeft ? WindowLeft : x1; + vis->x2 = x2 > WindowRight ? WindowRight : x2; + vis->angle = thing->angle; + + if (flip) + { + vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale * (vis->x1 - x1); + } + else + { + vis = R_NewVisSprite(); + + vis->xscale = xscale; + vis->yscale = yscale; + vis->x1 = WindowLeft; + vis->x2 = WindowRight; + vis->idepth = (unsigned)DivScale32(1, MAX(tz, MINZ)) >> 1; + vis->floorclip = thing->floorclip; + + fz -= thing->floorclip; + + vis->angle = thing->angle + voxel->AngleOffset; + + int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; + if (voxelspin != 0) + { + double ang = double(I_FPSTime()) * voxelspin / 1000; + vis->angle += angle_t(ang * (4294967296.f / 360)); + } + + // These are irrelevant for voxels. + vis->texturemid = 0x1CEDBEEF; + vis->startfrac = 0x1CEDBEEF; + vis->xiscale = 0x1CEDBEEF; + } // killough 3/27/98: save sector for special clipping later vis->heightsec = heightsec; vis->sector = thing->Sector; - fixed_t yscale = DivScale16(thing->scaleY, tex->yScale); + vis->cx = tx2; + vis->depth = tz; + vis->gx = fx; + vis->gy = fy; + vis->gz = fz; + vis->gzb = gzb; // [RH] use gzb, not thing->z + vis->gzt = gzt; // killough 3/27/98 vis->renderflags = thing->renderflags; vis->RenderStyle = thing->RenderStyle; vis->FillColor = thing->fillcolor; - vis->xscale = xscale; - vis->yscale = Scale (InvZtoScale, yscale, tz << 4); - vis->depth = tz; - vis->idepth = (DWORD)DivScale32 (1, tz) >> 1; // tz is 20.12, so idepth ought to be 12.20, but - vis->cx = tx2; // signed math makes it 13.19 - vis->gx = fx; - vis->gy = fy; - vis->gz = gzb; // [RH] use gzb, not thing->z - vis->gzt = gzt; // killough 3/27/98 - vis->floorclip = FixedDiv (thing->floorclip, yscale); - vis->texturemid = (tex->TopOffset << FRACBITS) - - FixedDiv (viewz-fz+thing->floorclip, yscale); - vis->x1 = x1 < WindowLeft ? WindowLeft : x1; - vis->x2 = x2 > WindowRight ? WindowRight : x2; vis->Translation = thing->Translation; // [RH] thing translation table vis->FakeFlatStat = fakeside; vis->alpha = thing->alpha; - vis->pic = tex; - if (flip) + if (voxel != NULL) { - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - vis->xiscale = -iscale; + vis->voxel = voxel->Voxel; + vis->bIsVoxel = true; } else { - vis->startfrac = 0; - vis->xiscale = iscale; + vis->pic = tex; + vis->bIsVoxel = false; } - if (vis->x1 > x1) - vis->startfrac += vis->xiscale*(vis->x1-x1); - // The software renderer cannot invert the source without inverting the overlay // too. That means if the source is inverted, we need to do the reverse of what // the invert overlay flag says to do. @@ -1477,7 +2047,7 @@ void R_ProjectSprite (AActor *thing, int fakeside) else { // diminished light vis->colormap = mybasecolormap->Maps + (GETPALOOKUP ( - (fixed_t)DivScale12 (r_SpriteVisibility, tz), spriteshade) << COLORMAPSHIFT); + (fixed_t)DivScale12 (r_SpriteVisibility, MAX(tz, MINZ)), spriteshade) << COLORMAPSHIFT); } } } @@ -2081,19 +2651,23 @@ void R_DrawSprite (vissprite_t *spr) static short cliptop[MAXWIDTH]; drawseg_t *ds; int i; + int x1, x2; int r1, r2; short topclip, botclip; short *clip1, *clip2; // [RH] Check for particles - if (spr->pic == NULL) + if (!spr->bIsVoxel && spr->pic == NULL) { R_DrawParticle (spr); return; } + x1 = spr->x1; + x2 = spr->x2; + // [RH] Quickly reject sprites with bad x ranges. - if (spr->x1 > spr->x2) + if (x1 > x2) return; // [RH] Sprites split behind a one-sided line can also be discarded. @@ -2111,54 +2685,62 @@ void R_DrawSprite (vissprite_t *spr) // [RH] rewrote this to be based on which part of the sector is really visible fixed_t scale = MulScale19 (InvZtoScale, spr->idepth); + fixed_t hzb = FIXED_MIN, hzt = FIXED_MAX; - if (spr->heightsec && - !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + if (spr->bIsVoxel && spr->floorclip != 0) + { + hzb = spr->gzb; + } + + if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) { // only things in specially marked sectors if (spr->FakeFlatStat != FAKED_AboveCeiling) { - fixed_t h = spr->heightsec->floorplane.ZatPoint (spr->gx, spr->gy); - //h = (centeryfrac - FixedMul (h-viewz, spr->yscale)) >> FRACBITS; - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; + fixed_t hz = spr->heightsec->floorplane.ZatPoint (spr->gx, spr->gy); + fixed_t h = (centeryfrac - FixedMul (hz-viewz, scale)) >> FRACBITS; if (spr->FakeFlatStat == FAKED_BelowFloor) { // seen below floor: clip top - if (h > topclip) + if (!spr->bIsVoxel && h > topclip) { topclip = MIN (h, viewheight); } + hzt = MIN(hzt, hz); } else { // seen in the middle: clip bottom - if (h < botclip) + if (!spr->bIsVoxel && h < botclip) { botclip = MAX (0, h); } + hzb = MAX(hzb, hz); } } if (spr->FakeFlatStat != FAKED_BelowFloor) { - fixed_t h = spr->heightsec->ceilingplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; + fixed_t hz = spr->heightsec->ceilingplane.ZatPoint (spr->gx, spr->gy); + fixed_t h = (centeryfrac - FixedMul (hz-viewz, scale)) >> FRACBITS; if (spr->FakeFlatStat == FAKED_AboveCeiling) { // seen above ceiling: clip bottom - if (h < botclip) + if (!spr->bIsVoxel && h < botclip) { botclip = MAX (0, h); } + hzb = MAX(hzb, hz); } else { // seen in the middle: clip top - if (h > topclip) + if (!spr->bIsVoxel && h > topclip) { topclip = MIN (h, viewheight); } + hzt = MIN(hzt, hz); } } } // killough 3/27/98: end special clipping for deep water / fake ceilings - else if (spr->floorclip) + else if (!spr->bIsVoxel && spr->floorclip) { // [RH] Move floorclip stuff from R_DrawVisSprite to here int clip = ((centeryfrac - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + @@ -2191,9 +2773,14 @@ void R_DrawSprite (vissprite_t *spr) } #endif - i = spr->x2 - spr->x1 + 1; - clip1 = clipbot + spr->x1; - clip2 = cliptop + spr->x1; + if (topclip >= botclip) + { + return; + } + + i = x2 - x1 + 1; + clip1 = clipbot + x1; + clip2 = cliptop + x1; do { *clip1++ = botclip; @@ -2212,7 +2799,7 @@ void R_DrawSprite (vissprite_t *spr) for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough { // determine if the drawseg obscures the sprite - if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + if (ds->x1 > x2 || ds->x2 < x1 || (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == -1 && !ds->bFogBoundary) ) { @@ -2220,8 +2807,8 @@ void R_DrawSprite (vissprite_t *spr) continue; } - r1 = MAX (ds->x1, spr->x1); - r2 = MIN (ds->x2, spr->x2); + r1 = MAX (ds->x1, x1); + r2 = MIN (ds->x2, x2); fixed_t neardepth, fardepth; if (ds->sz1 < ds->sz2) @@ -2279,9 +2866,33 @@ void R_DrawSprite (vissprite_t *spr) // all clipping has been performed, so draw the sprite - mfloorclip = clipbot; - mceilingclip = cliptop; - R_DrawVisSprite (spr); + if (!spr->bIsVoxel) + { + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr); + } + else + { + // If it is completely clipped away, don't bother drawing it. + if (cliptop[x2] >= clipbot[x2]) + { + for (i = x1; i < x2; ++i) + { + if (cliptop[i] < clipbot[i]) + { + break; + } + } + if (i == x2) + { + return; + } + } + int minvoxely = spr->gzt <= hzt ? 0 : (spr->gzt - hzt) / spr->yscale; + int maxvoxely = spr->gzb > hzb ? INT_MAX : (spr->gzt - hzb) / spr->yscale; + R_DrawVisVoxel(spr, minvoxely, maxvoxely, cliptop, clipbot); + } } // @@ -2538,13 +3149,14 @@ void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, vis->cx = tx; vis->gx = particle->x; vis->gy = particle->y; - vis->gz = y1; + vis->gzb = y1; vis->gzt = y2; vis->x1 = x1; vis->x2 = x2; vis->Translation = 0; vis->startfrac = particle->color; vis->pic = NULL; + vis->bIsVoxel = false; vis->renderflags = particle->trans; vis->FakeFlatStat = fakeside; vis->floorclip = 0; @@ -2595,7 +3207,7 @@ void R_DrawParticle (vissprite_t *vis) BYTE *dest; DWORD fg; BYTE color = vis->colormap[vis->startfrac]; - int yl = vis->gz; + int yl = vis->gzb; int ycount = vis->gzt - yl + 1; int x1 = vis->x1; int countbase = vis->x2 - x1 + 1; @@ -2629,3 +3241,480 @@ void R_DrawParticle (vissprite_t *vis) dest += spacing; } while (--ycount); } + +static fixed_t distrecip(fixed_t y) +{ + y >>= 3; + return y == 0 ? 0 : SafeDivScale32(centerxwide, y); +} + +extern fixed_t baseyaspectmul; + +void R_DrawVoxel(fixed_t dasprx, fixed_t daspry, fixed_t dasprz, angle_t dasprang, + fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) +{ + int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; + fixed_t cosang, sinang, sprcosang, sprsinang; + int backx, backy, gxinc, gyinc; + int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; + int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yplc, yinc=0; + int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; + kvxslab_t *voxptr, *voxend; + FVoxelMipLevel *mip; + + const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; + const int xdimenscale = Scale(centerxwide, yaspectmul, 160); + const fixed_t globalposx = viewx >> 12; + const fixed_t globalposy = -viewy >> 12; + const fixed_t globalposz = -viewz >> 8; + + dasprx = dasprx >> 12; + daspry = -daspry >> 12; + dasprz = -dasprz >> 8; + daxscale >>= 10; + dayscale >>= 10; + + cosang = viewcos >> 2; + sinang = -viewsin >> 2; + sprcosang = finecosine[dasprang >> ANGLETOFINESHIFT] >> 2; + sprsinang = -finesine[dasprang >> ANGLETOFINESHIFT] >> 2; + + R_SetupDrawSlab(colormap); + + // Select mip level + i = abs(DMulScale8(dasprx - globalposx, viewcos, daspry - globalposy, -viewsin)); + i = DivScale6(i, MIN(daxscale, dayscale)); + j = FocalLengthX >> 3; + for (k = 0; k < voxobj->NumMips; ++k) + { + if (i < j) { break; } + i >>= 1; + } + if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; + + mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; + + minslabz >>= k; + maxslabz >>= k; + + daxscale <<= (k+8); dayscale <<= (k+8); + dazscale = FixedDiv(dayscale, baseyaspectmul); + daxscale = FixedDiv(daxscale, yaspectmul); + daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); + dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); + + daxscalerecip = (1<<30) / daxscale; + dayscalerecip = (1<<30) / dayscale; + + x = FixedMul(globalposx - dasprx, daxscalerecip); + y = FixedMul(globalposy - daspry, daxscalerecip); + backx = (DMulScale10(x, sprcosang, y, sprsinang) + mip->PivotX) >> 8; + backy = (DMulScale10(y, sprcosang, x, -sprsinang) + mip->PivotY) >> 8; + cbackx = clamp(backx, 0, mip->SizeX - 1); + cbacky = clamp(backy, 0, mip->SizeY - 1); + + sprcosang = MulScale14(daxscale, sprcosang); + sprsinang = MulScale14(daxscale, sprsinang); + + x = (dasprx - globalposx) - DMulScale18(mip->PivotX, sprcosang, mip->PivotY, -sprsinang); + y = (daspry - globalposy) - DMulScale18(mip->PivotY, sprcosang, mip->PivotX, sprsinang); + + cosang = FixedMul(cosang, dayscalerecip); + sinang = FixedMul(sinang, dayscalerecip); + + gxstart = y*cosang - x*sinang; + gystart = x*cosang + y*sinang; + gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); + gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); + if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; + + x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); + fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); + fixed_t *ggyinc = ggxinc + (j + 1); + for (i = 0; i <= j; i++) + { + ggxinc[i] = x; x += gxinc; + ggyinc[i] = y; y += gyinc; + } + + syoff = DivScale21(globalposz - dasprz, dazscale) + (mip->PivotZ << 7); + yoff = (abs(gxinc) + abs(gyinc)) >> 1; + + for (cnt = 0; cnt < 8; cnt++) + { + switch (cnt) + { + case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; + case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; + case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; + case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; + case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; + case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; + case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; + case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; + } + xe = cbackx; ye = cbacky; + if (cnt < 4) + { + if ((xi < 0) && (xe >= xs)) continue; + if ((xi > 0) && (xe <= xs)) continue; + if ((yi < 0) && (ye >= ys)) continue; + if ((yi > 0) && (ye <= ys)) continue; + } + else + { + if ((xi < 0) && (xe > xs)) continue; + if ((xi > 0) && (xe < xs)) continue; + if ((yi < 0) && (ye > ys)) continue; + if ((yi > 0) && (ye < ys)) continue; + xe += xi; ye += yi; + } + + i = ksgn(ys-backy)+ksgn(xs-backx)*3+4; + switch(i) + { + case 6: case 7: x1 = 0; y1 = 0; break; + case 8: case 5: x1 = gxinc; y1 = gyinc; break; + case 0: case 3: x1 = gyinc; y1 = -gxinc; break; + case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; + } + switch(i) + { + case 2: case 5: x2 = 0; y2 = 0; break; + case 0: case 1: x2 = gxinc; y2 = gyinc; break; + case 8: case 7: x2 = gyinc; y2 = -gxinc; break; + case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; + } + BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } + else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } + + /* Fix for non 90 degree viewing ranges */ + nxoff = FixedMul(x2 - x1, viewingrangerecip); + x1 = FixedMul(x1, viewingrangerecip); + + ggxstart = gxstart + ggyinc[ys]; + ggystart = gystart - ggxinc[ys]; + + for (x = xs; x != xe; x += xi) + { + BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + + nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; + ny = ggystart + ggyinc[x]; + for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) + { + if ((ny <= nytooclose) || (ny >= nytoofar)) continue; + voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + if (voxptr >= voxend) continue; + + lx = MulScale32(nx >> 3, distrecip(ny+y1)) + centerx; + if (lx < 0) lx = 0; + rx = MulScale32((nx + nxoff) >> 3, distrecip(ny+y2)) + centerx; + if (rx > viewwidth) rx = viewwidth; + if (rx <= lx) continue; + rx -= lx; + + fixed_t l1 = distrecip(ny-yoff); + fixed_t l2 = distrecip(ny+yoff); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) + { + const BYTE *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + fixed_t z1, z2; + + if (ztop < minslabz) + { + int diff = minslabz - ztop; + ztop = minslabz; + col += diff; + zleng -= diff; + } + if (ztop + zleng > maxslabz) + { + int diff = ztop + zleng - maxslabz; + zleng -= diff; + } + if (zleng <= 0) continue; + + j = (ztop << 15) - syoff; + if (j < 0) + { + k = j + (zleng << 15); + if (k < 0) + { + if ((voxptr->backfacecull & oand32) == 0) continue; + z2 = MulScale32(l2, k) + centery; /* Below slab */ + } + else + { + if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ + z2 = MulScale32(l1, k) + centery; + } + z1 = MulScale32(l1, j) + centery; + } + else + { + if ((voxptr->backfacecull & oand16) == 0) continue; + z1 = MulScale32(l2, j) + centery; /* Above slab */ + z2 = MulScale32(l1, j + (zleng << 15)) + centery; + } + + if (zleng == 1) + { + yplc = 0; yinc = 0; + if (z1 < daumost[lx]) z1 = daumost[lx]; + } + else + { + if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); + else if (z2 > z1) yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; + if (z1 < daumost[lx]) { yplc = yinc*(daumost[lx]-z1); z1 = daumost[lx]; } else yplc = 0; + } + if (z2 > dadmost[lx]) z2 = dadmost[lx]; + z2 -= z1; if (z2 <= 0) continue; + + if (!(flags & DVF_OFFSCREEN)) + { + // Draw directly to the screen. + R_DrawSlab(rx, yplc, z2, yinc, col, ylookup[z1] + lx + dc_destorg); + } + else + { + // Record the area covered and possibly draw to an offscreen buffer. + dc_yl = z1; + dc_yh = z1 + z2 - 1; + dc_count = z2; + dc_iscale = yinc; + for (int x = 0; x < rx; ++x) + { + OffscreenCoverageBuffer->InsertSpan(lx + x, z1, z1 + z2); + if (!(flags & DVF_SPANSONLY)) + { + dc_x = lx + x; + rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); + dc_source = col; + dc_texturefrac = yplc; + hcolfunc_pre(); + } + } + } + } + } + } + } +} + +//========================================================================== +// +// FCoverageBuffer Constructor +// +//========================================================================== + +FCoverageBuffer::FCoverageBuffer(int lists) + : Spans(NULL), FreeSpans(NULL) +{ + NumLists = lists; + Spans = new Span *[lists]; + memset(Spans, 0, sizeof(Span*)*lists); +} + +//========================================================================== +// +// FCoverageBuffer Destructor +// +//========================================================================== + +FCoverageBuffer::~FCoverageBuffer() +{ + if (Spans != NULL) + { + delete[] Spans; + } +} + +//========================================================================== +// +// FCoverageBuffer :: Clear +// +//========================================================================== + +void FCoverageBuffer::Clear() +{ + SpanArena.FreeAll(); + memset(Spans, 0, sizeof(Span*)*NumLists); + FreeSpans = NULL; +} + +//========================================================================== +// +// FCoverageBuffer :: InsertSpan +// +// start is inclusive. +// stop is exclusive. +// +//========================================================================== + +void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) +{ + assert(unsigned(listnum) < NumLists); + assert(start < stop); + + Span **span_p = &Spans[listnum]; + Span *span; + + if (*span_p == NULL || (*span_p)->Start > stop) + { // This list is empty or the first entry is after this one, so we can just insert the span. + goto addspan; + } + + // Insert the new span in order, merging with existing ones. + while (*span_p != NULL) + { + if ((*span_p)->Stop < start) // ===== (existing span) + { // Span ends before this one starts. // ++++ (new span) + span_p = &(*span_p)->NextSpan; + continue; + } + + // Does the new span overlap or abut the existing one? + if ((*span_p)->Start <= start) + { + if ((*span_p)->Stop >= stop) // ============= + { // The existing span completely covers this one. // +++++ + return; + } + // Extend the existing span with the new one. // ====== + span = *span_p; // +++++++ + span->Stop = stop; // (or) +++++ + + // Free up any spans we just covered up. + span_p = &(*span_p)->NextSpan; + while (*span_p != NULL && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) + { + Span *span = *span_p; // ====== ====== + *span_p = span->NextSpan; // +++++++++++++ + span->NextSpan = FreeSpans; + FreeSpans = span; + } + if (*span_p != NULL && (*span_p)->Start <= stop) // ======= ======== + { // Our new span connects two existing spans. // ++++++++++++++ + // They should all be collapsed into a single span. + span->Stop = (*span_p)->Stop; + span = *span_p; + *span_p = span->NextSpan; + span->NextSpan = FreeSpans; + FreeSpans = span; + } + goto check; + } + else if ((*span_p)->Start <= stop) // ===== + { // The new span extends the existing span from // ++++ + // the beginning. // (or) ++++ + (*span_p)->Start = start; + goto check; + } + else // ====== + { // No overlap, so insert a new span. // +++++ + goto addspan; + } + } + // Append a new span to the end of the list. +addspan: + span = AllocSpan(); + span->NextSpan = *span_p; + span->Start = start; + span->Stop = stop; + *span_p = span; +check: +#ifdef _DEBUG + // Validate the span list: Spans must be in order, and there must be + // at least one pixel between spans. + for (span = Spans[listnum]; span != NULL; span = span->NextSpan) + { + assert(span->Start < span->Stop); + if (span->NextSpan != NULL) + { + assert(span->Stop < span->NextSpan->Start); + } + } +#endif + ; +} + +//========================================================================== +// +// FCoverageBuffer :: AllocSpan +// +//========================================================================== + +FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() +{ + Span *span; + + if (FreeSpans != NULL) + { + span = FreeSpans; + FreeSpans = span->NextSpan; + } + else + { + span = (Span *)SpanArena.Alloc(sizeof(Span)); + } + return span; +} + +//========================================================================== +// +// R_CheckOffscreenBuffer +// +// Allocates the offscreen coverage buffer and optionally the offscreen +// color buffer. If they already exist but are the wrong size, they will +// be reallocated. +// +//========================================================================== + +void R_CheckOffscreenBuffer(int width, int height, bool spansonly) +{ + if (OffscreenCoverageBuffer == NULL) + { + assert(OffscreenColorBuffer == NULL && "The color buffer cannot exist without the coverage buffer"); + OffscreenCoverageBuffer = new FCoverageBuffer(width); + } + else if (OffscreenCoverageBuffer->NumLists != width) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = new FCoverageBuffer(width); + if (OffscreenColorBuffer != NULL) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = NULL; + } + } + else + { + OffscreenCoverageBuffer->Clear(); + } + + if (!spansonly) + { + if (OffscreenColorBuffer == NULL) + { + OffscreenColorBuffer = new BYTE[width * height]; + } + else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = new BYTE[width * height]; + } + } + OffscreenBufferWidth = width; + OffscreenBufferHeight = height; +} diff --git a/src/r_things.h b/src/r_things.h index 9ae796ce9..da31daf79 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -104,6 +104,14 @@ void R_ClearSprites (); void R_DrawMasked (); void R_DrawRemainingPlayerSprites (); +void R_CheckOffscreenBuffer(int width, int height, bool spansonly); + +enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2 }; + +void R_DrawVoxel(fixed_t dasprx, fixed_t daspry, fixed_t dasprz, angle_t dasprang, + fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); + void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); diff --git a/src/resourcefiles/file_wad.cpp b/src/resourcefiles/file_wad.cpp index ffa9c85e8..3a6de70ea 100644 --- a/src/resourcefiles/file_wad.cpp +++ b/src/resourcefiles/file_wad.cpp @@ -113,6 +113,7 @@ bool FWadFile::Open(bool quiet) SetNamespace("TX_START", "TX_END", ns_newtextures); SetNamespace("V_START", "V_END", ns_strifevoices); SetNamespace("HI_START", "HI_END", ns_hires); + SetNamespace("VX_START", "VX_END", ns_voxels); SkinHack(); } delete [] fileinfo; diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index c5bde5712..866605d14 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -116,6 +116,7 @@ void FResourceLump::LumpNameSetup(const char *iname) !strncmp(iname, "textures/", 9) ? ns_newtextures : !strncmp(iname, "hires/", 6) ? ns_hires : !strncmp(iname, "sprites/", 8) ? ns_sprites : + !strncmp(iname, "voxels/", 7) ? ns_voxels : !strncmp(iname, "colormaps/", 10) ? ns_colormaps : !strncmp(iname, "acs/", 4) ? ns_acslibrary : !strncmp(iname, "voices/", 7) ? ns_strifevoices : @@ -137,7 +138,7 @@ void FResourceLump::LumpNameSetup(const char *iname) // Since '\' can't be used as a file name's part inside a ZIP // we have to work around this for sprites because it is a valid // frame character. - else if (Namespace == ns_sprites) + else if (Namespace == ns_sprites || Namespace == ns_voxels) { char *c; diff --git a/src/tables.h b/src/tables.h index 72a79383d..aeb2c6991 100644 --- a/src/tables.h +++ b/src/tables.h @@ -60,7 +60,7 @@ extern fixed_t finesine[5*FINEANGLES/4]; // (encapsulated in a struct so that we can still use array accesses). struct cosine_inline { - fixed_t operator[] (unsigned int x) + fixed_t operator[] (unsigned int x) const { return finesine[x+FINEANGLES/4]; } diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index 2a3d1809f..836cc2daa 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -234,6 +234,7 @@ void FTextureManager::AddTiles (void *tiles) rot.Texture[8] = rot.Texture[9] = texnum.GetIndex() + 4; rot.Flip = 0x00FC; + rot.Voxel = NULL; tex->Rotations = SpriteFrames.Push (rot); } else if (rotType == 2) @@ -247,6 +248,7 @@ void FTextureManager::AddTiles (void *tiles) rot.Texture[17-j*2] = texnum.GetIndex() + j; } rot.Flip = 0; + rot.Voxel = NULL; tex->Rotations = SpriteFrames.Push (rot); } } diff --git a/src/v_video.cpp b/src/v_video.cpp index 450650f53..1986a2a97 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1313,6 +1313,21 @@ void DFrameBuffer::RenderView(player_t *player) FCanvasTextureInfo::UpdateAll (); } +//========================================================================== +// +// +// +//========================================================================== +extern TDeletingArray Voxels; + +void DFrameBuffer::RemapVoxels() +{ + for (unsigned i=0; iRemap(); + } +} + //=========================================================================== // // Render the view to a savegame picture @@ -1689,6 +1704,7 @@ void V_Init2() Printf ("Resolution: %d x %d\n", SCREENWIDTH, SCREENHEIGHT); screen->SetGamma (gamma); + screen->RemapVoxels(); FBaseCVar::ResetColors (); C_NewModeAdjust(); M_InitVideoModesMenu(); @@ -1733,43 +1749,61 @@ CUSTOM_CVAR (Int, vid_aspect, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) // 1: 16:9 // 2: 16:10 // 4: 5:4 -int CheckRatio (int width, int height) +int CheckRatio (int width, int height, int *trueratio) { + int fakeratio = -1; + int ratio; + if ((vid_aspect >=1) && (vid_aspect <=4)) { // [SP] User wants to force aspect ratio; let them. - return vid_aspect == 3? 0: int(vid_aspect); + fakeratio = vid_aspect == 3? 0: int(vid_aspect); } if (vid_nowidescreen) { if (!vid_tft) { - return 0; + fakeratio = 0; + } + else + { + fakeratio = (height * 5/4 == width) ? 4 : 0; } - return (height * 5/4 == width) ? 4 : 0; } // If the size is approximately 16:9, consider it so. if (abs (height * 16/9 - width) < 10) { - return 1; + ratio = 1; } // 16:10 has more variance in the pixel dimensions. Grr. - if (abs (height * 16/10 - width) < 60) + else if (abs (height * 16/10 - width) < 60) { // 320x200 and 640x400 are always 4:3, not 16:10 if ((width == 320 && height == 200) || (width == 640 && height == 400)) { - return 0; + ratio = 0; + } + else + { + ratio = 2; } - return 2; } // Unless vid_tft is set, 1280x1024 is 4:3, not 5:4. - if (height * 5/4 == width && vid_tft) + else if (height * 5/4 == width && vid_tft) { - return 4; + ratio = 4; } // Assume anything else is 4:3. - return 0; + else + { + ratio = 0; + } + + if (trueratio != NULL) + { + *trueratio = ratio; + } + return (fakeratio >= 0) ? fakeratio : ratio; } // First column: Base width (unused) diff --git a/src/v_video.h b/src/v_video.h index 0fb9eaf8a..b56a4d289 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -364,6 +364,9 @@ public: // Set the rect defining the area affected by blending. virtual void SetBlendingRect (int x1, int y1, int x2, int y2); + // Remap voxel palette + virtual void RemapVoxels(); + // render 3D view virtual void RenderView(player_t *player); @@ -500,7 +503,7 @@ void V_DrawFrame (int left, int top, int width, int height); extern "C" void ASM_PatchPitch (void); #endif -int CheckRatio (int width, int height); +int CheckRatio (int width, int height, int *trueratio=NULL); static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } extern const int BaseRatioSizes[5][4]; diff --git a/src/w_wad.h b/src/w_wad.h index dc1ea7794..76d94959d 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -62,6 +62,7 @@ typedef enum { ns_bloodmisc, ns_strifevoices, ns_hires, + ns_voxels, // These namespaces are only used to mark lumps in special subdirectories // so that their contents doesn't interfere with the global namespace. diff --git a/zdoom.vcproj b/zdoom.vcproj index c96f7e246..29801cd05 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -704,6 +704,10 @@ RelativePath=".\src\md5.cpp" > + + @@ -1353,6 +1357,10 @@ RelativePath=".\src\md5.h" > + + From 869b9c9ccc8f3a2183552e9ddf6d83768177c40c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 2 Jan 2011 18:11:48 +0000 Subject: [PATCH 223/815] - Fixed: FMODSoundRenderer::DrawWaveDebug() only allocated enough space for the wavearray based on the window_size, so for large numbers of output channels, it would not allocate enough space for the spectrum data (which is definied by SPECTRUM_SIZE, not the window_size) and write junk on the stack when drawing the spectrums, causing a crash. SVN r3087 (trunk) --- src/sound/fmodsound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 399322061..182ba488d 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -2555,7 +2555,7 @@ void FMODSoundRenderer::DrawWaveDebug(int mode) // 16 pixels of padding between each window. window_size = (screen->GetWidth() - 16) / numoutchans - 16; - float *wavearray = (float*)alloca(window_size*sizeof(float)); + float *wavearray = (float*)alloca(MAX(SPECTRUM_SIZE,window_size)*sizeof(float)); y = 16; y = DrawChannelGroupOutput(SfxGroup, wavearray, window_size, window_height, y, mode); From 46ad4ec4bd802217839d93fc47ae51cdd8c544da Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 2 Jan 2011 18:54:57 +0000 Subject: [PATCH 224/815] - Added "special" console command for executing action specials. - Fixed buffer overflow attack for DEM_RUNSCRIPT(2). SVN r3088 (trunk) --- src/c_cmds.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/d_net.cpp | 32 +++++++++++++++++++++++++++++++- src/d_protocol.h | 1 + src/version.h | 2 +- 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 0831c805f..b3e43f92f 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -453,7 +453,7 @@ CCMD (puke) if (argc < 2 || argc > 5) { - Printf (" puke