/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // ui_subsystem.c -- menu support/handling functions #include #ifdef _WIN32 #include #endif #include "../client/client.h" #include "ui_local.h" void (*m_drawfunc) (void); const char *(*m_keyfunc) (int key); qboolean hasitem; /* ======================================================================= SUPPORT ROUTINES ======================================================================= */ /* ================= FreeFileList ================= */ void FreeFileList (char **list, int n) { int i; for (i = 0; i < n; i++) { if (list && list[i]) { free(list[i]); list[i] = 0; } } free(list); } /* ================= ItemInList ================= */ qboolean ItemInList (char *check, int num, char **list) { int i; for (i=0;imin[0] = x1; thisObj->max[0] = x1 + w1; thisObj->min[1] = y1; thisObj->max[1] = y1 + h1; thisObj->index = index; } /* ============= UI_AddMainButton From Q2max ============= */ void UI_AddMainButton (mainmenuobject_t *thisObj, int index, int x, int y, char *name) { int w, h; float x1, y1, w1, h1; R_DrawGetPicSize (&w, &h, name); x1 = x; y1 = y; w1 = w; h1 = h; SCR_AdjustFrom640 (&x1, &y1, &w1, &h1, ALIGN_CENTER); thisObj->min[0] = x1; thisObj->max[0] = x1 + w1; thisObj->min[1] = y1; thisObj->max[1] = y1 + h1; switch (index) { case 0: thisObj->OpenMenu = M_Menu_Game_f; case 1: thisObj->OpenMenu = M_Menu_Multiplayer_f; case 2: thisObj->OpenMenu = M_Menu_Options_f; case 3: thisObj->OpenMenu = M_Menu_Video_f; case 4: thisObj->OpenMenu = M_Menu_Quit_f; } } /* ================= UI_RefreshCursorButtons From Q2max ================= */ void UI_RefreshCursorButtons(void) { cursor.buttonused[MOUSEBUTTON2] = true; cursor.buttondown[MOUSEBUTTON1] = false; cursor.buttonclicks[MOUSEBUTTON2] = 0; cursor.buttonused[MOUSEBUTTON1] = true; cursor.buttondown[MOUSEBUTTON2] = false; cursor.buttonclicks[MOUSEBUTTON1] = 0; } /* ================= UI_PushMenu ================= */ void UI_PushMenu ( void (*draw) (void), const char *(*key) (int k) ) { int i; if (Cvar_VariableValue ("maxclients") == 1 && Com_ServerState () && !cls.consoleActive) // Knightmare added Cvar_Set ("paused", "1"); // Knightmare- if just opened menu, and ingame and not DM, grab screen first if (cls.key_dest != key_menu && !Cvar_VariableValue("deathmatch") && Com_ServerState() == 2) //ss_game //&& !cl.cinematictime && Com_ServerState()) R_GrabScreen(); // if this menu is already present, drop back to that level // to avoid stacking menus by hotkeys for (i=0 ; i= MAX_MENU_DEPTH) Com_Error (ERR_FATAL, "UI_PushMenu: MAX_MENU_DEPTH"); m_layers[m_menudepth].draw = m_drawfunc; m_layers[m_menudepth].key = m_keyfunc; m_menudepth++; } m_drawfunc = draw; m_keyfunc = key; m_entersound = true; // Knightmare- added Psychospaz's mouse support UI_RefreshCursorLink(); UI_RefreshCursorButtons(); cls.key_dest = key_menu; } /* ================= UI_ForceMenuOff ================= */ void UI_ForceMenuOff (void) { // Knightmare- added Psychospaz's mouse support UI_RefreshCursorLink(); m_drawfunc = 0; m_keyfunc = 0; cls.key_dest = key_game; m_menudepth = 0; Key_ClearStates (); if (!cls.consoleActive && Cvar_VariableValue ("maxclients") == 1 && Com_ServerState()) // Knightmare added Cvar_Set ("paused", "0"); } /* ================= UI_PopMenu ================= */ void UI_PopMenu (void) { S_StartLocalSound( menu_out_sound ); if (m_menudepth < 1) Com_Error (ERR_FATAL, "UI_PopMenu: depth < 1"); m_menudepth--; m_drawfunc = m_layers[m_menudepth].draw; m_keyfunc = m_layers[m_menudepth].key; // Knightmare- added Psychospaz's mouse support UI_RefreshCursorLink(); UI_RefreshCursorButtons(); if (!m_menudepth) UI_ForceMenuOff (); } /* ================= UI_BackMenu ================= */ void UI_BackMenu (void *unused) { UI_PopMenu(); } /* ================= Default_MenuKey ================= */ const char *Default_MenuKey ( menuframework_s *m, int key ) { const char *sound = NULL; menucommon_s *item; if ( m ) { if ( ( item = Menu_ItemAtCursor( m ) ) != 0 ) { if ( item->type == MTYPE_FIELD ) { if ( Field_Key( ( menufield_s * ) item, key ) ) return NULL; } } } switch ( key ) { case K_JOY2: //BC B button. case K_BACKSPACE: case K_ESCAPE: UI_PopMenu(); return menu_out_sound; case K_AUX29: //BC dpad up. case K_KP_UPARROW: case K_UPARROW: if ( m ) { m->cursor--; // Knightmare- added Psychospaz's mouse support UI_RefreshCursorLink(); Menu_AdjustCursor( m, -1 ); sound = menu_move_sound; } break; case K_AUX31: //BC dpad down. case K_TAB: case K_KP_DOWNARROW: case K_DOWNARROW: if ( m ) { m->cursor++; // Knightmare- added Psychospaz's mouse support UI_RefreshCursorLink(); Menu_AdjustCursor( m, 1 ); sound = menu_move_sound; } break; case K_AUX32: //BC dpad left. case K_KP_LEFTARROW: case K_LEFTARROW: if ( m ) { Menu_SlideItem( m, -1 ); sound = menu_move_sound; } break; case K_AUX30: //BC dpad right. case K_KP_RIGHTARROW: case K_RIGHTARROW: if ( m ) { Menu_SlideItem( m, 1 ); sound = menu_move_sound; } break; /*case K_MOUSE1: case K_MOUSE2: case K_MOUSE3: //Knightmare 12/22/2001 case K_MOUSE4: case K_MOUSE5:*/ //end Knightmare case K_JOY1: //case K_JOY2: case K_JOY3: case K_JOY4: case K_AUX1: case K_AUX2: case K_AUX3: case K_AUX4: case K_AUX5: case K_AUX6: case K_AUX7: case K_AUX8: case K_AUX9: case K_AUX10: case K_AUX11: case K_AUX12: case K_AUX13: case K_AUX14: case K_AUX15: case K_AUX16: case K_AUX17: case K_AUX18: case K_AUX19: case K_AUX20: case K_AUX21: case K_AUX22: case K_AUX23: case K_AUX24: case K_AUX25: case K_AUX26: case K_AUX27: case K_AUX28: //case K_AUX29: //case K_AUX30: //case K_AUX31: //case K_AUX32: case K_SPACE: case K_KP_ENTER: case K_ENTER: if ( m ) { if ( item->type == MTYPE_CHECKBOX ) { Menu_SlideItem( m, 1 ); sound = menu_click; } else { Menu_SelectItem( m ); if ( item->type == MTYPE_ACTION ) sound = menu_move_sound; } } break; } return sound; } /* ================= UI_Init ================= */ void UI_Init (void) { // init this cvar here so M_Print can use it if (!alt_text_color) alt_text_color = Cvar_Get ("alt_text_color", "2", CVAR_ARCHIVE); UI_LoadMapList(); // load map list Cmd_AddCommand ("menu_main", M_Menu_Main_f); Cmd_AddCommand ("menu_game", M_Menu_Game_f); Cmd_AddCommand ("menu_loadgame", M_Menu_LoadGame_f); Cmd_AddCommand ("menu_savegame", M_Menu_SaveGame_f); Cmd_AddCommand ("menu_credits", M_Menu_Credits_f ); Cmd_AddCommand ("menu_multiplayer", M_Menu_Multiplayer_f ); Cmd_AddCommand ("menu_joinserver", M_Menu_JoinServer_f); Cmd_AddCommand ("menu_addressbook", M_Menu_AddressBook_f); Cmd_AddCommand ("menu_startserver", M_Menu_StartServer_f); Cmd_AddCommand ("menu_dmoptions", M_Menu_DMOptions_f); Cmd_AddCommand ("menu_playerconfig", M_Menu_PlayerConfig_f); Cmd_AddCommand ("menu_downloadoptions", M_Menu_DownloadOptions_f); Cmd_AddCommand ("menu_video", M_Menu_Video_f); Cmd_AddCommand ("menu_video_advanced", M_Menu_Video_Advanced_f); Cmd_AddCommand ("menu_options", M_Menu_Options_f); Cmd_AddCommand ("menu_sound", M_Menu_Options_Sound_f); Cmd_AddCommand ("menu_controls", M_Menu_Options_Controls_f); Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("menu_ingame", M_Menu_Options_Ingame_f); Cmd_AddCommand ("menu_interface", M_Menu_Options_Interface_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); } /* ======================================================================= Menu Mouse Cursor - psychospaz ======================================================================= */ /* ================= UI_RefreshCursorMenu ================= */ void UI_RefreshCursorMenu (void) { cursor.menu = NULL; } /* ================= UI_RefreshCursorLink ================= */ void UI_RefreshCursorLink (void) { cursor.menuitem = NULL; } /* ================= Slider_CursorPositionX ================= */ int Slider_CursorPositionX (menuslider_s *s) { float range; range = (s->curvalue - s->minvalue) / (float)(s->maxvalue - s->minvalue); if (range < 0) range = 0; if (range > 1) range = 1; return (int)(SCR_ScaledVideo(MENU_FONT_SIZE) + RCOLUMN_OFFSET + (SLIDER_RANGE)*SCR_ScaledVideo(MENU_FONT_SIZE) * range); } /* ================= NewSliderValueForX ================= */ int NewSliderValueForX (int x, menuslider_s *s) { float newValue, sliderbase; int newValueInt; int pos; sliderbase = s->generic.x + s->generic.parent->x + MENU_FONT_SIZE + RCOLUMN_OFFSET; SCR_AdjustFrom640 (&sliderbase, NULL, NULL, NULL, ALIGN_CENTER); pos = x - sliderbase; // pos = x - SCR_ScaledVideo(s->generic.x + s->generic.parent->x + MENU_FONT_SIZE + RCOLUMN_OFFSET); newValue = ((float)pos)/((SLIDER_RANGE-1)*SCR_ScaledVideo(MENU_FONT_SIZE)); newValueInt = s->minvalue + newValue * (float)(s->maxvalue - s->minvalue); return newValueInt; } /* ================= Slider_CheckSlide ================= */ void Slider_CheckSlide (menuslider_s *s) { if (s->curvalue > s->maxvalue) s->curvalue = s->maxvalue; else if (s->curvalue < s->minvalue) s->curvalue = s->minvalue; if (s->generic.callback) s->generic.callback( s ); } /* ================= Menu_DragSlideItem ================= */ void Menu_DragSlideItem (menuframework_s *menu, void *menuitem) { menucommon_s *item = (menucommon_s *) menuitem; menuslider_s *slider = (menuslider_s *) menuitem; slider->curvalue = NewSliderValueForX(cursor.x, slider); Slider_CheckSlide (slider); } /* ================= Menu_ClickSlideItem ================= */ void Menu_ClickSlideItem (menuframework_s *menu, void *menuitem) { int min, max; float x, w; menucommon_s *item = (menucommon_s *) menuitem; menuslider_s *slider = (menuslider_s *) menuitem; x = menu->x + item->x + Slider_CursorPositionX(slider) - 4; w = 8; SCR_AdjustFrom640 (&x, NULL, &w, NULL, ALIGN_CENTER); min = x; max = x + w; // min = SCR_ScaledVideo(menu->x + item->x + Slider_CursorPositionX(slider) - 4); // max = SCR_ScaledVideo(menu->x + item->x + Slider_CursorPositionX(slider) + 4); if (cursor.x < min) Menu_SlideItem( menu, -1 ); if (cursor.x > max) Menu_SlideItem( menu, 1 ); } /* ================= UI_Think_MouseCursor ================= */ void UI_Think_MouseCursor (void) { char * sound = NULL; menuframework_s *m = (menuframework_s *)cursor.menu; if (m_drawfunc == M_Main_Draw) //have to hack for main menu :p { UI_CheckMainMenuMouse(); return; } if (m_drawfunc == M_Credits_MenuDraw) //have to hack for credits :p { if ((cursor.buttonclicks[MOUSEBUTTON1])||(cursor.buttonclicks[MOUSEBUTTON2])) { cursor.buttonused[MOUSEBUTTON2] = true; cursor.buttonclicks[MOUSEBUTTON2] = 0; cursor.buttonused[MOUSEBUTTON1] = true; cursor.buttonclicks[MOUSEBUTTON1] = 0; S_StartLocalSound( menu_out_sound ); if (creditsBuffer) FS_FreeFile (creditsBuffer); UI_PopMenu(); return; } } else if (m_drawfunc == M_Quit_Draw) //hack for quit menu { if (cursor.buttonclicks[MOUSEBUTTON2]) { cursor.buttonused[MOUSEBUTTON2] = true; cursor.buttonclicks[MOUSEBUTTON2] = 0; cursor.buttonused[MOUSEBUTTON1] = true; cursor.buttonclicks[MOUSEBUTTON1] = 0; S_StartLocalSound ("world/cheer.wav"); UI_PopMenu (); return; } } //mouse clicking on the player model menu... if (m_drawfunc == PlayerConfig_MenuDraw) PlayerConfig_MouseClick(); if (m_drawfunc == Options_Ingame_MenuDraw) MenuCrosshair_MouseClick(); if (!m) return; //Exit with double click 2nd mouse button if (cursor.menuitem) { if (!hasitem) { sound = menu_move_sound; hasitem=true; } //MOUSE1 if (cursor.buttondown[MOUSEBUTTON1]) { if (cursor.menuitemtype == MENUITEM_SLIDER) { Menu_DragSlideItem(m, cursor.menuitem); } else if (!cursor.buttonused[MOUSEBUTTON1] && cursor.buttonclicks[MOUSEBUTTON1]) { if (cursor.menuitemtype == MENUITEM_ROTATE) { if (menu_rotate->value) Menu_SlideItem( m, -1 ); else Menu_SlideItem( m, 1 ); //zzzzzz //if ( item->type == MTYPE_CHECKBOX ) //sound = menu_move_sound; sound = menu_click; cursor.buttonused[MOUSEBUTTON1] = true; } else { cursor.buttonused[MOUSEBUTTON1] = true; Menu_MouseSelectItem( cursor.menuitem ); sound = menu_move_sound; } } } //MOUSE2 if (cursor.buttondown[MOUSEBUTTON2] && cursor.buttonclicks[MOUSEBUTTON2]) { if (cursor.menuitemtype == MENUITEM_SLIDER && !cursor.buttonused[MOUSEBUTTON2]) { Menu_ClickSlideItem(m, cursor.menuitem); sound = menu_move_sound; cursor.buttonused[MOUSEBUTTON2] = true; } else if (!cursor.buttonused[MOUSEBUTTON2]) { if (cursor.menuitemtype == MENUITEM_ROTATE) { if (menu_rotate->value) Menu_SlideItem( m, 1 ); else Menu_SlideItem( m, -1 ); sound = menu_move_sound; cursor.buttonused[MOUSEBUTTON2] = true; } } } } else if (!cursor.buttonused[MOUSEBUTTON2] && cursor.buttonclicks[MOUSEBUTTON2]==2 && cursor.buttondown[MOUSEBUTTON2]) { if (m_drawfunc==PlayerConfig_MenuDraw) PConfigAccept(); UI_PopMenu(); sound = menu_out_sound; cursor.buttonused[MOUSEBUTTON2] = true; cursor.buttonclicks[MOUSEBUTTON2] = 0; cursor.buttonused[MOUSEBUTTON1] = true; cursor.buttonclicks[MOUSEBUTTON1] = 0; } else { if (hasitem==true) hasitem=false; } if ( sound ) S_StartLocalSound( sound ); } /* ================= UI_Draw_Cursor ================= */ void UI_Draw_Cursor (void) { float alpha = 1, scale = SCR_ScaledVideo(1)*0.5; int w,h; char *overlay = NULL; char *cur_img = NULL; if (m_drawfunc == M_Main_Draw) { if (MainMenuMouseHover) { if ((cursor.buttonused[0] && cursor.buttonclicks[0]) || (cursor.buttonused[1] && cursor.buttonclicks[1])) { cur_img = "/gfx/m_cur_click.pcx"; alpha = 0.85 + 0.15*sin(anglemod(cl.time*0.005)); } else { cur_img = "/gfx/m_cur_hover.pcx"; alpha = 0.85 + 0.15*sin(anglemod(cl.time*0.005)); } } else cur_img = "/gfx/m_cur_main.pcx"; //overlay = "/gfx/m_cur_over.pcx"; } else { if (cursor.menuitem) { if (cursor.menuitemtype == MENUITEM_TEXT) { cur_img = "/gfx/m_cur_text.pcx"; } else { if ((cursor.buttonused[0] && cursor.buttonclicks[0]) || (cursor.buttonused[1] && cursor.buttonclicks[1])) { cur_img = "/gfx/m_cur_click.pcx"; alpha = 0.85 + 0.15*sin(anglemod(cl.time*0.005)); } else { cur_img = "/gfx/m_cur_hover.pcx"; alpha = 0.85 + 0.15*sin(anglemod(cl.time*0.005)); } //overlay = "/gfx/m_cur_over.pcx"; } } else { cur_img = "/gfx/m_cur_main.pcx"; //overlay = "/gfx/m_cur_over.pcx"; } } if (cur_img) { R_DrawGetPicSize( &w, &h, cur_img ); R_DrawScaledPic( cursor.x - scale*w/2, cursor.y - scale*h/2, scale, 1.0, cur_img); /* if (overlay) { R_DrawGetPicSize( &w, &h, overlay ); R_DrawScaledPic( cursor.x - scale*w/2, cursor.y - scale*h/2, scale, 1, overlay); } */ } } /*void UI_Draw_Cursor (void) { int w,h; //get sizing vars R_DrawGetPicSize( &w, &h, "m_mouse_cursor" ); w = SCR_ScaledVideo(w)*0.5; h = SCR_ScaledVideo(h)*0.5; R_DrawStretchPic (cursor.x-w/2, cursor.y-h/2, w, h, "m_mouse_cursor", 1.0); }*/ /* ================= UI_Draw ================= */ void UI_Draw (void) { if (cls.key_dest != key_menu) return; // scaled menu stuff //SCR_InitScreenScale(); // repaint everything next frame SCR_DirtyScreen (); // dim everything behind it down if (cl.cinematictime > 0 || cls.state == ca_disconnected) { //bc removing this background!!! /* if (R_DrawFindPic("/gfx/menu_background.pcx")) { R_DrawStretchPic (0, 0, viddef.width, viddef.height, "/gfx/menu_background.pcx", 1.0); //R_DrawFadeScreen (); } else*/ R_DrawFill2 (0,0,viddef.width, viddef.height, 0,0,0,255); } // ingame menu uses alpha //else if (R_DrawFindPic("/gfx/menu_background.pcx")) // R_DrawStretchPic (0, 0, viddef.width, viddef.height, "/gfx/menu_background.pcx", menu_alpha->value); else R_DrawFadeScreen (); // Knigthmare- added Psychospaz's mouse support UI_RefreshCursorMenu(); m_drawfunc (); // delay playing the enter sound until after the // menu has been drawn, to avoid delay while // caching images if (m_entersound) { S_StartLocalSound( menu_in_sound ); m_entersound = false; } // Knigthmare- added Psychospaz's mouse support //menu cursor for mouse usage :) UI_Draw_Cursor(); } /* ================= UI_Keydown ================= */ void UI_Keydown (int key) { const char *s; if (m_keyfunc) if ( ( s = m_keyfunc( key ) ) != 0 ) S_StartLocalSound( ( char * ) s ); }