diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 8c839701c..2bae0ae55 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,24 @@ -August 4, 2009 +August 6, 2009 +- Fixed: M_QuitResponse() tried to play a sound even when none was specified + in the gameinfo. +- Added Yes/No selections for Y/N messages so that you can answer them + entirely with a joystick. +- Fixed: Starting the menu at the title screen with a key other than Escape + left the top level menu out of the menu stack. +- Changed the save menu so that cancelling input of a new save name only + deactivates that control and does not completely close the menus. +- Fixed "any key" messages to override input to menus hidden beneath them and + to work with joysticks. +- Removed the input parameter from M_StartMessage and the corresponding + messageNeedsInput global, because it was redundant. Any messages that want + a Y/N response also supply a callback, and messages that don't care which + key you press don't supply a callback. +- Changed MKEY_Back so that it cancels out of text entry fields before + backing to the previous menu, which it already did for the keyboard. +- Changed the menu responder so that key downs always produce results, + regardless of whether or not an equivalent key is already down. + +August 4, 2009 - Added the MF6_STEPMISSILE flag so that the Whirlwind can "walk" up steps. - Changed the dword definition of PalEntry to uint32 so that it has one consistent definition across all source files. diff --git a/src/d_event.h b/src/d_event.h index d5e6c8e10..513ea10b4 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -115,6 +115,7 @@ typedef enum // Called by IO functions when input is detected. void D_PostEvent (const event_t* ev); +void D_RemoveNextCharEvent(); // diff --git a/src/d_main.cpp b/src/d_main.cpp index 83141b0e0..f3224713e 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -241,6 +241,8 @@ void D_ProcessEvents (void) for (; eventtail != eventhead ; eventtail = (eventtail+1)&(MAXEVENTS-1)) { ev = &events[eventtail]; + if (ev->type == EV_None) + continue; if (ev->type == EV_DeviceChange) UpdateJoystickMenu(I_UpdateDeviceList()); if (C_Responder (ev)) @@ -293,6 +295,41 @@ void D_PostEvent (const event_t *ev) eventhead = (eventhead+1)&(MAXEVENTS-1); } +//========================================================================== +// +// D_RemoveNextCharEvent +// +// Removes the next EV_GUI_Char event in the input queue. Used by the menu, +// since it (generally) consumes EV_GUI_KeyDown events and not EV_GUI_Char +// events, and it needs to ensure that there is no left over input when it's +// done. If there are multiple EV_GUI_KeyDowns before the EV_GUI_Char, then +// there are dead chars involved, so those should be removed, too. We do +// this by changing the message type to EV_None rather than by actually +// removing the event from the queue. +// +//========================================================================== + +void D_RemoveNextCharEvent() +{ + assert(events[eventtail].type == EV_GUI_Event && events[eventtail].subtype == EV_GUI_KeyDown); + for (int evnum = eventtail; evnum != eventhead; evnum = (evnum+1) & (MAXEVENTS-1)) + { + event_t *ev = &events[evnum]; + if (ev->type != EV_GUI_Event) + break; + if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_Char) + { + ev->type = EV_None; + if (ev->subtype == EV_GUI_Char) + break; + } + else + { + break; + } + } +} + //========================================================================== // // CVAR dmflags diff --git a/src/g_game.cpp b/src/g_game.cpp index 47a93df71..44d8eb5cf 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -859,7 +859,7 @@ bool G_Responder (event_t *ev) stricmp (cmd, "bumpgamma") && stricmp (cmd, "screenshot"))) { - M_StartControlPanel (true); + M_StartControlPanel (true, true); return true; } else diff --git a/src/m_menu.cpp b/src/m_menu.cpp index 054885a4a..97335382d 100644 --- a/src/m_menu.cpp +++ b/src/m_menu.cpp @@ -172,7 +172,8 @@ 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_SetupNextMenu (oldmenu_t *menudef); -static void M_StartMessage (const char *string, void(*routine)(int), bool input); +static void M_StartMessage (const char *string, void(*routine)(int)); +static void M_EndMessage (int key); // [RH] For player setup menu. void M_PlayerSetup (); @@ -222,9 +223,9 @@ static FSaveGameNode *lastSaveSlot; // Used for highlighting the most recently u static int messageToPrint; // 1 = message to be printed static const char *messageString; // ...and here is the message string! static EMenuState messageLastMenuActive; -static bool messageNeedsInput; // timed message = no input from user -static void (*messageRoutine)(int response); +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 @@ -606,8 +607,7 @@ static oldmenu_t SaveDef = // through console commands. CCMD (menu_main) { - M_StartControlPanel (true); - M_SetupNextMenu (TopLevelMenu); + M_StartControlPanel (true, true); } CCMD (menu_load) @@ -1295,9 +1295,9 @@ void M_LoadGame (int choice) if (netgame) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CLOADNET"), NULL, false); + M_StartMessage (GStrings("CLOADNET"), NULL); else - M_StartMessage (GStrings("LOADNET"), NULL, false); + M_StartMessage (GStrings("LOADNET"), NULL); return; } @@ -1370,7 +1370,7 @@ void M_SaveGame (int choice) { if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer)) { - M_StartMessage (GStrings("SAVEDEAD"), NULL, false); + M_StartMessage (GStrings("SAVEDEAD"), NULL); return; } @@ -1430,7 +1430,7 @@ void M_QuickSave () else mysnprintf (tempstring, countof(tempstring), GStrings("QSPROMPT"), quickSaveSlot->Title); strcpy (savegamestring, quickSaveSlot->Title); - M_StartMessage (tempstring, M_QuickSaveResponse, true); + M_StartMessage (tempstring, M_QuickSaveResponse); } @@ -1453,9 +1453,9 @@ void M_QuickLoad () if (netgame) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CQLOADNET"), NULL, false); + M_StartMessage (GStrings("CQLOADNET"), NULL); else - M_StartMessage (GStrings("QLOADNET"), NULL, false); + M_StartMessage (GStrings("QLOADNET"), NULL); return; } @@ -1471,7 +1471,7 @@ void M_QuickLoad () mysnprintf (tempstring, countof(tempstring), GStrings("CQLPROMPT"), quickSaveSlot->Title); else mysnprintf (tempstring, countof(tempstring), GStrings("QLPROMPT"), quickSaveSlot->Title); - M_StartMessage (tempstring, M_QuickLoadResponse, true); + M_StartMessage (tempstring, M_QuickLoadResponse); } // @@ -1574,9 +1574,9 @@ void M_NewGame(int choice) if (netgame && !demoplayback) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL, false); + M_StartMessage (GStrings("CNEWGAME"), NULL); else - M_StartMessage (GStrings("NEWGAME"), NULL, false); + M_StartMessage (GStrings("NEWGAME"), NULL); return; } @@ -1794,7 +1794,7 @@ void M_ChooseSkill (int choice) if (*msg==0) msg = GStrings("NIGHTMARE"); if (*msg=='$') msg = GStrings(msg+1); confirmskill = choice; - M_StartMessage (msg, M_VerifyNightmare, true); + M_StartMessage (msg, M_VerifyNightmare); return; } @@ -1813,12 +1813,12 @@ void M_Episode (int choice) { if (gameinfo.gametype == GAME_Doom) { - M_StartMessage(GStrings("SWSTRING"),NULL,false); + M_StartMessage(GStrings("SWSTRING"), NULL); //M_SetupNextMenu(&ReadDef); } else if (gameinfo.gametype == GAME_Chex) { - M_StartMessage(GStrings("CSWSTRING"),NULL,false); + M_StartMessage(GStrings("CSWSTRING"), NULL); } else { @@ -1856,9 +1856,9 @@ static void SCClass (int option) if (netgame) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL, false); + M_StartMessage (GStrings("CNEWGAME"), NULL); else - M_StartMessage (GStrings("NEWGAME"), NULL, false); + M_StartMessage (GStrings("NEWGAME"), NULL); return; } @@ -1892,9 +1892,9 @@ static void M_ChooseClass (int choice) if (netgame) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage (GStrings("CNEWGAME"), NULL, false); + M_StartMessage (GStrings("CNEWGAME"), NULL); else - M_StartMessage (GStrings("NEWGAME"), NULL, false); + M_StartMessage (GStrings("NEWGAME"), NULL); return; } @@ -1953,16 +1953,16 @@ void M_EndGame(int choice) if (netgame) { if(gameinfo.gametype == GAME_Chex) - M_StartMessage(GStrings("CNETEND"),NULL,false); + M_StartMessage(GStrings("CNETEND"), NULL); else - M_StartMessage(GStrings("NETEND"),NULL,false); + M_StartMessage(GStrings("NETEND"), NULL); return; } if(gameinfo.gametype == GAME_Chex) - M_StartMessage(GStrings("CENDGAME"),M_EndGameResponse,true); + M_StartMessage(GStrings("CENDGAME"), M_EndGameResponse); else - M_StartMessage(GStrings("ENDGAME"),M_EndGameResponse,true); + M_StartMessage(GStrings("ENDGAME"), M_EndGameResponse); } @@ -2005,7 +2005,7 @@ void M_QuitResponse(int ch) return; if (!netgame) { - if (gameinfo.quitSound) + if (gameinfo.quitSound.IsNotEmpty()) { S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.quitSound, 1, ATTN_NONE); I_WaitVBL (105); @@ -2049,7 +2049,7 @@ void M_QuitGame (int choice) EndString = GStrings("RAVENQUITMSG"); } - M_StartMessage (EndString, M_QuitResponse, true); + M_StartMessage (EndString, M_QuitResponse); } @@ -2736,19 +2736,19 @@ static void M_SlidePlayerBlue (int choice) // // Menu Functions // -void M_StartMessage (const char *string, void (*routine)(int), bool input) +void M_StartMessage (const char *string, void (*routine)(int)) { C_HideConsole (); messageLastMenuActive = menuactive; messageToPrint = 1; messageString = string; messageRoutine = routine; - messageNeedsInput = input; + messageSelection = 0; if (menuactive == MENU_Off) { M_ActivateMenuInput (); } - if (input) + if (messageRoutine != NULL) { S_StopSound (CHAN_VOICE); S_Sound (CHAN_VOICE | CHAN_UI, "menu/prompt", 1, ATTN_NONE); @@ -2756,6 +2756,22 @@ void M_StartMessage (const char *string, void (*routine)(int), bool input) 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", 1, ATTN_NONE); +} // @@ -2803,8 +2819,7 @@ bool M_Responder (event_t *ev) // Pop-up menu? if (ev->data1 == KEY_ESCAPE) { - M_StartControlPanel(true); - M_SetupNextMenu(TopLevelMenu); + M_StartControlPanel(true, true); return true; } // If devparm is set, pressing F1 always takes a screenshot no matter @@ -2900,6 +2915,15 @@ bool M_Responder (event_t *ev) } 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; @@ -2923,37 +2947,13 @@ bool M_Responder (event_t *ev) { // Take care of any messages that need input ch = tolower (ch); - if (messageNeedsInput) + assert(messageRoutine != NULL); + if (ch != ' ' && ch != 'n' && ch != 'y') { - // For each printable keystroke, both EV_GUI_KeyDown and - // EV_GUI_Char will be generated, in that order. If we close - // the menu after the first event arrives and the fullscreen - // console is up, the console will get the EV_GUI_Char event - // next. Therefore, the message input should only respond to - // EV_GUI_Char events (sans Escape, which only generates - // EV_GUI_KeyDown.) - if (ev->subtype != EV_GUI_Char && ch != GK_ESCAPE) - { -// return false; - } - if (ch != ' ' && ch != 'n' && ch != 'y' && ch != GK_ESCAPE) - { - return false; - } + return false; } - - menuactive = messageLastMenuActive; - messageToPrint = 0; - if (messageRoutine) - messageRoutine (ch); - - 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", 1, ATTN_NONE); + D_RemoveNextCharEvent(); + M_EndMessage(ch); return true; } else @@ -3038,6 +3038,12 @@ bool M_Responder (event_t *ev) 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) @@ -3048,14 +3054,12 @@ bool M_Responder (event_t *ev) } else { - if (MenuButtons[mkey].PressKey(ch)) + MenuButtons[mkey].PressKey(ch); + if (mkey <= MKEY_PageDown) { - if (mkey <= MKEY_PageDown) - { - MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; - } - M_ButtonHandler(mkey, false); + MenuButtonTickers[mkey] = KEY_REPEAT_DELAY; } + M_ButtonHandler(mkey, false); } } @@ -3077,10 +3081,35 @@ void M_ButtonHandler(EMenuKey key, bool repeat) } if (key == MKEY_Back) { - // 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(); + 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 (currentMenu == &SaveDef || currentMenu == &LoadDef) @@ -3244,7 +3273,7 @@ static bool M_SaveLoadResponder (event_t *ev) EndString.Format("%s" TEXTCOLOR_WHITE "%s" TEXTCOLOR_NORMAL "?\n\n%s", GStrings("MNU_DELETESG"), SelSaveGame->Title, GStrings("PRESSYN")); - M_StartMessage (EndString, M_DeleteSaveResponse, true); + M_StartMessage (EndString, M_DeleteSaveResponse); } break; @@ -3279,12 +3308,16 @@ static void M_LoadSelect (const FSaveGameNode *file) // // 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_ClearMenus; + genStringCancel = M_CancelSaveName; genStringLen = SAVESTRINGSIZE-1; if (file != &NewSaveNode) @@ -3323,7 +3356,7 @@ static void M_DeleteSaveResponse (int choice) // // M_StartControlPanel // -void M_StartControlPanel (bool makeSound) +void M_StartControlPanel (bool makeSound, bool wantTop) { // intro might call this repeatedly if (menuactive == MENU_On) @@ -3331,12 +3364,20 @@ void M_StartControlPanel (bool makeSound) for (int i = 0; i < NUM_MKEYS; ++i) { - MenuButtons[i].ResetTriggers(); + MenuButtons[i].ReleaseKey(0); } drawSkull = true; MenuStackDepth = 0; - currentMenu = TopLevelMenu; - itemOn = currentMenu->lastOn; + 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 (); @@ -3373,6 +3414,7 @@ void M_Drawer () // Horiz. & Vertically center string and print it. if (messageToPrint) { + int fontheight = SmallFont->GetHeight(); screen->Dim (fade); BorderNeedRefresh = screen->GetPageCount (); SB_state = screen->GetPageCount (); @@ -3387,10 +3429,19 @@ void M_Drawer () { screen->DrawText (SmallFont, CR_UNTRANSLATED, 160 - lines[i].Width/2, y, lines[i].Text, DTA_Clean, true, TAG_DONE); - y += SmallFont->GetHeight (); + 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) + { + M_DrawConText(CR_RED, 150, y + (fontheight + 1) * messageSelection, "\xd"); + } + } } else if (menuactive != MENU_Off) { diff --git a/src/m_menu.h b/src/m_menu.h index ff899511d..ce7ba99e2 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -52,7 +52,7 @@ 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); +void M_StartControlPanel (bool makeSound, bool wantTop=false); // Turns off the menu void M_ClearMenus (); @@ -257,6 +257,7 @@ enum EMenuKey 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]; diff --git a/src/m_options.cpp b/src/m_options.cpp index 0241074c8..1f127ff26 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -1480,7 +1480,7 @@ CCMD (sizeup) // Draws a string in the console font, scaled to the 8x8 cells // used by the default console font. -static void M_DrawConText (int color, int x, int y, const char *str) +void M_DrawConText (int color, int x, int y, const char *str) { int len = (int)strlen(str); diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index e9b4c375e..0a09e69a0 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -772,7 +772,7 @@ bool FMODSoundRenderer::Init() if (result == FMOD_OK) { // On Linux, FMOD defaults to OSS. If OSS is not present, it doesn't - // try ALSA, it just fails. We'll try for it, but only if OSS wasn't + // try ALSA; it just fails. We'll try for it, but only if OSS wasn't // explicitly specified for snd_output. if (driver == 0 && eval == FMOD_OUTPUTTYPE_AUTODETECT) { diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index dfe23836d..49a17d312 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -8,6 +8,8 @@ PRESSKEY = "press a key."; PRESSYN = "press y or n."; QUITMSG = "are you sure you want to\nquit this great game?"; +TXT_YES = "Yes"; +TXT_NO = "No"; // Quit Doom 1 messages QUITMSG1 = "please don't leave, there's more\ndemons to toast!"; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index 6b2d41b87..1f06dd49b 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -13,6 +13,8 @@ D_CDROM = "VERSION CD-ROM: DEFAULT.CFG DANS C:\\DOOMDATA\n"; PRESSKEY = "APPUYEZ SUR UNE TOUCHE."; PRESSYN = "APPUYEZ SUR Y OU N"; QUITMSG = "VOUS VOULEZ VRAIMENT\nQUITTER CE SUPER JEU?"; +TXT_YES = "Oui"; +TXT_NO = "Non"; // QuitDoom1 messages QUITMSG1 = "ne partez pas,Il ya encore\ndes démons a griller!"; diff --git a/wadsrc/static/language.ita b/wadsrc/static/language.ita index 4bd8236ec..b284a2e41 100644 --- a/wadsrc/static/language.ita +++ b/wadsrc/static/language.ita @@ -10,6 +10,8 @@ PRESSKEY = "premi un tasto."; PRESSYN = "premi y oppure n."; QUITMSG = "Sei sicuro di volere uscire\nda questo grandioso gioco?"; +TXT_YES = "Si"; +TXT_NO = "No"; // QuitDoom1 messages QUITMSG1 = "per favore non uscire, ci sono altri\ndemoni da ammazzare!";