File: client\menu.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 #include <ctype.h>
   21 #ifdef _WIN32
   22 #include <io.h>
   23 #endif
   24 #include "client.h"
   25 #include "../client/qmenu.h"
   26 
   27 static int      m_main_cursor;
   28 
   29 #define NUM_CURSOR_FRAMES 15
   30 
   31 static char *menu_in_sound              = "misc/menu1.wav";
   32 static char *menu_move_sound    = "misc/menu2.wav";
   33 static char *menu_out_sound             = "misc/menu3.wav";
   34 
   35 void M_Menu_Main_f (void);
   36         void M_Menu_Game_f (void);
   37                 void M_Menu_LoadGame_f (void);
   38                 void M_Menu_SaveGame_f (void);
   39                 void M_Menu_PlayerConfig_f (void);
   40                         void M_Menu_DownloadOptions_f (void);
   41                 void M_Menu_Credits_f( void );
   42         void M_Menu_Multiplayer_f( void );
   43                 void M_Menu_JoinServer_f (void);
   44                         void M_Menu_AddressBook_f( void );
   45                 void M_Menu_StartServer_f (void);
   46                         void M_Menu_DMOptions_f (void);
   47         void M_Menu_Video_f (void);
   48         void M_Menu_Options_f (void);
   49                 void M_Menu_Keys_f (void);
   50         void M_Menu_Quit_f (void);
   51 
   52         void M_Menu_Credits( void );
   53 
   54 qboolean        m_entersound;           // play after drawing a frame, so caching
   55                                                                 // won't disrupt the sound
   56 
   57 void    (*m_drawfunc) (void);
   58 const char *(*m_keyfunc) (int key);
   59 
   60 //=============================================================================
   61 /* Support Routines */
   62 
   63 #define MAX_MENU_DEPTH  8
   64 
   65 
   66 typedef struct
   67 {
   68         void    (*draw) (void);
   69         const char *(*key) (int k);
   70 } menulayer_t;
   71 
   72 menulayer_t     m_layers[MAX_MENU_DEPTH];
   73 int             m_menudepth;
   74 
   75 static void M_Banner( char *name )
   76 {
   77         int w, h;
   78 
   79         re.DrawGetPicSize (&w, &h, name );
   80         re.DrawPic( viddef.width / 2 - w / 2, viddef.height / 2 - 110, name );
   81 }
   82 
   83 void M_PushMenu ( void (*draw) (void), const char *(*key) (int k) )
   84 {
   85         int             i;
   86 
   87         if (Cvar_VariableValue ("maxclients") == 1 
   88                 && Com_ServerState ())
   89                 Cvar_Set ("paused", "1");
   90 
   91         // if this menu is already present, drop back to that level
   92         // to avoid stacking menus by hotkeys
   93         for (i=0 ; i<m_menudepth ; i++)
   94                 if (m_layers[i].draw == draw &&
   95                         m_layers[i].key == key)
   96                 {
   97                         m_menudepth = i;
   98                 }
   99 
  100         if (i == m_menudepth)
  101         {
  102                 if (m_menudepth >= MAX_MENU_DEPTH)
  103                         Com_Error (ERR_FATAL, "M_PushMenu: MAX_MENU_DEPTH");
  104                 m_layers[m_menudepth].draw = m_drawfunc;
  105                 m_layers[m_menudepth].key = m_keyfunc;
  106                 m_menudepth++;
  107         }
  108 
  109         m_drawfunc = draw;
  110         m_keyfunc = key;
  111 
  112         m_entersound = true;
  113 
  114         cls.key_dest = key_menu;
  115 }
  116 
  117 void M_ForceMenuOff (void)
  118 {
  119         m_drawfunc = 0;
  120         m_keyfunc = 0;
  121         cls.key_dest = key_game;
  122         m_menudepth = 0;
  123         Key_ClearStates ();
  124         Cvar_Set ("paused", "0");
  125 }
  126 
  127 void M_PopMenu (void)
  128 {
  129         S_StartLocalSound( menu_out_sound );
  130         if (m_menudepth < 1)
  131                 Com_Error (ERR_FATAL, "M_PopMenu: depth < 1");
  132         m_menudepth--;
  133 
  134         m_drawfunc = m_layers[m_menudepth].draw;
  135         m_keyfunc = m_layers[m_menudepth].key;
  136 
  137         if (!m_menudepth)
  138                 M_ForceMenuOff ();
  139 }
  140 
  141 
  142 const char *Default_MenuKey( menuframework_s *m, int key )
  143 {
  144         const char *sound = NULL;
  145         menucommon_s *item;
  146 
  147         if ( m )
  148         {
  149                 if ( ( item = Menu_ItemAtCursor( m ) ) != 0 )
  150                 {
  151                         if ( item->type == MTYPE_FIELD )
  152                         {
  153                                 if ( Field_Key( ( menufield_s * ) item, key ) )
  154                                         return NULL;
  155                         }
  156                 }
  157         }
  158 
  159         switch ( key )
  160         {
  161         case K_ESCAPE:
  162                 M_PopMenu();
  163                 return menu_out_sound;
  164         case K_KP_UPARROW:
  165         case K_UPARROW:
  166                 if ( m )
  167                 {
  168                         m->cursor--;
  169                         Menu_AdjustCursor( m, -1 );
  170                         sound = menu_move_sound;
  171                 }
  172                 break;
  173         case K_TAB:
  174                 if ( m )
  175                 {
  176                         m->cursor++;
  177                         Menu_AdjustCursor( m, 1 );
  178                         sound = menu_move_sound;
  179                 }
  180                 break;
  181         case K_KP_DOWNARROW:
  182         case K_DOWNARROW:
  183                 if ( m )
  184                 {
  185                         m->cursor++;
  186                         Menu_AdjustCursor( m, 1 );
  187                         sound = menu_move_sound;
  188                 }
  189                 break;
  190         case K_KP_LEFTARROW:
  191         case K_LEFTARROW:
  192                 if ( m )
  193                 {
  194                         Menu_SlideItem( m, -1 );
  195                         sound = menu_move_sound;
  196                 }
  197                 break;
  198         case K_KP_RIGHTARROW:
  199         case K_RIGHTARROW:
  200                 if ( m )
  201                 {
  202                         Menu_SlideItem( m, 1 );
  203                         sound = menu_move_sound;
  204                 }
  205                 break;
  206 
  207         case K_MOUSE1:
  208         case K_MOUSE2:
  209         case K_MOUSE3:
  210         case K_JOY1:
  211         case K_JOY2:
  212         case K_JOY3:
  213         case K_JOY4:
  214         case K_AUX1:
  215         case K_AUX2:
  216         case K_AUX3:
  217         case K_AUX4:
  218         case K_AUX5:
  219         case K_AUX6:
  220         case K_AUX7:
  221         case K_AUX8:
  222         case K_AUX9:
  223         case K_AUX10:
  224         case K_AUX11:
  225         case K_AUX12:
  226         case K_AUX13:
  227         case K_AUX14:
  228         case K_AUX15:
  229         case K_AUX16:
  230         case K_AUX17:
  231         case K_AUX18:
  232         case K_AUX19:
  233         case K_AUX20:
  234         case K_AUX21:
  235         case K_AUX22:
  236         case K_AUX23:
  237         case K_AUX24:
  238         case K_AUX25:
  239         case K_AUX26:
  240         case K_AUX27:
  241         case K_AUX28:
  242         case K_AUX29:
  243         case K_AUX30:
  244         case K_AUX31:
  245         case K_AUX32:
  246                 
  247         case K_KP_ENTER:
  248         case K_ENTER:
  249                 if ( m )
  250                         Menu_SelectItem( m );
  251                 sound = menu_move_sound;
  252                 break;
  253         }
  254 
  255         return sound;
  256 }
  257 
  258 //=============================================================================
  259 
  260 /*
  261 ================
  262 M_DrawCharacter
  263 
  264 Draws one solid graphics character
  265 cx and cy are in 320*240 coordinates, and will be centered on
  266 higher res screens.
  267 ================
  268 */
  269 void M_DrawCharacter (int cx, int cy, int num)
  270 {
  271         re.DrawChar ( cx + ((viddef.width - 320)>>1), cy + ((viddef.height - 240)>>1), num);
  272 }
  273 
  274 void M_Print (int cx, int cy, char *str)
  275 {
  276         while (*str)
  277         {
  278                 M_DrawCharacter (cx, cy, (*str)+128);
  279                 str++;
  280                 cx += 8;
  281         }
  282 }
  283 
  284 void M_PrintWhite (int cx, int cy, char *str)
  285 {
  286         while (*str)
  287         {
  288                 M_DrawCharacter (cx, cy, *str);
  289                 str++;
  290                 cx += 8;
  291         }
  292 }
  293 
  294 void M_DrawPic (int x, int y, char *pic)
  295 {
  296         re.DrawPic (x + ((viddef.width - 320)>>1), y + ((viddef.height - 240)>>1), pic);
  297 }
  298 
  299 
  300 /*
  301 =============
  302 M_DrawCursor
  303 
  304 Draws an animating cursor with the point at
  305 x,y.  The pic will extend to the left of x,
  306 and both above and below y.
  307 =============
  308 */
  309 void M_DrawCursor( int x, int y, int f )
  310 {
  311         char    cursorname[80];
  312         static qboolean cached;
  313 
  314         if ( !cached )
  315         {
  316                 int i;
  317 
  318                 for ( i = 0; i < NUM_CURSOR_FRAMES; i++ )
  319                 {
  320                         Com_sprintf( cursorname, sizeof( cursorname ), "m_cursor%d", i );
  321 
  322                         re.RegisterPic( cursorname );
  323                 }
  324                 cached = true;
  325         }
  326 
  327         Com_sprintf( cursorname, sizeof(cursorname), "m_cursor%d", f );
  328         re.DrawPic( x, y, cursorname );
  329 }
  330 
  331 void M_DrawTextBox (int x, int y, int width, int lines)
  332 {
  333         int             cx, cy;
  334         int             n;
  335 
  336         // draw left side
  337         cx = x;
  338         cy = y;
  339         M_DrawCharacter (cx, cy, 1);
  340         for (n = 0; n < lines; n++)
  341         {
  342                 cy += 8;
  343                 M_DrawCharacter (cx, cy, 4);
  344         }
  345         M_DrawCharacter (cx, cy+8, 7);
  346 
  347         // draw middle
  348         cx += 8;
  349         while (width > 0)
  350         {
  351                 cy = y;
  352                 M_DrawCharacter (cx, cy, 2);
  353                 for (n = 0; n < lines; n++)
  354                 {
  355                         cy += 8;
  356                         M_DrawCharacter (cx, cy, 5);
  357                 }
  358                 M_DrawCharacter (cx, cy+8, 8);
  359                 width -= 1;
  360                 cx += 8;
  361         }
  362 
  363         // draw right side
  364         cy = y;
  365         M_DrawCharacter (cx, cy, 3);
  366         for (n = 0; n < lines; n++)
  367         {
  368                 cy += 8;
  369                 M_DrawCharacter (cx, cy, 6);
  370         }
  371         M_DrawCharacter (cx, cy+8, 9);
  372 }
  373 
  374                 
  375 /*
  376 =======================================================================
  377 
  378 MAIN MENU
  379 
  380 =======================================================================
  381 */
  382 #define MAIN_ITEMS      5
  383 
  384 
  385 void M_Main_Draw (void)
  386 {
  387         int i;
  388         int w, h;
  389         int ystart;
  390         int     xoffset;
  391         int widest = -1;
  392         int totalheight = 0;
  393         char litname[80];
  394         char *names[] =
  395         {
  396                 "m_main_game",
  397                 "m_main_multiplayer",
  398                 "m_main_options",
  399                 "m_main_video",
  400                 "m_main_quit",
  401                 0
  402         };
  403 
  404         for ( i = 0; names[i] != 0; i++ )
  405         {
  406                 re.DrawGetPicSize( &w, &h, names[i] );
  407 
  408                 if ( w > widest )
  409                         widest = w;
  410                 totalheight += ( h + 12 );
  411         }
  412 
  413         ystart = ( viddef.height / 2 - 110 );
  414         xoffset = ( viddef.width - widest + 70 ) / 2;
  415 
  416         for ( i = 0; names[i] != 0; i++ )
  417         {
  418                 if ( i != m_main_cursor )
  419                         re.DrawPic( xoffset, ystart + i * 40 + 13, names[i] );
  420         }
  421         strcpy( litname, names[m_main_cursor] );
  422         strcat( litname, "_sel" );
  423         re.DrawPic( xoffset, ystart + m_main_cursor * 40 + 13, litname );
  424 
  425         M_DrawCursor( xoffset - 25, ystart + m_main_cursor * 40 + 11, (int)(cls.realtime / 100)%NUM_CURSOR_FRAMES );
  426 
  427         re.DrawGetPicSize( &w, &h, "m_main_plaque" );
  428         re.DrawPic( xoffset - 30 - w, ystart, "m_main_plaque" );
  429 
  430         re.DrawPic( xoffset - 30 - w, ystart + h + 5, "m_main_logo" );
  431 }
  432 
  433 
  434 const char *M_Main_Key (int key)
  435 {
  436         const char *sound = menu_move_sound;
  437 
  438         switch (key)
  439         {
  440         case K_ESCAPE:
  441                 M_PopMenu ();
  442                 break;
  443 
  444         case K_KP_DOWNARROW:
  445         case K_DOWNARROW:
  446                 if (++m_main_cursor >= MAIN_ITEMS)
  447                         m_main_cursor = 0;
  448                 return sound;
  449 
  450         case K_KP_UPARROW:
  451         case K_UPARROW:
  452                 if (--m_main_cursor < 0)
  453                         m_main_cursor = MAIN_ITEMS - 1;
  454                 return sound;
  455 
  456         case K_KP_ENTER:
  457         case K_ENTER:
  458                 m_entersound = true;
  459 
  460                 switch (m_main_cursor)
  461                 {
  462                 case 0:
  463                         M_Menu_Game_f ();
  464                         break;
  465 
  466                 case 1:
  467                         M_Menu_Multiplayer_f();
  468                         break;
  469 
  470                 case 2:
  471                         M_Menu_Options_f ();
  472                         break;
  473 
  474                 case 3:
  475                         M_Menu_Video_f ();
  476                         break;
  477 
  478                 case 4:
  479                         M_Menu_Quit_f ();
  480                         break;
  481                 }
  482         }
  483 
  484         return NULL;
  485 }
  486 
  487 
  488 void M_Menu_Main_f (void)
  489 {
  490         M_PushMenu (M_Main_Draw, M_Main_Key);
  491 }
  492 
  493 /*
  494 =======================================================================
  495 
  496 MULTIPLAYER MENU
  497 
  498 =======================================================================
  499 */
  500 static menuframework_s  s_multiplayer_menu;
  501 static menuaction_s             s_join_network_server_action;
  502 static menuaction_s             s_start_network_server_action;
  503 static menuaction_s             s_player_setup_action;
  504 
  505 static void Multiplayer_MenuDraw (void)
  506 {
  507         M_Banner( "m_banner_multiplayer" );
  508 
  509         Menu_AdjustCursor( &s_multiplayer_menu, 1 );
  510         Menu_Draw( &s_multiplayer_menu );
  511 }
  512 
  513 static void PlayerSetupFunc( void *unused )
  514 {
  515         M_Menu_PlayerConfig_f();
  516 }
  517 
  518 static void JoinNetworkServerFunc( void *unused )
  519 {
  520         M_Menu_JoinServer_f();
  521 }
  522 
  523 static void StartNetworkServerFunc( void *unused )
  524 {
  525         M_Menu_StartServer_f ();
  526 }
  527 
  528 void Multiplayer_MenuInit( void )
  529 {
  530         s_multiplayer_menu.x = viddef.width * 0.50 - 64;
  531         s_multiplayer_menu.nitems = 0;
  532 
  533         s_join_network_server_action.generic.type       = MTYPE_ACTION;
  534         s_join_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
  535         s_join_network_server_action.generic.x          = 0;
  536         s_join_network_server_action.generic.y          = 0;
  537         s_join_network_server_action.generic.name       = " join network server";
  538         s_join_network_server_action.generic.callback = JoinNetworkServerFunc;
  539 
  540         s_start_network_server_action.generic.type      = MTYPE_ACTION;
  541         s_start_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
  542         s_start_network_server_action.generic.x         = 0;
  543         s_start_network_server_action.generic.y         = 10;
  544         s_start_network_server_action.generic.name      = " start network server";
  545         s_start_network_server_action.generic.callback = StartNetworkServerFunc;
  546 
  547         s_player_setup_action.generic.type      = MTYPE_ACTION;
  548         s_player_setup_action.generic.flags  = QMF_LEFT_JUSTIFY;
  549         s_player_setup_action.generic.x         = 0;
  550         s_player_setup_action.generic.y         = 20;
  551         s_player_setup_action.generic.name      = " player setup";
  552         s_player_setup_action.generic.callback = PlayerSetupFunc;
  553 
  554         Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_join_network_server_action );
  555         Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_start_network_server_action );
  556         Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_player_setup_action );
  557 
  558         Menu_SetStatusBar( &s_multiplayer_menu, NULL );
  559 
  560         Menu_Center( &s_multiplayer_menu );
  561 }
  562 
  563 const char *Multiplayer_MenuKey( int key )
  564 {
  565         return Default_MenuKey( &s_multiplayer_menu, key );
  566 }
  567 
  568 void M_Menu_Multiplayer_f( void )
  569 {
  570         Multiplayer_MenuInit();
  571         M_PushMenu( Multiplayer_MenuDraw, Multiplayer_MenuKey );
  572 }
  573 
  574 /*
  575 =======================================================================
  576 
  577 KEYS MENU
  578 
  579 =======================================================================
  580 */
  581 char *bindnames[][2] =
  582 {
  583 {"+attack",             "attack"},
  584 {"weapnext",            "next weapon"},
  585 {"+forward",            "walk forward"},
  586 {"+back",                       "backpedal"},
  587 {"+left",                       "turn left"},
  588 {"+right",                      "turn right"},
  589 {"+speed",                      "run"},
  590 {"+moveleft",           "step left"},
  591 {"+moveright",          "step right"},
  592 {"+strafe",             "sidestep"},
  593 {"+lookup",             "look up"},
  594 {"+lookdown",           "look down"},
  595 {"centerview",          "center view"},
  596 {"+mlook",                      "mouse look"},
  597 {"+klook",                      "keyboard look"},
  598 {"+moveup",                     "up / jump"},
  599 {"+movedown",           "down / crouch"},
  600 
  601 {"inven",                       "inventory"},
  602 {"invuse",                      "use item"},
  603 {"invdrop",                     "drop item"},
  604 {"invprev",                     "prev item"},
  605 {"invnext",                     "next item"},
  606 
  607 {"cmd help",            "help computer" }, 
  608 { 0, 0 }
  609 };
  610 
  611 int                             keys_cursor;
  612 static int              bind_grab;
  613 
  614 static menuframework_s  s_keys_menu;
  615 static menuaction_s             s_keys_attack_action;
  616 static menuaction_s             s_keys_change_weapon_action;
  617 static menuaction_s             s_keys_walk_forward_action;
  618 static menuaction_s             s_keys_backpedal_action;
  619 static menuaction_s             s_keys_turn_left_action;
  620 static menuaction_s             s_keys_turn_right_action;
  621 static menuaction_s             s_keys_run_action;
  622 static menuaction_s             s_keys_step_left_action;
  623 static menuaction_s             s_keys_step_right_action;
  624 static menuaction_s             s_keys_sidestep_action;
  625 static menuaction_s             s_keys_look_up_action;
  626 static menuaction_s             s_keys_look_down_action;
  627 static menuaction_s             s_keys_center_view_action;
  628 static menuaction_s             s_keys_mouse_look_action;
  629 static menuaction_s             s_keys_keyboard_look_action;
  630 static menuaction_s             s_keys_move_up_action;
  631 static menuaction_s             s_keys_move_down_action;
  632 static menuaction_s             s_keys_inventory_action;
  633 static menuaction_s             s_keys_inv_use_action;
  634 static menuaction_s             s_keys_inv_drop_action;
  635 static menuaction_s             s_keys_inv_prev_action;
  636 static menuaction_s             s_keys_inv_next_action;
  637 
  638 static menuaction_s             s_keys_help_computer_action;
  639 
  640 static void M_UnbindCommand (char *command)
  641 {
  642         int             j;
  643         int             l;
  644         char    *b;
  645 
  646         l = strlen(command);
  647 
  648         for (j=0 ; j<256 ; j++)
  649         {
  650                 b = keybindings[j];
  651                 if (!b)
  652                         continue;
  653                 if (!strncmp (b, command, l) )
  654                         Key_SetBinding (j, "");
  655         }
  656 }
  657 
  658 static void M_FindKeysForCommand (char *command, int *twokeys)
  659 {
  660         int             count;
  661         int             j;
  662         int             l;
  663         char    *b;
  664 
  665         twokeys[0] = twokeys[1] = -1;
  666         l = strlen(command);
  667         count = 0;
  668 
  669         for (j=0 ; j<256 ; j++)
  670         {
  671                 b = keybindings[j];
  672                 if (!b)
  673                         continue;
  674                 if (!strncmp (b, command, l) )
  675                 {
  676                         twokeys[count] = j;
  677                         count++;
  678                         if (count == 2)
  679                                 break;
  680                 }
  681         }
  682 }
  683 
  684 static void KeyCursorDrawFunc( menuframework_s *menu )
  685 {
  686         if ( bind_grab )
  687                 re.DrawChar( menu->x, menu->y + menu->cursor * 9, '=' );
  688         else
  689                 re.DrawChar( menu->x, menu->y + menu->cursor * 9, 12 + ( ( int ) ( Sys_Milliseconds() / 250 ) & 1 ) );
  690 }
  691 
  692 static void DrawKeyBindingFunc( void *self )
  693 {
  694         int keys[2];
  695         menuaction_s *a = ( menuaction_s * ) self;
  696 
  697         M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys);
  698                 
  699         if (keys[0] == -1)
  700         {
  701                 Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, "???" );
  702         }
  703         else
  704         {
  705                 int x;
  706                 const char *name;
  707 
  708                 name = Key_KeynumToString (keys[0]);
  709 
  710                 Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, name );
  711 
  712                 x = strlen(name) * 8;
  713 
  714                 if (keys[1] != -1)
  715                 {
  716                         Menu_DrawString( a->generic.x + a->generic.parent->x + 24 + x, a->generic.y + a->generic.parent->y, "or" );
  717                         Menu_DrawString( a->generic.x + a->generic.parent->x + 48 + x, a->generic.y + a->generic.parent->y, Key_KeynumToString (keys[1]) );
  718                 }
  719         }
  720 }
  721 
  722 static void KeyBindingFunc( void *self )
  723 {
  724         menuaction_s *a = ( menuaction_s * ) self;
  725         int keys[2];
  726 
  727         M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys );
  728 
  729         if (keys[1] != -1)
  730                 M_UnbindCommand( bindnames[a->generic.localdata[0]][0]);
  731 
  732         bind_grab = true;
  733 
  734         Menu_SetStatusBar( &s_keys_menu, "press a key or button for this action" );
  735 }
  736 
  737 static void Keys_MenuInit( void )
  738 {
  739         int y = 0;
  740         int i = 0;
  741 
  742         s_keys_menu.x = viddef.width * 0.50;
  743         s_keys_menu.nitems = 0;
  744         s_keys_menu.cursordraw = KeyCursorDrawFunc;
  745 
  746         s_keys_attack_action.generic.type       = MTYPE_ACTION;
  747         s_keys_attack_action.generic.flags  = QMF_GRAYED;
  748         s_keys_attack_action.generic.x          = 0;
  749         s_keys_attack_action.generic.y          = y;
  750         s_keys_attack_action.generic.ownerdraw = DrawKeyBindingFunc;
  751         s_keys_attack_action.generic.localdata[0] = i;
  752         s_keys_attack_action.generic.name       = bindnames[s_keys_attack_action.generic.localdata[0]][1];
  753 
  754         s_keys_change_weapon_action.generic.type        = MTYPE_ACTION;
  755         s_keys_change_weapon_action.generic.flags  = QMF_GRAYED;
  756         s_keys_change_weapon_action.generic.x           = 0;
  757         s_keys_change_weapon_action.generic.y           = y += 9;
  758         s_keys_change_weapon_action.generic.ownerdraw = DrawKeyBindingFunc;
  759         s_keys_change_weapon_action.generic.localdata[0] = ++i;
  760         s_keys_change_weapon_action.generic.name        = bindnames[s_keys_change_weapon_action.generic.localdata[0]][1];
  761 
  762         s_keys_walk_forward_action.generic.type = MTYPE_ACTION;
  763         s_keys_walk_forward_action.generic.flags  = QMF_GRAYED;
  764         s_keys_walk_forward_action.generic.x            = 0;
  765         s_keys_walk_forward_action.generic.y            = y += 9;
  766         s_keys_walk_forward_action.generic.ownerdraw = DrawKeyBindingFunc;
  767         s_keys_walk_forward_action.generic.localdata[0] = ++i;
  768         s_keys_walk_forward_action.generic.name = bindnames[s_keys_walk_forward_action.generic.localdata[0]][1];
  769 
  770         s_keys_backpedal_action.generic.type    = MTYPE_ACTION;
  771         s_keys_backpedal_action.generic.flags  = QMF_GRAYED;
  772         s_keys_backpedal_action.generic.x               = 0;
  773         s_keys_backpedal_action.generic.y               = y += 9;
  774         s_keys_backpedal_action.generic.ownerdraw = DrawKeyBindingFunc;
  775         s_keys_backpedal_action.generic.localdata[0] = ++i;
  776         s_keys_backpedal_action.generic.name    = bindnames[s_keys_backpedal_action.generic.localdata[0]][1];
  777 
  778         s_keys_turn_left_action.generic.type    = MTYPE_ACTION;
  779         s_keys_turn_left_action.generic.flags  = QMF_GRAYED;
  780         s_keys_turn_left_action.generic.x               = 0;
  781         s_keys_turn_left_action.generic.y               = y += 9;
  782         s_keys_turn_left_action.generic.ownerdraw = DrawKeyBindingFunc;
  783         s_keys_turn_left_action.generic.localdata[0] = ++i;
  784         s_keys_turn_left_action.generic.name    = bindnames[s_keys_turn_left_action.generic.localdata[0]][1];
  785 
  786         s_keys_turn_right_action.generic.type   = MTYPE_ACTION;
  787         s_keys_turn_right_action.generic.flags  = QMF_GRAYED;
  788         s_keys_turn_right_action.generic.x              = 0;
  789         s_keys_turn_right_action.generic.y              = y += 9;
  790         s_keys_turn_right_action.generic.ownerdraw = DrawKeyBindingFunc;
  791         s_keys_turn_right_action.generic.localdata[0] = ++i;
  792         s_keys_turn_right_action.generic.name   = bindnames[s_keys_turn_right_action.generic.localdata[0]][1];
  793 
  794         s_keys_run_action.generic.type  = MTYPE_ACTION;
  795         s_keys_run_action.generic.flags  = QMF_GRAYED;
  796         s_keys_run_action.generic.x             = 0;
  797         s_keys_run_action.generic.y             = y += 9;
  798         s_keys_run_action.generic.ownerdraw = DrawKeyBindingFunc;
  799         s_keys_run_action.generic.localdata[0] = ++i;
  800         s_keys_run_action.generic.name  = bindnames[s_keys_run_action.generic.localdata[0]][1];
  801 
  802         s_keys_step_left_action.generic.type    = MTYPE_ACTION;
  803         s_keys_step_left_action.generic.flags  = QMF_GRAYED;
  804         s_keys_step_left_action.generic.x               = 0;
  805         s_keys_step_left_action.generic.y               = y += 9;
  806         s_keys_step_left_action.generic.ownerdraw = DrawKeyBindingFunc;
  807         s_keys_step_left_action.generic.localdata[0] = ++i;
  808         s_keys_step_left_action.generic.name    = bindnames[s_keys_step_left_action.generic.localdata[0]][1];
  809 
  810         s_keys_step_right_action.generic.type   = MTYPE_ACTION;
  811         s_keys_step_right_action.generic.flags  = QMF_GRAYED;
  812         s_keys_step_right_action.generic.x              = 0;
  813         s_keys_step_right_action.generic.y              = y += 9;
  814         s_keys_step_right_action.generic.ownerdraw = DrawKeyBindingFunc;
  815         s_keys_step_right_action.generic.localdata[0] = ++i;
  816         s_keys_step_right_action.generic.name   = bindnames[s_keys_step_right_action.generic.localdata[0]][1];
  817 
  818         s_keys_sidestep_action.generic.type     = MTYPE_ACTION;
  819         s_keys_sidestep_action.generic.flags  = QMF_GRAYED;
  820         s_keys_sidestep_action.generic.x                = 0;
  821         s_keys_sidestep_action.generic.y                = y += 9;
  822         s_keys_sidestep_action.generic.ownerdraw = DrawKeyBindingFunc;
  823         s_keys_sidestep_action.generic.localdata[0] = ++i;
  824         s_keys_sidestep_action.generic.name     = bindnames[s_keys_sidestep_action.generic.localdata[0]][1];
  825 
  826         s_keys_look_up_action.generic.type      = MTYPE_ACTION;
  827         s_keys_look_up_action.generic.flags  = QMF_GRAYED;
  828         s_keys_look_up_action.generic.x         = 0;
  829         s_keys_look_up_action.generic.y         = y += 9;
  830         s_keys_look_up_action.generic.ownerdraw = DrawKeyBindingFunc;
  831         s_keys_look_up_action.generic.localdata[0] = ++i;
  832         s_keys_look_up_action.generic.name      = bindnames[s_keys_look_up_action.generic.localdata[0]][1];
  833 
  834         s_keys_look_down_action.generic.type    = MTYPE_ACTION;
  835         s_keys_look_down_action.generic.flags  = QMF_GRAYED;
  836         s_keys_look_down_action.generic.x               = 0;
  837         s_keys_look_down_action.generic.y               = y += 9;
  838         s_keys_look_down_action.generic.ownerdraw = DrawKeyBindingFunc;
  839         s_keys_look_down_action.generic.localdata[0] = ++i;
  840         s_keys_look_down_action.generic.name    = bindnames[s_keys_look_down_action.generic.localdata[0]][1];
  841 
  842         s_keys_center_view_action.generic.type  = MTYPE_ACTION;
  843         s_keys_center_view_action.generic.flags  = QMF_GRAYED;
  844         s_keys_center_view_action.generic.x             = 0;
  845         s_keys_center_view_action.generic.y             = y += 9;
  846         s_keys_center_view_action.generic.ownerdraw = DrawKeyBindingFunc;
  847         s_keys_center_view_action.generic.localdata[0] = ++i;
  848         s_keys_center_view_action.generic.name  = bindnames[s_keys_center_view_action.generic.localdata[0]][1];
  849 
  850         s_keys_mouse_look_action.generic.type   = MTYPE_ACTION;
  851         s_keys_mouse_look_action.generic.flags  = QMF_GRAYED;
  852         s_keys_mouse_look_action.generic.x              = 0;
  853         s_keys_mouse_look_action.generic.y              = y += 9;
  854         s_keys_mouse_look_action.generic.ownerdraw = DrawKeyBindingFunc;
  855         s_keys_mouse_look_action.generic.localdata[0] = ++i;
  856         s_keys_mouse_look_action.generic.name   = bindnames[s_keys_mouse_look_action.generic.localdata[0]][1];
  857 
  858         s_keys_keyboard_look_action.generic.type        = MTYPE_ACTION;
  859         s_keys_keyboard_look_action.generic.flags  = QMF_GRAYED;
  860         s_keys_keyboard_look_action.generic.x           = 0;
  861         s_keys_keyboard_look_action.generic.y           = y += 9;
  862         s_keys_keyboard_look_action.generic.ownerdraw = DrawKeyBindingFunc;
  863         s_keys_keyboard_look_action.generic.localdata[0] = ++i;
  864         s_keys_keyboard_look_action.generic.name        = bindnames[s_keys_keyboard_look_action.generic.localdata[0]][1];
  865 
  866         s_keys_move_up_action.generic.type      = MTYPE_ACTION;
  867         s_keys_move_up_action.generic.flags  = QMF_GRAYED;
  868         s_keys_move_up_action.generic.x         = 0;
  869         s_keys_move_up_action.generic.y         = y += 9;
  870         s_keys_move_up_action.generic.ownerdraw = DrawKeyBindingFunc;
  871         s_keys_move_up_action.generic.localdata[0] = ++i;
  872         s_keys_move_up_action.generic.name      = bindnames[s_keys_move_up_action.generic.localdata[0]][1];
  873 
  874         s_keys_move_down_action.generic.type    = MTYPE_ACTION;
  875         s_keys_move_down_action.generic.flags  = QMF_GRAYED;
  876         s_keys_move_down_action.generic.x               = 0;
  877         s_keys_move_down_action.generic.y               = y += 9;
  878         s_keys_move_down_action.generic.ownerdraw = DrawKeyBindingFunc;
  879         s_keys_move_down_action.generic.localdata[0] = ++i;
  880         s_keys_move_down_action.generic.name    = bindnames[s_keys_move_down_action.generic.localdata[0]][1];
  881 
  882         s_keys_inventory_action.generic.type    = MTYPE_ACTION;
  883         s_keys_inventory_action.generic.flags  = QMF_GRAYED;
  884         s_keys_inventory_action.generic.x               = 0;
  885         s_keys_inventory_action.generic.y               = y += 9;
  886         s_keys_inventory_action.generic.ownerdraw = DrawKeyBindingFunc;
  887         s_keys_inventory_action.generic.localdata[0] = ++i;
  888         s_keys_inventory_action.generic.name    = bindnames[s_keys_inventory_action.generic.localdata[0]][1];
  889 
  890         s_keys_inv_use_action.generic.type      = MTYPE_ACTION;
  891         s_keys_inv_use_action.generic.flags  = QMF_GRAYED;
  892         s_keys_inv_use_action.generic.x         = 0;
  893         s_keys_inv_use_action.generic.y         = y += 9;
  894         s_keys_inv_use_action.generic.ownerdraw = DrawKeyBindingFunc;
  895         s_keys_inv_use_action.generic.localdata[0] = ++i;
  896         s_keys_inv_use_action.generic.name      = bindnames[s_keys_inv_use_action.generic.localdata[0]][1];
  897 
  898         s_keys_inv_drop_action.generic.type     = MTYPE_ACTION;
  899         s_keys_inv_drop_action.generic.flags  = QMF_GRAYED;
  900         s_keys_inv_drop_action.generic.x                = 0;
  901         s_keys_inv_drop_action.generic.y                = y += 9;
  902         s_keys_inv_drop_action.generic.ownerdraw = DrawKeyBindingFunc;
  903         s_keys_inv_drop_action.generic.localdata[0] = ++i;
  904         s_keys_inv_drop_action.generic.name     = bindnames[s_keys_inv_drop_action.generic.localdata[0]][1];
  905 
  906         s_keys_inv_prev_action.generic.type     = MTYPE_ACTION;
  907         s_keys_inv_prev_action.generic.flags  = QMF_GRAYED;
  908         s_keys_inv_prev_action.generic.x                = 0;
  909         s_keys_inv_prev_action.generic.y                = y += 9;
  910         s_keys_inv_prev_action.generic.ownerdraw = DrawKeyBindingFunc;
  911         s_keys_inv_prev_action.generic.localdata[0] = ++i;
  912         s_keys_inv_prev_action.generic.name     = bindnames[s_keys_inv_prev_action.generic.localdata[0]][1];
  913 
  914         s_keys_inv_next_action.generic.type     = MTYPE_ACTION;
  915         s_keys_inv_next_action.generic.flags  = QMF_GRAYED;
  916         s_keys_inv_next_action.generic.x                = 0;
  917         s_keys_inv_next_action.generic.y                = y += 9;
  918         s_keys_inv_next_action.generic.ownerdraw = DrawKeyBindingFunc;
  919         s_keys_inv_next_action.generic.localdata[0] = ++i;
  920         s_keys_inv_next_action.generic.name     = bindnames[s_keys_inv_next_action.generic.localdata[0]][1];
  921 
  922         s_keys_help_computer_action.generic.type        = MTYPE_ACTION;
  923         s_keys_help_computer_action.generic.flags  = QMF_GRAYED;
  924         s_keys_help_computer_action.generic.x           = 0;
  925         s_keys_help_computer_action.generic.y           = y += 9;
  926         s_keys_help_computer_action.generic.ownerdraw = DrawKeyBindingFunc;
  927         s_keys_help_computer_action.generic.localdata[0] = ++i;
  928         s_keys_help_computer_action.generic.name        = bindnames[s_keys_help_computer_action.generic.localdata[0]][1];
  929 
  930         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_attack_action );
  931         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_change_weapon_action );
  932         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_walk_forward_action );
  933         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_backpedal_action );
  934         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_left_action );
  935         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_right_action );
  936         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_run_action );
  937         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_left_action );
  938         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_right_action );
  939         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_sidestep_action );
  940         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_up_action );
  941         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_down_action );
  942         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_center_view_action );
  943         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_mouse_look_action );
  944         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_keyboard_look_action );
  945         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_up_action );
  946         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_down_action );
  947 
  948         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inventory_action );
  949         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_use_action );
  950         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_drop_action );
  951         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_prev_action );
  952         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_next_action );
  953 
  954         Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_help_computer_action );
  955         
  956         Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
  957         Menu_Center( &s_keys_menu );
  958 }
  959 
  960 static void Keys_MenuDraw (void)
  961 {
  962         Menu_AdjustCursor( &s_keys_menu, 1 );
  963         Menu_Draw( &s_keys_menu );
  964 }
  965 
  966 static const char *Keys_MenuKey( int key )
  967 {
  968         menuaction_s *item = ( menuaction_s * ) Menu_ItemAtCursor( &s_keys_menu );
  969 
  970         if ( bind_grab )
  971         {       
  972                 if ( key != K_ESCAPE && key != '`' )
  973                 {
  974                         char cmd[1024];
  975 
  976                         Com_sprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item->generic.localdata[0]][0]);
  977                         Cbuf_InsertText (cmd);
  978                 }
  979                 
  980                 Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
  981                 bind_grab = false;
  982                 return menu_out_sound;
  983         }
  984 
  985         switch ( key )
  986         {
  987         case K_KP_ENTER:
  988         case K_ENTER:
  989                 KeyBindingFunc( item );
  990                 return menu_in_sound;
  991         case K_BACKSPACE:               // delete bindings
  992         case K_DEL:                             // delete bindings
  993         case K_KP_DEL:
  994                 M_UnbindCommand( bindnames[item->generic.localdata[0]][0] );
  995                 return menu_out_sound;
  996         default:
  997                 return Default_MenuKey( &s_keys_menu, key );
  998         }
  999 }
 1000 
 1001 void M_Menu_Keys_f (void)
 1002 {
 1003         Keys_MenuInit();
 1004         M_PushMenu( Keys_MenuDraw, Keys_MenuKey );
 1005 }
 1006 
 1007 
 1008 /*
 1009 =======================================================================
 1010 
 1011 CONTROLS MENU
 1012 
 1013 =======================================================================
 1014 */
 1015 static cvar_t *win_noalttab;
 1016 extern cvar_t *in_joystick;
 1017 
 1018 static menuframework_s  s_options_menu;
 1019 static menuaction_s             s_options_defaults_action;
 1020 static menuaction_s             s_options_customize_options_action;
 1021 static menuslider_s             s_options_sensitivity_slider;
 1022 static menulist_s               s_options_freelook_box;
 1023 static menulist_s               s_options_noalttab_box;
 1024 static menulist_s               s_options_alwaysrun_box;
 1025 static menulist_s               s_options_invertmouse_box;
 1026 static menulist_s               s_options_lookspring_box;
 1027 static menulist_s               s_options_lookstrafe_box;
 1028 static menulist_s               s_options_crosshair_box;
 1029 static menuslider_s             s_options_sfxvolume_slider;
 1030 static menulist_s               s_options_joystick_box;
 1031 static menulist_s               s_options_cdvolume_box;
 1032 static menulist_s               s_options_quality_list;
 1033 static menulist_s               s_options_compatibility_list;
 1034 static menulist_s               s_options_console_action;
 1035 
 1036 static void CrosshairFunc( void *unused )
 1037 {
 1038         Cvar_SetValue( "crosshair", s_options_crosshair_box.curvalue );
 1039 }
 1040 
 1041 static void JoystickFunc( void *unused )
 1042 {
 1043         Cvar_SetValue( "in_joystick", s_options_joystick_box.curvalue );
 1044 }
 1045 
 1046 static void CustomizeControlsFunc( void *unused )
 1047 {
 1048         M_Menu_Keys_f();
 1049 }
 1050 
 1051 static void AlwaysRunFunc( void *unused )
 1052 {
 1053         Cvar_SetValue( "cl_run", s_options_alwaysrun_box.curvalue );
 1054 }
 1055 
 1056 static void FreeLookFunc( void *unused )
 1057 {
 1058         Cvar_SetValue( "freelook", s_options_freelook_box.curvalue );
 1059 }
 1060 
 1061 static void MouseSpeedFunc( void *unused )
 1062 {
 1063         Cvar_SetValue( "sensitivity", s_options_sensitivity_slider.curvalue / 2.0F );
 1064 }
 1065 
 1066 static void NoAltTabFunc( void *unused )
 1067 {
 1068         Cvar_SetValue( "win_noalttab", s_options_noalttab_box.curvalue );
 1069 }
 1070 
 1071 static float ClampCvar( float min, float max, float value )
 1072 {
 1073         if ( value < min ) return min;
 1074         if ( value > max ) return max;
 1075         return value;
 1076 }
 1077 
 1078 static void ControlsSetMenuItemValues( void )
 1079 {
 1080         s_options_sfxvolume_slider.curvalue             = Cvar_VariableValue( "s_volume" ) * 10;
 1081         s_options_cdvolume_box.curvalue                 = !Cvar_VariableValue("cd_nocd");
 1082         s_options_quality_list.curvalue                 = !Cvar_VariableValue( "s_loadas8bit" );
 1083         s_options_sensitivity_slider.curvalue   = ( sensitivity->value ) * 2;
 1084 
 1085         Cvar_SetValue( "cl_run", ClampCvar( 0, 1, cl_run->value ) );
 1086         s_options_alwaysrun_box.curvalue                = cl_run->value;
 1087 
 1088         s_options_invertmouse_box.curvalue              = m_pitch->value < 0;
 1089 
 1090         Cvar_SetValue( "lookspring", ClampCvar( 0, 1, lookspring->value ) );
 1091         s_options_lookspring_box.curvalue               = lookspring->value;
 1092 
 1093         Cvar_SetValue( "lookstrafe", ClampCvar( 0, 1, lookstrafe->value ) );
 1094         s_options_lookstrafe_box.curvalue               = lookstrafe->value;
 1095 
 1096         Cvar_SetValue( "freelook", ClampCvar( 0, 1, freelook->value ) );
 1097         s_options_freelook_box.curvalue                 = freelook->value;
 1098 
 1099         Cvar_SetValue( "crosshair", ClampCvar( 0, 3, crosshair->value ) );
 1100         s_options_crosshair_box.curvalue                = crosshair->value;
 1101 
 1102         Cvar_SetValue( "in_joystick", ClampCvar( 0, 1, in_joystick->value ) );
 1103         s_options_joystick_box.curvalue         = in_joystick->value;
 1104 
 1105         s_options_noalttab_box.curvalue                 = win_noalttab->value;
 1106 }
 1107 
 1108 static void ControlsResetDefaultsFunc( void *unused )
 1109 {
 1110         Cbuf_AddText ("exec default.cfg\n");
 1111         Cbuf_Execute();
 1112 
 1113         ControlsSetMenuItemValues();
 1114 }
 1115 
 1116 static void InvertMouseFunc( void *unused )
 1117 {
 1118         Cvar_SetValue( "m_pitch", -m_pitch->value );
 1119 }
 1120 
 1121 static void LookspringFunc( void *unused )
 1122 {
 1123         Cvar_SetValue( "lookspring", !lookspring->value );
 1124 }
 1125 
 1126 static void LookstrafeFunc( void *unused )
 1127 {
 1128         Cvar_SetValue( "lookstrafe", !lookstrafe->value );
 1129 }
 1130 
 1131 static void UpdateVolumeFunc( void *unused )
 1132 {
 1133         Cvar_SetValue( "s_volume", s_options_sfxvolume_slider.curvalue / 10 );
 1134 }
 1135 
 1136 static void UpdateCDVolumeFunc( void *unused )
 1137 {
 1138         Cvar_SetValue( "cd_nocd", !s_options_cdvolume_box.curvalue );
 1139 }
 1140 
 1141 static void ConsoleFunc( void *unused )
 1142 {
 1143         /*
 1144         ** the proper way to do this is probably to have ToggleConsole_f accept a parameter
 1145         */
 1146         extern void Key_ClearTyping( void );
 1147 
 1148         if ( cl.attractloop )
 1149         {
 1150                 Cbuf_AddText ("killserver\n");
 1151                 return;
 1152         }
 1153 
 1154         Key_ClearTyping ();
 1155         Con_ClearNotify ();
 1156 
 1157         M_ForceMenuOff ();
 1158         cls.key_dest = key_console;
 1159 }
 1160 
 1161 static void UpdateSoundQualityFunc( void *unused )
 1162 {
 1163         if ( s_options_quality_list.curvalue )
 1164         {
 1165                 Cvar_SetValue( "s_khz", 22 );
 1166                 Cvar_SetValue( "s_loadas8bit", false );
 1167         }
 1168         else
 1169         {
 1170                 Cvar_SetValue( "s_khz", 11 );
 1171                 Cvar_SetValue( "s_loadas8bit", true );
 1172         }
 1173         
 1174         Cvar_SetValue( "s_primary", s_options_compatibility_list.curvalue );
 1175 
 1176         M_DrawTextBox( 8, 120 - 48, 36, 3 );
 1177         M_Print( 16 + 16, 120 - 48 + 8,  "Restarting the sound system. This" );
 1178         M_Print( 16 + 16, 120 - 48 + 16, "could take up to a minute, so" );
 1179         M_Print( 16 + 16, 120 - 48 + 24, "please be patient." );
 1180 
 1181         // the text box won't show up unless we do a buffer swap
 1182         re.EndFrame();
 1183 
 1184         CL_Snd_Restart_f();
 1185 }
 1186 
 1187 void Options_MenuInit( void )
 1188 {
 1189         static const char *cd_music_items[] =
 1190         {
 1191                 "disabled",
 1192                 "enabled",
 1193                 0
 1194         };
 1195         static const char *quality_items[] =
 1196         {
 1197                 "low", "high", 0
 1198         };
 1199 
 1200         static const char *compatibility_items[] =
 1201         {
 1202                 "max compatibility", "max performance", 0
 1203         };
 1204 
 1205         static const char *yesno_names[] =
 1206         {
 1207                 "no",
 1208                 "yes",
 1209                 0
 1210         };
 1211 
 1212         static const char *crosshair_names[] =
 1213         {
 1214                 "none",
 1215                 "cross",
 1216                 "dot",
 1217                 "angle",
 1218                 0
 1219         };
 1220 
 1221         win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE );
 1222 
 1223         /*
 1224         ** configure controls menu and menu items
 1225         */
 1226         s_options_menu.x = viddef.width / 2;
 1227         s_options_menu.y = viddef.height / 2 - 58;
 1228         s_options_menu.nitems = 0;
 1229 
 1230         s_options_sfxvolume_slider.generic.type = MTYPE_SLIDER;
 1231         s_options_sfxvolume_slider.generic.x    = 0;
 1232         s_options_sfxvolume_slider.generic.y    = 0;
 1233         s_options_sfxvolume_slider.generic.name = "effects volume";
 1234         s_options_sfxvolume_slider.generic.callback     = UpdateVolumeFunc;
 1235         s_options_sfxvolume_slider.minvalue             = 0;
 1236         s_options_sfxvolume_slider.maxvalue             = 10;
 1237         s_options_sfxvolume_slider.curvalue             = Cvar_VariableValue( "s_volume" ) * 10;
 1238 
 1239         s_options_cdvolume_box.generic.type     = MTYPE_SPINCONTROL;
 1240         s_options_cdvolume_box.generic.x                = 0;
 1241         s_options_cdvolume_box.generic.y                = 10;
 1242         s_options_cdvolume_box.generic.name     = "CD music";
 1243         s_options_cdvolume_box.generic.callback = UpdateCDVolumeFunc;
 1244         s_options_cdvolume_box.itemnames                = cd_music_items;
 1245         s_options_cdvolume_box.curvalue                 = !Cvar_VariableValue("cd_nocd");
 1246 
 1247         s_options_quality_list.generic.type     = MTYPE_SPINCONTROL;
 1248         s_options_quality_list.generic.x                = 0;
 1249         s_options_quality_list.generic.y                = 20;;
 1250         s_options_quality_list.generic.name             = "sound quality";
 1251         s_options_quality_list.generic.callback = UpdateSoundQualityFunc;
 1252         s_options_quality_list.itemnames                = quality_items;
 1253         s_options_quality_list.curvalue                 = !Cvar_VariableValue( "s_loadas8bit" );
 1254 
 1255         s_options_compatibility_list.generic.type       = MTYPE_SPINCONTROL;
 1256         s_options_compatibility_list.generic.x          = 0;
 1257         s_options_compatibility_list.generic.y          = 30;
 1258         s_options_compatibility_list.generic.name       = "sound compatibility";
 1259         s_options_compatibility_list.generic.callback = UpdateSoundQualityFunc;
 1260         s_options_compatibility_list.itemnames          = compatibility_items;
 1261         s_options_compatibility_list.curvalue           = Cvar_VariableValue( "s_primary" );
 1262 
 1263         s_options_sensitivity_slider.generic.type       = MTYPE_SLIDER;
 1264         s_options_sensitivity_slider.generic.x          = 0;
 1265         s_options_sensitivity_slider.generic.y          = 50;
 1266         s_options_sensitivity_slider.generic.name       = "mouse speed";
 1267         s_options_sensitivity_slider.generic.callback = MouseSpeedFunc;
 1268         s_options_sensitivity_slider.minvalue           = 2;
 1269         s_options_sensitivity_slider.maxvalue           = 22;
 1270 
 1271         s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL;
 1272         s_options_alwaysrun_box.generic.x       = 0;
 1273         s_options_alwaysrun_box.generic.y       = 60;
 1274         s_options_alwaysrun_box.generic.name    = "always run";
 1275         s_options_alwaysrun_box.generic.callback = AlwaysRunFunc;
 1276         s_options_alwaysrun_box.itemnames = yesno_names;
 1277 
 1278         s_options_invertmouse_box.generic.type = MTYPE_SPINCONTROL;
 1279         s_options_invertmouse_box.generic.x     = 0;
 1280         s_options_invertmouse_box.generic.y     = 70;
 1281         s_options_invertmouse_box.generic.name  = "invert mouse";
 1282         s_options_invertmouse_box.generic.callback = InvertMouseFunc;
 1283         s_options_invertmouse_box.itemnames = yesno_names;
 1284 
 1285         s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL;
 1286         s_options_lookspring_box.generic.x      = 0;
 1287         s_options_lookspring_box.generic.y      = 80;
 1288         s_options_lookspring_box.generic.name   = "lookspring";
 1289         s_options_lookspring_box.generic.callback = LookspringFunc;
 1290         s_options_lookspring_box.itemnames = yesno_names;
 1291 
 1292         s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL;
 1293         s_options_lookstrafe_box.generic.x      = 0;
 1294         s_options_lookstrafe_box.generic.y      = 90;
 1295         s_options_lookstrafe_box.generic.name   = "lookstrafe";
 1296         s_options_lookstrafe_box.generic.callback = LookstrafeFunc;
 1297         s_options_lookstrafe_box.itemnames = yesno_names;
 1298 
 1299         s_options_freelook_box.generic.type = MTYPE_SPINCONTROL;
 1300         s_options_freelook_box.generic.x        = 0;
 1301         s_options_freelook_box.generic.y        = 100;
 1302         s_options_freelook_box.generic.name     = "free look";
 1303         s_options_freelook_box.generic.callback = FreeLookFunc;
 1304         s_options_freelook_box.itemnames = yesno_names;
 1305 
 1306         s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL;
 1307         s_options_crosshair_box.generic.x       = 0;
 1308         s_options_crosshair_box.generic.y       = 110;
 1309         s_options_crosshair_box.generic.name    = "crosshair";
 1310         s_options_crosshair_box.generic.callback = CrosshairFunc;
 1311         s_options_crosshair_box.itemnames = crosshair_names;
 1312 /*
 1313         s_options_noalttab_box.generic.type = MTYPE_SPINCONTROL;
 1314         s_options_noalttab_box.generic.x        = 0;
 1315         s_options_noalttab_box.generic.y        = 110;
 1316         s_options_noalttab_box.generic.name     = "disable alt-tab";
 1317         s_options_noalttab_box.generic.callback = NoAltTabFunc;
 1318         s_options_noalttab_box.itemnames = yesno_names;
 1319 */
 1320         s_options_joystick_box.generic.type = MTYPE_SPINCONTROL;
 1321         s_options_joystick_box.generic.x        = 0;
 1322         s_options_joystick_box.generic.y        = 120;
 1323         s_options_joystick_box.generic.name     = "use joystick";
 1324         s_options_joystick_box.generic.callback = JoystickFunc;
 1325         s_options_joystick_box.itemnames = yesno_names;
 1326 
 1327         s_options_customize_options_action.generic.type = MTYPE_ACTION;
 1328         s_options_customize_options_action.generic.x            = 0;
 1329         s_options_customize_options_action.generic.y            = 140;
 1330         s_options_customize_options_action.generic.name = "customize controls";
 1331         s_options_customize_options_action.generic.callback = CustomizeControlsFunc;
 1332 
 1333         s_options_defaults_action.generic.type  = MTYPE_ACTION;
 1334         s_options_defaults_action.generic.x             = 0;
 1335         s_options_defaults_action.generic.y             = 150;
 1336         s_options_defaults_action.generic.name  = "reset defaults";
 1337         s_options_defaults_action.generic.callback = ControlsResetDefaultsFunc;
 1338 
 1339         s_options_console_action.generic.type   = MTYPE_ACTION;
 1340         s_options_console_action.generic.x              = 0;
 1341         s_options_console_action.generic.y              = 160;
 1342         s_options_console_action.generic.name   = "go to console";
 1343         s_options_console_action.generic.callback = ConsoleFunc;
 1344 
 1345         ControlsSetMenuItemValues();
 1346 
 1347         Menu_AddItem( &s_options_menu, ( void * ) &s_options_sfxvolume_slider );
 1348         Menu_AddItem( &s_options_menu, ( void * ) &s_options_cdvolume_box );
 1349         Menu_AddItem( &s_options_menu, ( void * ) &s_options_quality_list );
 1350         Menu_AddItem( &s_options_menu, ( void * ) &s_options_compatibility_list );
 1351         Menu_AddItem( &s_options_menu, ( void * ) &s_options_sensitivity_slider );
 1352         Menu_AddItem( &s_options_menu, ( void * ) &s_options_alwaysrun_box );
 1353         Menu_AddItem( &s_options_menu, ( void * ) &s_options_invertmouse_box );
 1354         Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookspring_box );
 1355         Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookstrafe_box );
 1356         Menu_AddItem( &s_options_menu, ( void * ) &s_options_freelook_box );
 1357         Menu_AddItem( &s_options_menu, ( void * ) &s_options_crosshair_box );
 1358         Menu_AddItem( &s_options_menu, ( void * ) &s_options_joystick_box );
 1359         Menu_AddItem( &s_options_menu, ( void * ) &s_options_customize_options_action );
 1360         Menu_AddItem( &s_options_menu, ( void * ) &s_options_defaults_action );
 1361         Menu_AddItem( &s_options_menu, ( void * ) &s_options_console_action );
 1362 }
 1363 
 1364 void Options_MenuDraw (void)
 1365 {
 1366         M_Banner( "m_banner_options" );
 1367         Menu_AdjustCursor( &s_options_menu, 1 );
 1368         Menu_Draw( &s_options_menu );
 1369 }
 1370 
 1371 const char *Options_MenuKey( int key )
 1372 {
 1373         return Default_MenuKey( &s_options_menu, key );
 1374 }
 1375 
 1376 void M_Menu_Options_f (void)
 1377 {
 1378         Options_MenuInit();
 1379         M_PushMenu ( Options_MenuDraw, Options_MenuKey );
 1380 }
 1381 
 1382 /*
 1383 =======================================================================
 1384 
 1385 VIDEO MENU
 1386 
 1387 =======================================================================
 1388 */
 1389 
 1390 void M_Menu_Video_f (void)
 1391 {
 1392         VID_MenuInit();
 1393         M_PushMenu( VID_MenuDraw, VID_MenuKey );
 1394 }
 1395 
 1396 /*
 1397 =============================================================================
 1398 
 1399 END GAME MENU
 1400 
 1401 =============================================================================
 1402 */
 1403 static int credits_start_time;
 1404 static const char **credits;
 1405 static char *creditsIndex[256];
 1406 static char *creditsBuffer;
 1407 static const char *idcredits[] =
 1408 {
 1409         "+QUAKE II BY ID SOFTWARE",
 1410         "",
 1411         "+PROGRAMMING",
 1412         "John Carmack",
 1413         "John Cash",
 1414         "Brian Hook",
 1415         "",
 1416         "+ART",
 1417         "Adrian Carmack",
 1418         "Kevin Cloud",
 1419         "Paul Steed",
 1420         "",
 1421         "+LEVEL DESIGN",
 1422         "Tim Willits",
 1423         "American McGee",
 1424         "Christian Antkow",
 1425         "Paul Jaquays",
 1426         "Brandon James",
 1427         "",
 1428         "+BIZ",
 1429         "Todd Hollenshead",
 1430         "Barrett (Bear) Alexander",
 1431         "Donna Jackson",
 1432         "",
 1433         "",
 1434         "+SPECIAL THANKS",
 1435         "Ben Donges for beta testing",
 1436         "",
 1437         "",
 1438         "",
 1439         "",
 1440         "",
 1441         "",
 1442         "+ADDITIONAL SUPPORT",
 1443         "",
 1444         "+LINUX PORT AND CTF",
 1445         "Dave \"Zoid\" Kirsch",
 1446         "",
 1447         "+CINEMATIC SEQUENCES",
 1448         "Ending Cinematic by Blur Studio - ",
 1449         "Venice, CA",
 1450         "",
 1451         "Environment models for Introduction",
 1452         "Cinematic by Karl Dolgener",
 1453         "",
 1454         "Assistance with environment design",
 1455         "by Cliff Iwai",
 1456         "",
 1457         "+SOUND EFFECTS AND MUSIC",
 1458         "Sound Design by Soundelux Media Labs.",
 1459         "Music Composed and Produced by",
 1460         "Soundelux Media Labs.  Special thanks",
 1461         "to Bill Brown, Tom Ozanich, Brian",
 1462         "Celano, Jeff Eisner, and The Soundelux",
 1463         "Players.",
 1464         "",
 1465         "\"Level Music\" by Sonic Mayhem",
 1466         "www.sonicmayhem.com",
 1467         "",
 1468         "\"Quake II Theme Song\"",
 1469         "(C) 1997 Rob Zombie. All Rights",
 1470         "Reserved.",
 1471         "",
 1472         "Track 10 (\"Climb\") by Jer Sypult",
 1473         "",
 1474         "Voice of computers by",
 1475         "Carly Staehlin-Taylor",
 1476         "",
 1477         "+THANKS TO ACTIVISION",
 1478         "+IN PARTICULAR:",
 1479         "",
 1480         "John Tam",
 1481         "Steve Rosenthal",
 1482         "Marty Stratton",
 1483         "Henk Hartong",
 1484         "",
 1485         "Quake II(tm) (C)1997 Id Software, Inc.",
 1486         "All Rights Reserved.  Distributed by",
 1487         "Activision, Inc. under license.",
 1488         "Quake II(tm), the Id Software name,",
 1489         "the \"Q II\"(tm) logo and id(tm)",
 1490         "logo are trademarks of Id Software,",
 1491         "Inc. Activision(R) is a registered",
 1492         "trademark of Activision, Inc. All",
 1493         "other trademarks and trade names are",
 1494         "properties of their respective owners.",
 1495         0
 1496 };
 1497 
 1498 static const char *xatcredits[] =
 1499 {
 1500         "+QUAKE II MISSION PACK: THE RECKONING",
 1501         "+BY",
 1502         "+XATRIX ENTERTAINMENT, INC.",
 1503         "",
 1504         "+DESIGN AND DIRECTION",
 1505         "Drew Markham",
 1506         "",
 1507         "+PRODUCED BY",
 1508         "Greg Goodrich",
 1509         "",
 1510         "+PROGRAMMING",
 1511         "Rafael Paiz",
 1512         "",
 1513         "+LEVEL DESIGN / ADDITIONAL GAME DESIGN",
 1514         "Alex Mayberry",
 1515         "",
 1516         "+LEVEL DESIGN",
 1517         "Mal Blackwell",
 1518         "Dan Koppel",
 1519         "",
 1520         "+ART DIRECTION",
 1521         "Michael \"Maxx\" Kaufman",
 1522         "",
 1523         "+COMPUTER GRAPHICS SUPERVISOR AND",
 1524         "+CHARACTER ANIMATION DIRECTION",
 1525         "Barry Dempsey",
 1526         "",
 1527         "+SENIOR ANIMATOR AND MODELER",
 1528         "Jason Hoover",
 1529         "",
 1530         "+CHARACTER ANIMATION AND",
 1531         "+MOTION CAPTURE SPECIALIST",
 1532         "Amit Doron",
 1533         "",
 1534         "+ART",
 1535         "Claire Praderie-Markham",
 1536         "Viktor Antonov",
 1537         "Corky Lehmkuhl",
 1538         "",
 1539         "+INTRODUCTION ANIMATION",
 1540         "Dominique Drozdz",
 1541         "",
 1542         "+ADDITIONAL LEVEL DESIGN",
 1543         "Aaron Barber",
 1544         "Rhett Baldwin",
 1545         "",
 1546         "+3D CHARACTER ANIMATION TOOLS",
 1547         "Gerry Tyra, SA Technology",
 1548         "",
 1549         "+ADDITIONAL EDITOR TOOL PROGRAMMING",
 1550         "Robert Duffy",
 1551         "",
 1552         "+ADDITIONAL PROGRAMMING",
 1553         "Ryan Feltrin",
 1554         "",
 1555         "+PRODUCTION COORDINATOR",
 1556         "Victoria Sylvester",
 1557         "",
 1558         "+SOUND DESIGN",
 1559         "Gary Bradfield",
 1560         "",
 1561         "+MUSIC BY",
 1562         "Sonic Mayhem",
 1563         "",
 1564         "",
 1565         "",
 1566         "+SPECIAL THANKS",
 1567         "+TO",
 1568         "+OUR FRIENDS AT ID SOFTWARE",
 1569         "",
 1570         "John Carmack",
 1571         "John Cash",
 1572         "Brian Hook",
 1573         "Adrian Carmack",
 1574         "Kevin Cloud",
 1575         "Paul Steed",
 1576         "Tim Willits",
 1577         "Christian Antkow",
 1578         "Paul Jaquays",
 1579         "Brandon James",
 1580         "Todd Hollenshead",
 1581         "Barrett (Bear) Alexander",
 1582         "Dave \"Zoid\" Kirsch",
 1583         "Donna Jackson",
 1584         "",
 1585         "",
 1586         "",
 1587         "+THANKS TO ACTIVISION",
 1588         "+IN PARTICULAR:",
 1589         "",
 1590         "Marty Stratton",
 1591         "Henk \"The Original Ripper\" Hartong",
 1592         "Kevin Kraff",
 1593         "Jamey Gottlieb",
 1594         "Chris Hepburn",
 1595         "",
 1596         "+AND THE GAME TESTERS",
 1597         "",
 1598         "Tim Vanlaw",
 1599         "Doug Jacobs",
 1600         "Steven Rosenthal",
 1601         "David Baker",
 1602         "Chris Campbell",
 1603         "Aaron Casillas",
 1604         "Steve Elwell",
 1605         "Derek Johnstone",
 1606         "Igor Krinitskiy",
 1607         "Samantha Lee",
 1608         "Michael Spann",
 1609         "Chris Toft",
 1610         "Juan Valdes",
 1611         "",
 1612         "+THANKS TO INTERGRAPH COMPUTER SYTEMS",
 1613         "+IN PARTICULAR:",
 1614         "",
 1615         "Michael T. Nicolaou",
 1616         "",
 1617         "",
 1618         "Quake II Mission Pack: The Reckoning",
 1619         "(tm) (C)1998 Id Software, Inc. All",
 1620         "Rights Reserved. Developed by Xatrix",
 1621         "Entertainment, Inc. for Id Software,",
 1622         "Inc. Distributed by Activision Inc.",
 1623         "under license. Quake(R) is a",
 1624         "registered trademark of Id Software,",
 1625         "Inc. Quake II Mission Pack: The",
 1626         "Reckoning(tm), Quake II(tm), the Id",
 1627         "Software name, the \"Q II\"(tm) logo",
 1628         "and id(tm) logo are trademarks of Id",
 1629         "Software, Inc. Activision(R) is a",
 1630         "registered trademark of Activision,",
 1631         "Inc. Xatrix(R) is a registered",
 1632         "trademark of Xatrix Entertainment,",
 1633         "Inc. All other trademarks and trade",
 1634         "names are properties of their",
 1635         "respective owners.",
 1636         0
 1637 };
 1638 
 1639 static const char *roguecredits[] =
 1640 {
 1641         "+QUAKE II MISSION PACK 2: GROUND ZERO",
 1642         "+BY",
 1643         "+ROGUE ENTERTAINMENT, INC.",
 1644         "",
 1645         "+PRODUCED BY",
 1646         "Jim Molinets",
 1647         "",
 1648         "+PROGRAMMING",
 1649         "Peter Mack",
 1650         "Patrick Magruder",
 1651         "",
 1652         "+LEVEL DESIGN",
 1653         "Jim Molinets",
 1654         "Cameron Lamprecht",
 1655         "Berenger Fish",
 1656         "Robert Selitto",
 1657         "Steve Tietze",
 1658         "Steve Thoms",
 1659         "",
 1660         "+ART DIRECTION",
 1661         "Rich Fleider",
 1662         "",
 1663         "+ART",
 1664         "Rich Fleider",
 1665         "Steve Maines",
 1666         "Won Choi",
 1667         "",
 1668         "+ANIMATION SEQUENCES",
 1669         "Creat Studios",
 1670         "Steve Maines",
 1671         "",
 1672         "+ADDITIONAL LEVEL DESIGN",
 1673         "Rich Fleider",
 1674         "Steve Maines",
 1675         "Peter Mack",
 1676         "",
 1677         "+SOUND",
 1678         "James Grunke",
 1679         "",
 1680         "+GROUND ZERO THEME",
 1681         "+AND",
 1682         "+MUSIC BY",
 1683         "Sonic Mayhem",
 1684         "",
 1685         "+VWEP MODELS",
 1686         "Brent \"Hentai\" Dill",
 1687         "",
 1688         "",
 1689         "",
 1690         "+SPECIAL THANKS",
 1691         "+TO",
 1692         "+OUR FRIENDS AT ID SOFTWARE",
 1693         "",
 1694         "John Carmack",
 1695         "John Cash",
 1696         "Brian Hook",
 1697         "Adrian Carmack",
 1698         "Kevin Cloud",
 1699         "Paul Steed",
 1700         "Tim Willits",
 1701         "Christian Antkow",
 1702         "Paul Jaquays",
 1703         "Brandon James",
 1704         "Todd Hollenshead",
 1705         "Barrett (Bear) Alexander",
 1706         "Katherine Anna Kang",
 1707         "Donna Jackson",
 1708         "Dave \"Zoid\" Kirsch",
 1709         "",
 1710         "",
 1711         "",
 1712         "+THANKS TO ACTIVISION",
 1713         "+IN PARTICULAR:",
 1714         "",
 1715         "Marty Stratton",
 1716         "Henk Hartong",
 1717         "Mitch Lasky",
 1718         "Steve Rosenthal",
 1719         "Steve Elwell",
 1720         "",
 1721         "+AND THE GAME TESTERS",
 1722         "",
 1723         "The Ranger Clan",
 1724         "Dave \"Zoid\" Kirsch",
 1725         "Nihilistic Software",
 1726         "Robert Duffy",
 1727         "",
 1728         "And Countless Others",
 1729         "",
 1730         "",
 1731         "",
 1732         "Quake II Mission Pack 2: Ground Zero",
 1733         "(tm) (C)1998 Id Software, Inc. All",
 1734         "Rights Reserved. Developed by Rogue",
 1735         "Entertainment, Inc. for Id Software,",
 1736         "Inc. Distributed by Activision Inc.",
 1737         "under license. Quake(R) is a",
 1738         "registered trademark of Id Software,",
 1739         "Inc. Quake II Mission Pack 2: Ground",
 1740         "Zero(tm), Quake II(tm), the Id",
 1741         "Software name, the \"Q II\"(tm) logo",
 1742         "and id(tm) logo are trademarks of Id",
 1743         "Software, Inc. Activision(R) is a",
 1744         "registered trademark of Activision,",
 1745         "Inc. Rogue(R) is a registered",
 1746         "trademark of Rogue Entertainment,",
 1747         "Inc. All other trademarks and trade",
 1748         "names are properties of their",
 1749         "respective owners.",
 1750         0
 1751 };
 1752 
 1753 
 1754 void M_Credits_MenuDraw( void )
 1755 {
 1756         int i, y;
 1757 
 1758         /*
 1759         ** draw the credits
 1760         */
 1761         for ( i = 0, y = viddef.height - ( ( cls.realtime - credits_start_time ) / 40.0F ); credits[i] && y < viddef.height; y += 10, i++ )
 1762         {
 1763                 int j, stringoffset = 0;
 1764                 int bold = false;
 1765 
 1766                 if ( y <= -8 )
 1767                         continue;
 1768 
 1769                 if ( credits[i][0] == '+' )
 1770                 {
 1771                         bold = true;
 1772                         stringoffset = 1;
 1773                 }
 1774                 else
 1775                 {
 1776                         bold = false;
 1777                         stringoffset = 0;
 1778                 }
 1779 
 1780                 for ( j = 0; credits[i][j+stringoffset]; j++ )
 1781                 {
 1782                         int x;
 1783 
 1784                         x = ( viddef.width - strlen( credits[i] ) * 8 - stringoffset * 8 ) / 2 + ( j + stringoffset ) * 8;
 1785 
 1786                         if ( bold )
 1787                                 re.DrawChar( x, y, credits[i][j+stringoffset] + 128 );
 1788                         else
 1789                                 re.DrawChar( x, y, credits[i][j+stringoffset] );
 1790                 }
 1791         }
 1792 
 1793         if ( y < 0 )
 1794                 credits_start_time = cls.realtime;
 1795 }
 1796 
 1797 const char *M_Credits_Key( int key )
 1798 {
 1799         switch (key)
 1800         {
 1801         case K_ESCAPE:
 1802                 if (creditsBuffer)
 1803                         FS_FreeFile (creditsBuffer);
 1804                 M_PopMenu ();
 1805                 break;
 1806         }
 1807 
 1808         return menu_out_sound;
 1809 
 1810 }
 1811 
 1812 extern int Developer_searchpath (int who);
 1813 
 1814 void M_Menu_Credits_f( void )
 1815 {
 1816         int             n;
 1817         int             count;
 1818         char    *p;
 1819         int             isdeveloper = 0;
 1820 
 1821         creditsBuffer = NULL;
 1822         count = FS_LoadFile ("credits", &creditsBuffer);
 1823         if (count != -1)
 1824         {
 1825                 p = creditsBuffer;
 1826                 for (n = 0; n < 255; n++)
 1827                 {
 1828                         creditsIndex[n] = p;
 1829                         while (*p != '\r' && *p != '\n')
 1830                         {
 1831                                 p++;
 1832                                 if (--count == 0)
 1833                                         break;
 1834                         }
 1835                         if (*p == '\r')
 1836                         {
 1837                                 *p++ = 0;
 1838                                 if (--count == 0)
 1839                                         break;
 1840                         }
 1841                         *p++ = 0;
 1842                         if (--count == 0)
 1843                                 break;
 1844                 }
 1845                 creditsIndex[++n] = 0;
 1846                 credits = creditsIndex;
 1847         }
 1848         else
 1849         {
 1850                 isdeveloper = Developer_searchpath (1);
 1851                 
 1852                 if (isdeveloper == 1)                   // xatrix
 1853                         credits = xatcredits;
 1854                 else if (isdeveloper == 2)              // ROGUE
 1855                         credits = roguecredits;
 1856                 else
 1857                 {
 1858                         credits = idcredits;    
 1859                 }
 1860 
 1861         }
 1862 
 1863         credits_start_time = cls.realtime;
 1864         M_PushMenu( M_Credits_MenuDraw, M_Credits_Key);
 1865 }
 1866 
 1867 /*
 1868 =============================================================================
 1869 
 1870 GAME MENU
 1871 
 1872 =============================================================================
 1873 */
 1874 
 1875 static int              m_game_cursor;
 1876 
 1877 static menuframework_s  s_game_menu;
 1878 static menuaction_s             s_easy_game_action;
 1879 static menuaction_s             s_medium_game_action;
 1880 static menuaction_s             s_hard_game_action;
 1881 static menuaction_s             s_load_game_action;
 1882 static menuaction_s             s_save_game_action;
 1883 static menuaction_s             s_credits_action;
 1884 static menuseparator_s  s_blankline;
 1885 
 1886 static void StartGame( void )
 1887 {
 1888         // disable updates and start the cinematic going
 1889         cl.servercount = -1;
 1890         M_ForceMenuOff ();
 1891         Cvar_SetValue( "deathmatch", 0 );
 1892         Cvar_SetValue( "coop", 0 );
 1893 
 1894         Cvar_SetValue( "gamerules", 0 );                //PGM
 1895 
 1896         Cbuf_AddText ("loading ; killserver ; wait ; newgame\n");
 1897         cls.key_dest = key_game;
 1898 }
 1899 
 1900 static void EasyGameFunc( void *data )
 1901 {
 1902         Cvar_ForceSet( "skill", "0" );
 1903         StartGame();
 1904 }
 1905 
 1906 static void MediumGameFunc( void *data )
 1907 {
 1908         Cvar_ForceSet( "skill", "1" );
 1909         StartGame();
 1910 }
 1911 
 1912 static void HardGameFunc( void *data )
 1913 {
 1914         Cvar_ForceSet( "skill", "2" );
 1915         StartGame();
 1916 }
 1917 
 1918 static void LoadGameFunc( void *unused )
 1919 {
 1920         M_Menu_LoadGame_f ();
 1921 }
 1922 
 1923 static void SaveGameFunc( void *unused )
 1924 {
 1925         M_Menu_SaveGame_f();
 1926 }
 1927 
 1928 static void CreditsFunc( void *unused )
 1929 {
 1930         M_Menu_Credits_f();
 1931 }
 1932 
 1933 void Game_MenuInit( void )
 1934 {
 1935         static const char *difficulty_names[] =
 1936         {
 1937                 "easy",
 1938                 "medium",
 1939                 "hard",
 1940                 0
 1941         };
 1942 
 1943         s_game_menu.x = viddef.width * 0.50;
 1944         s_game_menu.nitems = 0;
 1945 
 1946         s_easy_game_action.generic.type = MTYPE_ACTION;
 1947         s_easy_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1948         s_easy_game_action.generic.x            = 0;
 1949         s_easy_game_action.generic.y            = 0;
 1950         s_easy_game_action.generic.name = "easy";
 1951         s_easy_game_action.generic.callback = EasyGameFunc;
 1952 
 1953         s_medium_game_action.generic.type       = MTYPE_ACTION;
 1954         s_medium_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1955         s_medium_game_action.generic.x          = 0;
 1956         s_medium_game_action.generic.y          = 10;
 1957         s_medium_game_action.generic.name       = "medium";
 1958         s_medium_game_action.generic.callback = MediumGameFunc;
 1959 
 1960         s_hard_game_action.generic.type = MTYPE_ACTION;
 1961         s_hard_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1962         s_hard_game_action.generic.x            = 0;
 1963         s_hard_game_action.generic.y            = 20;
 1964         s_hard_game_action.generic.name = "hard";
 1965         s_hard_game_action.generic.callback = HardGameFunc;
 1966 
 1967         s_blankline.generic.type = MTYPE_SEPARATOR;
 1968 
 1969         s_load_game_action.generic.type = MTYPE_ACTION;
 1970         s_load_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1971         s_load_game_action.generic.x            = 0;
 1972         s_load_game_action.generic.y            = 40;
 1973         s_load_game_action.generic.name = "load game";
 1974         s_load_game_action.generic.callback = LoadGameFunc;
 1975 
 1976         s_save_game_action.generic.type = MTYPE_ACTION;
 1977         s_save_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1978         s_save_game_action.generic.x            = 0;
 1979         s_save_game_action.generic.y            = 50;
 1980         s_save_game_action.generic.name = "save game";
 1981         s_save_game_action.generic.callback = SaveGameFunc;
 1982 
 1983         s_credits_action.generic.type   = MTYPE_ACTION;
 1984         s_credits_action.generic.flags  = QMF_LEFT_JUSTIFY;
 1985         s_credits_action.generic.x              = 0;
 1986         s_credits_action.generic.y              = 60;
 1987         s_credits_action.generic.name   = "credits";
 1988         s_credits_action.generic.callback = CreditsFunc;
 1989 
 1990         Menu_AddItem( &s_game_menu, ( void * ) &s_easy_game_action );
 1991         Menu_AddItem( &s_game_menu, ( void * ) &s_medium_game_action );
 1992         Menu_AddItem( &s_game_menu, ( void * ) &s_hard_game_action );
 1993         Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );
 1994         Menu_AddItem( &s_game_menu, ( void * ) &s_load_game_action );
 1995         Menu_AddItem( &s_game_menu, ( void * ) &s_save_game_action );
 1996         Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );
 1997         Menu_AddItem( &s_game_menu, ( void * ) &s_credits_action );
 1998 
 1999         Menu_Center( &s_game_menu );
 2000 }
 2001 
 2002 void Game_MenuDraw( void )
 2003 {
 2004         M_Banner( "m_banner_game" );
 2005         Menu_AdjustCursor( &s_game_menu, 1 );
 2006         Menu_Draw( &s_game_menu );
 2007 }
 2008 
 2009 const char *Game_MenuKey( int key )
 2010 {
 2011         return Default_MenuKey( &s_game_menu, key );
 2012 }
 2013 
 2014 void M_Menu_Game_f (void)
 2015 {
 2016         Game_MenuInit();
 2017         M_PushMenu( Game_MenuDraw, Game_MenuKey );
 2018         m_game_cursor = 1;
 2019 }
 2020 
 2021 /*
 2022 =============================================================================
 2023 
 2024 LOADGAME MENU
 2025 
 2026 =============================================================================
 2027 */
 2028 
 2029 #define MAX_SAVEGAMES   15
 2030 
 2031 static menuframework_s  s_savegame_menu;
 2032 
 2033 static menuframework_s  s_loadgame_menu;
 2034 static menuaction_s             s_loadgame_actions[MAX_SAVEGAMES];
 2035 
 2036 char            m_savestrings[MAX_SAVEGAMES][32];
 2037 qboolean        m_savevalid[MAX_SAVEGAMES];
 2038 
 2039 void Create_Savestrings (void)
 2040 {
 2041         int             i;
 2042         FILE    *f;
 2043         char    name[MAX_OSPATH];
 2044 
 2045         for (i=0 ; i<MAX_SAVEGAMES ; i++)
 2046         {
 2047                 Com_sprintf (name, sizeof(name), "%s/save/save%i/server.ssv", FS_Gamedir(), i);
 2048                 f = fopen (name, "rb");
 2049                 if (!f)
 2050                 {
 2051                         strcpy (m_savestrings[i], "<EMPTY>");
 2052                         m_savevalid[i] = false;
 2053                 }
 2054                 else
 2055                 {
 2056                         FS_Read (m_savestrings[i], sizeof(m_savestrings[i]), f);
 2057                         fclose (f);
 2058                         m_savevalid[i] = true;
 2059                 }
 2060         }
 2061 }
 2062 
 2063 void LoadGameCallback( void *self )
 2064 {
 2065         menuaction_s *a = ( menuaction_s * ) self;
 2066 
 2067         if ( m_savevalid[ a->generic.localdata[0] ] )
 2068                 Cbuf_AddText (va("load save%i\n",  a->generic.localdata[0] ) );
 2069         M_ForceMenuOff ();
 2070 }
 2071 
 2072 void LoadGame_MenuInit( void )
 2073 {
 2074         int i;
 2075 
 2076         s_loadgame_menu.x = viddef.width / 2 - 120;
 2077         s_loadgame_menu.y = viddef.height / 2 - 58;
 2078         s_loadgame_menu.nitems = 0;
 2079 
 2080         Create_Savestrings();
 2081 
 2082         for ( i = 0; i < MAX_SAVEGAMES; i++ )
 2083         {
 2084                 s_loadgame_actions[i].generic.name                      = m_savestrings[i];
 2085                 s_loadgame_actions[i].generic.flags                     = QMF_LEFT_JUSTIFY;
 2086                 s_loadgame_actions[i].generic.localdata[0]      = i;
 2087                 s_loadgame_actions[i].generic.callback          = LoadGameCallback;
 2088 
 2089                 s_loadgame_actions[i].generic.x = 0;
 2090                 s_loadgame_actions[i].generic.y = ( i ) * 10;
 2091                 if (i>0)        // separate from autosave
 2092                         s_loadgame_actions[i].generic.y += 10;
 2093 
 2094                 s_loadgame_actions[i].generic.type = MTYPE_ACTION;
 2095 
 2096                 Menu_AddItem( &s_loadgame_menu, &s_loadgame_actions[i] );
 2097         }
 2098 }
 2099 
 2100 void LoadGame_MenuDraw( void )
 2101 {
 2102         M_Banner( "m_banner_load_game" );
 2103 //      Menu_AdjustCursor( &s_loadgame_menu, 1 );
 2104         Menu_Draw( &s_loadgame_menu );
 2105 }
 2106 
 2107 const char *LoadGame_MenuKey( int key )
 2108 {
 2109         if ( key == K_ESCAPE || key == K_ENTER )
 2110         {
 2111                 s_savegame_menu.cursor = s_loadgame_menu.cursor - 1;
 2112                 if ( s_savegame_menu.cursor < 0 )
 2113                         s_savegame_menu.cursor = 0;
 2114         }
 2115         return Default_MenuKey( &s_loadgame_menu, key );
 2116 }
 2117 
 2118 void M_Menu_LoadGame_f (void)
 2119 {
 2120         LoadGame_MenuInit();
 2121         M_PushMenu( LoadGame_MenuDraw, LoadGame_MenuKey );
 2122 }
 2123 
 2124 
 2125 /*
 2126 =============================================================================
 2127 
 2128 SAVEGAME MENU
 2129 
 2130 =============================================================================
 2131 */
 2132 static menuframework_s  s_savegame_menu;
 2133 static menuaction_s             s_savegame_actions[MAX_SAVEGAMES];
 2134 
 2135 void SaveGameCallback( void *self )
 2136 {
 2137         menuaction_s *a = ( menuaction_s * ) self;
 2138 
 2139         Cbuf_AddText (va("save save%i\n", a->generic.localdata[0] ));
 2140         M_ForceMenuOff ();
 2141 }
 2142 
 2143 void SaveGame_MenuDraw( void )
 2144 {
 2145         M_Banner( "m_banner_save_game" );
 2146         Menu_AdjustCursor( &s_savegame_menu, 1 );
 2147         Menu_Draw( &s_savegame_menu );
 2148 }
 2149 
 2150 void SaveGame_MenuInit( void )
 2151 {
 2152         int i;
 2153 
 2154         s_savegame_menu.x = viddef.width / 2 - 120;
 2155         s_savegame_menu.y = viddef.height / 2 - 58;
 2156         s_savegame_menu.nitems = 0;
 2157 
 2158         Create_Savestrings();
 2159 
 2160         // don't include the autosave slot
 2161         for ( i = 0; i < MAX_SAVEGAMES-1; i++ )
 2162         {
 2163                 s_savegame_actions[i].generic.name = m_savestrings[i+1];
 2164                 s_savegame_actions[i].generic.localdata[0] = i+1;
 2165                 s_savegame_actions[i].generic.flags = QMF_LEFT_JUSTIFY;
 2166                 s_savegame_actions[i].generic.callback = SaveGameCallback;
 2167 
 2168                 s_savegame_actions[i].generic.x = 0;
 2169                 s_savegame_actions[i].generic.y = ( i ) * 10;
 2170 
 2171                 s_savegame_actions[i].generic.type = MTYPE_ACTION;
 2172 
 2173                 Menu_AddItem( &s_savegame_menu, &s_savegame_actions[i] );
 2174         }
 2175 }
 2176 
 2177 const char *SaveGame_MenuKey( int key )
 2178 {
 2179         if ( key == K_ENTER || key == K_ESCAPE )
 2180         {
 2181                 s_loadgame_menu.cursor = s_savegame_menu.cursor - 1;
 2182                 if ( s_loadgame_menu.cursor < 0 )
 2183                         s_loadgame_menu.cursor = 0;
 2184         }
 2185         return Default_MenuKey( &s_savegame_menu, key );
 2186 }
 2187 
 2188 void M_Menu_SaveGame_f (void)
 2189 {
 2190         if (!Com_ServerState())
 2191                 return;         // not playing a game
 2192 
 2193         SaveGame_MenuInit();
 2194         M_PushMenu( SaveGame_MenuDraw, SaveGame_MenuKey );
 2195         Create_Savestrings ();
 2196 }
 2197 
 2198 
 2199 /*
 2200 =============================================================================
 2201 
 2202 JOIN SERVER MENU
 2203 
 2204 =============================================================================
 2205 */
 2206 #define MAX_LOCAL_SERVERS 8
 2207 
 2208 static menuframework_s  s_joinserver_menu;
 2209 static menuseparator_s  s_joinserver_server_title;
 2210 static menuaction_s             s_joinserver_search_action;
 2211 static menuaction_s             s_joinserver_address_book_action;
 2212 static menuaction_s             s_joinserver_server_actions[MAX_LOCAL_SERVERS];
 2213 
 2214 int             m_num_servers;
 2215 #define NO_SERVER_STRING        "<no server>"
 2216 
 2217 // user readable information
 2218 static char local_server_names[MAX_LOCAL_SERVERS][80];
 2219 
 2220 // network address
 2221 static netadr_t local_server_netadr[MAX_LOCAL_SERVERS];
 2222 
 2223 void M_AddToServerList (netadr_t adr, char *info)
 2224 {
 2225         int             i;
 2226 
 2227         if (m_num_servers == MAX_LOCAL_SERVERS)
 2228                 return;
 2229         while ( *info == ' ' )
 2230                 info++;
 2231 
 2232         // ignore if duplicated
 2233         for (i=0 ; i<m_num_servers ; i++)
 2234                 if (!strcmp(info, local_server_names[i]))
 2235                         return;
 2236 
 2237         local_server_netadr[m_num_servers] = adr;
 2238         strncpy (local_server_names[m_num_servers], info, sizeof(local_server_names[0])-1);
 2239         m_num_servers++;
 2240 }
 2241 
 2242 
 2243 void JoinServerFunc( void *self )
 2244 {
 2245         char    buffer[128];
 2246         int             index;
 2247 
 2248         index = ( menuaction_s * ) self - s_joinserver_server_actions;
 2249 
 2250         if ( Q_stricmp( local_server_names[index], NO_SERVER_STRING ) == 0 )
 2251                 return;
 2252 
 2253         if (index >= m_num_servers)
 2254                 return;
 2255 
 2256         Com_sprintf (buffer, sizeof(buffer), "connect %s\n", NET_AdrToString (local_server_netadr[index]));
 2257         Cbuf_AddText (buffer);
 2258         M_ForceMenuOff ();
 2259 }
 2260 
 2261 void AddressBookFunc( void *self )
 2262 {
 2263         M_Menu_AddressBook_f();
 2264 }
 2265 
 2266 void NullCursorDraw( void *self )
 2267 {
 2268 }
 2269 
 2270 void SearchLocalGames( void )
 2271 {
 2272         int             i;
 2273 
 2274         m_num_servers = 0;
 2275         for (i=0 ; i<MAX_LOCAL_SERVERS ; i++)
 2276                 strcpy (local_server_names[i], NO_SERVER_STRING);
 2277 
 2278         M_DrawTextBox( 8, 120 - 48, 36, 3 );
 2279         M_Print( 16 + 16, 120 - 48 + 8,  "Searching for local servers, this" );
 2280         M_Print( 16 + 16, 120 - 48 + 16, "could take up to a minute, so" );
 2281         M_Print( 16 + 16, 120 - 48 + 24, "please be patient." );
 2282 
 2283         // the text box won't show up unless we do a buffer swap
 2284         re.EndFrame();
 2285 
 2286         // send out info packets
 2287         CL_PingServers_f();
 2288 }
 2289 
 2290 void SearchLocalGamesFunc( void *self )
 2291 {
 2292         SearchLocalGames();
 2293 }
 2294 
 2295 void JoinServer_MenuInit( void )
 2296 {
 2297         int i;
 2298 
 2299         s_joinserver_menu.x = viddef.width * 0.50 - 120;
 2300         s_joinserver_menu.nitems = 0;
 2301 
 2302         s_joinserver_address_book_action.generic.type   = MTYPE_ACTION;
 2303         s_joinserver_address_book_action.generic.name   = "address book";
 2304         s_joinserver_address_book_action.generic.flags  = QMF_LEFT_JUSTIFY;
 2305         s_joinserver_address_book_action.generic.x              = 0;
 2306         s_joinserver_address_book_action.generic.y              = 0;
 2307         s_joinserver_address_book_action.generic.callback = AddressBookFunc;
 2308 
 2309         s_joinserver_search_action.generic.type = MTYPE_ACTION;
 2310         s_joinserver_search_action.generic.name = "refresh server list";
 2311         s_joinserver_search_action.generic.flags        = QMF_LEFT_JUSTIFY;
 2312         s_joinserver_search_action.generic.x    = 0;
 2313         s_joinserver_search_action.generic.y    = 10;
 2314         s_joinserver_search_action.generic.callback = SearchLocalGamesFunc;
 2315         s_joinserver_search_action.generic.statusbar = "search for servers";
 2316 
 2317         s_joinserver_server_title.generic.type = MTYPE_SEPARATOR;
 2318         s_joinserver_server_title.generic.name = "connect to...";
 2319         s_joinserver_server_title.generic.x    = 80;
 2320         s_joinserver_server_title.generic.y        = 30;
 2321 
 2322         for ( i = 0; i < MAX_LOCAL_SERVERS; i++ )
 2323         {
 2324                 s_joinserver_server_actions[i].generic.type     = MTYPE_ACTION;
 2325                 strcpy (local_server_names[i], NO_SERVER_STRING);
 2326                 s_joinserver_server_actions[i].generic.name     = local_server_names[i];
 2327                 s_joinserver_server_actions[i].generic.flags    = QMF_LEFT_JUSTIFY;
 2328                 s_joinserver_server_actions[i].generic.x                = 0;
 2329                 s_joinserver_server_actions[i].generic.y                = 40 + i*10;
 2330                 s_joinserver_server_actions[i].generic.callback = JoinServerFunc;
 2331                 s_joinserver_server_actions[i].generic.statusbar = "press ENTER to connect";
 2332         }
 2333 
 2334         Menu_AddItem( &s_joinserver_menu, &s_joinserver_address_book_action );
 2335         Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_title );
 2336         Menu_AddItem( &s_joinserver_menu, &s_joinserver_search_action );
 2337 
 2338         for ( i = 0; i < 8; i++ )
 2339                 Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_actions[i] );
 2340 
 2341         Menu_Center( &s_joinserver_menu );
 2342 
 2343         SearchLocalGames();
 2344 }
 2345 
 2346 void JoinServer_MenuDraw(void)
 2347 {
 2348         M_Banner( "m_banner_join_server" );
 2349         Menu_Draw( &s_joinserver_menu );
 2350 }
 2351 
 2352 
 2353 const char *JoinServer_MenuKey( int key )
 2354 {
 2355         return Default_MenuKey( &s_joinserver_menu, key );
 2356 }
 2357 
 2358 void M_Menu_JoinServer_f (void)
 2359 {
 2360         JoinServer_MenuInit();
 2361         M_PushMenu( JoinServer_MenuDraw, JoinServer_MenuKey );
 2362 }
 2363 
 2364 
 2365 /*
 2366 =============================================================================
 2367 
 2368 START SERVER MENU
 2369 
 2370 =============================================================================
 2371 */
 2372 static menuframework_s s_startserver_menu;
 2373 static char **mapnames;
 2374 static int        nummaps;
 2375 
 2376 static menuaction_s     s_startserver_start_action;
 2377 static menuaction_s     s_startserver_dmoptions_action;
 2378 static menufield_s      s_timelimit_field;
 2379 static menufield_s      s_fraglimit_field;
 2380 static menufield_s      s_maxclients_field;
 2381 static menufield_s      s_hostname_field;
 2382 static menulist_s       s_startmap_list;
 2383 static menulist_s       s_rules_box;
 2384 
 2385 void DMOptionsFunc( void *self )
 2386 {
 2387         if (s_rules_box.curvalue == 1)
 2388                 return;
 2389         M_Menu_DMOptions_f();
 2390 }
 2391 
 2392 void RulesChangeFunc ( void *self )
 2393 {
 2394         // DM
 2395         if (s_rules_box.curvalue == 0)
 2396         {
 2397                 s_maxclients_field.generic.statusbar = NULL;
 2398                 s_startserver_dmoptions_action.generic.statusbar = NULL;
 2399         }
 2400         else if(s_rules_box.curvalue == 1)              // coop                         // PGM
 2401         {
 2402                 s_maxclients_field.generic.statusbar = "4 maximum for cooperative";
 2403                 if (atoi(s_maxclients_field.buffer) > 4)
 2404                         strcpy( s_maxclients_field.buffer, "4" );
 2405                 s_startserver_dmoptions_action.generic.statusbar = "N/A for cooperative";
 2406         }
 2407 //=====
 2408 //PGM
 2409         // ROGUE GAMES
 2410         else if(Developer_searchpath(2) == 2)
 2411         {
 2412                 if (s_rules_box.curvalue == 2)                  // tag  
 2413                 {
 2414                         s_maxclients_field.generic.statusbar = NULL;
 2415                         s_startserver_dmoptions_action.generic.statusbar = NULL;
 2416                 }
 2417 /*
 2418                 else if(s_rules_box.curvalue == 3)              // deathball
 2419                 {
 2420                         s_maxclients_field.generic.statusbar = NULL;
 2421                         s_startserver_dmoptions_action.generic.statusbar = NULL;
 2422                 }
 2423 */
 2424         }
 2425 //PGM
 2426 //=====
 2427 }
 2428 
 2429 void StartServerActionFunc( void *self )
 2430 {
 2431         char    startmap[1024];
 2432         int             timelimit;
 2433         int             fraglimit;
 2434         int             maxclients;
 2435         char    *spot;
 2436 
 2437         strcpy( startmap, strchr( mapnames[s_startmap_list.curvalue], '\n' ) + 1 );
 2438 
 2439         maxclients  = atoi( s_maxclients_field.buffer );
 2440         timelimit       = atoi( s_timelimit_field.buffer );
 2441         fraglimit       = atoi( s_fraglimit_field.buffer );
 2442 
 2443         Cvar_SetValue( "maxclients", ClampCvar( 0, maxclients, maxclients ) );
 2444         Cvar_SetValue ("timelimit", ClampCvar( 0, timelimit, timelimit ) );
 2445         Cvar_SetValue ("fraglimit", ClampCvar( 0, fraglimit, fraglimit ) );
 2446         Cvar_Set("hostname", s_hostname_field.buffer );
 2447 //      Cvar_SetValue ("deathmatch", !s_rules_box.curvalue );
 2448 //      Cvar_SetValue ("coop", s_rules_box.curvalue );
 2449 
 2450 //PGM
 2451         if((s_rules_box.curvalue < 2) || (Developer_searchpath(2) != 2))
 2452         {
 2453                 Cvar_SetValue ("deathmatch", !s_rules_box.curvalue );
 2454                 Cvar_SetValue ("coop", s_rules_box.curvalue );
 2455                 Cvar_SetValue ("gamerules", 0 );
 2456         }
 2457         else
 2458         {
 2459                 Cvar_SetValue ("deathmatch", 1 );       // deathmatch is always true for rogue games, right?
 2460                 Cvar_SetValue ("coop", 0 );                     // FIXME - this might need to depend on which game we're running
 2461                 Cvar_SetValue ("gamerules", s_rules_box.curvalue );
 2462         }
 2463 //PGM
 2464 
 2465         spot = NULL;
 2466         if (s_rules_box.curvalue == 1)          // PGM
 2467         {
 2468                 if(Q_stricmp(startmap, "bunk1") == 0)
 2469                         spot = "start";
 2470                 else if(Q_stricmp(startmap, "mintro") == 0)
 2471                         spot = "start";
 2472                 else if(Q_stricmp(startmap, "fact1") == 0)
 2473                         spot = "start";
 2474                 else if(Q_stricmp(startmap, "power1") == 0)
 2475                         spot = "pstart";
 2476                 else if(Q_stricmp(startmap, "biggun") == 0)
 2477                         spot = "bstart";
 2478                 else if(Q_stricmp(startmap, "hangar1") == 0)
 2479                         spot = "unitstart";
 2480                 else if(Q_stricmp(startmap, "city1") == 0)
 2481                         spot = "unitstart";
 2482                 else if(Q_stricmp(startmap, "boss1") == 0)
 2483                         spot = "bosstart";
 2484         }
 2485 
 2486         if (spot)
 2487         {
 2488                 if (Com_ServerState())
 2489                         Cbuf_AddText ("disconnect\n");
 2490                 Cbuf_AddText (va("gamemap \"*%s$%s\"\n", startmap, spot));
 2491         }
 2492         else
 2493         {
 2494                 Cbuf_AddText (va("map %s\n", startmap));
 2495         }
 2496 
 2497         M_ForceMenuOff ();
 2498 }
 2499 
 2500 void StartServer_MenuInit( void )
 2501 {
 2502         static const char *dm_coop_names[] =
 2503         {
 2504                 "deathmatch",
 2505                 "cooperative",
 2506                 0
 2507         };
 2508 //=======
 2509 //PGM
 2510         static const char *dm_coop_names_rogue[] =
 2511         {
 2512                 "deathmatch",
 2513                 "cooperative",
 2514                 "tag",
 2515 //              "deathball",
 2516                 0
 2517         };
 2518 //PGM
 2519 //=======
 2520         char *buffer;
 2521         char  mapsname[1024];
 2522         char *s;
 2523         int length;
 2524         int i;
 2525         FILE *fp;
 2526 
 2527         /*
 2528         ** load the list of map names
 2529         */
 2530         Com_sprintf( mapsname, sizeof( mapsname ), "%s/maps.lst", FS_Gamedir() );
 2531         if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
 2532         {
 2533                 if ( ( length = FS_LoadFile( "maps.lst", ( void ** ) &buffer ) ) == -1 )
 2534                         Com_Error( ERR_DROP, "couldn't find maps.lst\n" );
 2535         }
 2536         else
 2537         {
 2538 #ifdef _WIN32
 2539                 length = filelength( fileno( fp  ) );
2540 #else 2541 fseek(fp, 0, SEEK_END); 2542 length = ftell(fp); 2543 fseek(fp, 0, SEEK_SET);
2544 #endif 2545 buffer = malloc( length ); 2546 fread( buffer, length, 1, fp ); 2547 } 2548 2549 s = buffer; 2550 2551 i = 0; 2552 while ( i < length ) 2553 { 2554 if ( s[i] == '\r' ) 2555 nummaps++; 2556 i++; 2557 } 2558 2559 if ( nummaps == 0 ) 2560 Com_Error( ERR_DROP, "no maps in maps.lst\n" ); 2561 2562 mapnames = malloc( sizeof( char * ) * ( nummaps + 1 ) ); 2563 memset( mapnames, 0, sizeof( char * ) * ( nummaps + 1 ) ); 2564 2565 s = buffer; 2566 2567 for ( i = 0; i < nummaps; i++ ) 2568 { 2569 char shortname[MAX_TOKEN_CHARS]; 2570 char longname[MAX_TOKEN_CHARS]; 2571 char scratch[200]; 2572 int j, l; 2573 2574 strcpy( shortname, COM_Parse( &s ) ); 2575 l = strlen(shortname); 2576 for (j=0 ; j<l ; j++) 2577 shortname[j] = toupper(shortname[j]); 2578 strcpy( longname, COM_Parse( &s ) ); 2579 Com_sprintf( scratch, sizeof( scratch ), "%s\n%s", longname, shortname ); 2580 2581 mapnames[i] = malloc( strlen( scratch ) + 1 ); 2582 strcpy( mapnames[i], scratch ); 2583 } 2584 mapnames[nummaps] = 0; 2585 2586 if ( fp != 0 ) 2587 { 2588 fp = 0; 2589 free( buffer ); 2590 } 2591 else 2592 { 2593 FS_FreeFile( buffer ); 2594 } 2595 2596 /* 2597 ** initialize the menu stuff 2598 */ 2599 s_startserver_menu.x = viddef.width * 0.50; 2600 s_startserver_menu.nitems = 0; 2601 2602 s_startmap_list.generic.type = MTYPE_SPINCONTROL; 2603 s_startmap_list.generic.x = 0; 2604 s_startmap_list.generic.y = 0; 2605 s_startmap_list.generic.name = "initial map"; 2606 s_startmap_list.itemnames = mapnames; 2607 2608 s_rules_box.generic.type = MTYPE_SPINCONTROL; 2609 s_rules_box.generic.x = 0; 2610 s_rules_box.generic.y = 20; 2611 s_rules_box.generic.name = "rules"; 2612 2613 //PGM - rogue games only available with rogue DLL. 2614 if(Developer_searchpath(2) == 2) 2615 s_rules_box.itemnames = dm_coop_names_rogue; 2616 else 2617 s_rules_box.itemnames = dm_coop_names; 2618 //PGM 2619 2620 if (Cvar_VariableValue("coop")) 2621 s_rules_box.curvalue = 1; 2622 else 2623 s_rules_box.curvalue = 0; 2624 s_rules_box.generic.callback = RulesChangeFunc; 2625 2626 s_timelimit_field.generic.type = MTYPE_FIELD; 2627 s_timelimit_field.generic.name = "time limit"; 2628 s_timelimit_field.generic.flags = QMF_NUMBERSONLY; 2629 s_timelimit_field.generic.x = 0; 2630 s_timelimit_field.generic.y = 36; 2631 s_timelimit_field.generic.statusbar = "0 = no limit"; 2632 s_timelimit_field.length = 3; 2633 s_timelimit_field.visible_length = 3; 2634 strcpy( s_timelimit_field.buffer, Cvar_VariableString("timelimit") ); 2635 2636 s_fraglimit_field.generic.type = MTYPE_FIELD; 2637 s_fraglimit_field.generic.name = "frag limit"; 2638 s_fraglimit_field.generic.flags = QMF_NUMBERSONLY; 2639 s_fraglimit_field.generic.x = 0; 2640 s_fraglimit_field.generic.y = 54; 2641 s_fraglimit_field.generic.statusbar = "0 = no limit"; 2642 s_fraglimit_field.length = 3; 2643 s_fraglimit_field.visible_length = 3; 2644 strcpy( s_fraglimit_field.buffer, Cvar_VariableString("fraglimit") ); 2645 2646 /* 2647 ** maxclients determines the maximum number of players that can join 2648 ** the game. If maxclients is only "1" then we should default the menu 2649 ** option to 8 players, otherwise use whatever its current value is. 2650 ** Clamping will be done when the server is actually started. 2651 */ 2652 s_maxclients_field.generic.type = MTYPE_FIELD; 2653 s_maxclients_field.generic.name = "max players"; 2654 s_maxclients_field.generic.flags = QMF_NUMBERSONLY; 2655 s_maxclients_field.generic.x = 0; 2656 s_maxclients_field.generic.y = 72; 2657 s_maxclients_field.generic.statusbar = NULL; 2658 s_maxclients_field.length = 3; 2659 s_maxclients_field.visible_length = 3; 2660 if ( Cvar_VariableValue( "maxclients" ) == 1 ) 2661 strcpy( s_maxclients_field.buffer, "8" ); 2662 else 2663 strcpy( s_maxclients_field.buffer, Cvar_VariableString("maxclients") ); 2664 2665 s_hostname_field.generic.type = MTYPE_FIELD; 2666 s_hostname_field.generic.name = "hostname"; 2667 s_hostname_field.generic.flags = 0; 2668 s_hostname_field.generic.x = 0; 2669 s_hostname_field.generic.y = 90; 2670 s_hostname_field.generic.statusbar = NULL; 2671 s_hostname_field.length = 12; 2672 s_hostname_field.visible_length = 12; 2673 strcpy( s_hostname_field.buffer, Cvar_VariableString("hostname") ); 2674 2675 s_startserver_dmoptions_action.generic.type = MTYPE_ACTION; 2676 s_startserver_dmoptions_action.generic.name = " deathmatch flags"; 2677 s_startserver_dmoptions_action.generic.flags= QMF_LEFT_JUSTIFY; 2678 s_startserver_dmoptions_action.generic.x = 24; 2679 s_startserver_dmoptions_action.generic.y = 108; 2680 s_startserver_dmoptions_action.generic.statusbar = NULL; 2681 s_startserver_dmoptions_action.generic.callback = DMOptionsFunc; 2682 2683 s_startserver_start_action.generic.type = MTYPE_ACTION; 2684 s_startserver_start_action.generic.name = " begin"; 2685 s_startserver_start_action.generic.flags= QMF_LEFT_JUSTIFY; 2686 s_startserver_start_action.generic.x = 24; 2687 s_startserver_start_action.generic.y = 128; 2688 s_startserver_start_action.generic.callback = StartServerActionFunc; 2689 2690 Menu_AddItem( &s_startserver_menu, &s_startmap_list ); 2691 Menu_AddItem( &s_startserver_menu, &s_rules_box ); 2692 Menu_AddItem( &s_startserver_menu, &s_timelimit_field ); 2693 Menu_AddItem( &s_startserver_menu, &s_fraglimit_field ); 2694 Menu_AddItem( &s_startserver_menu, &s_maxclients_field ); 2695 Menu_AddItem( &s_startserver_menu, &s_hostname_field ); 2696 Menu_AddItem( &s_startserver_menu, &s_startserver_dmoptions_action ); 2697 Menu_AddItem( &s_startserver_menu, &s_startserver_start_action ); 2698 2699 Menu_Center( &s_startserver_menu ); 2700 2701 // call this now to set proper inital state 2702 RulesChangeFunc ( NULL ); 2703 } 2704 2705 void StartServer_MenuDraw(void) 2706 { 2707 Menu_Draw( &s_startserver_menu ); 2708 } 2709 2710 const char *StartServer_MenuKey( int key ) 2711 { 2712 if ( key == K_ESCAPE ) 2713 { 2714 if ( mapnames ) 2715 { 2716 int i; 2717 2718 for ( i = 0; i < nummaps; i++ ) 2719 free( mapnames[i] ); 2720 free( mapnames ); 2721 } 2722 mapnames = 0; 2723 nummaps = 0; 2724 } 2725 2726 return Default_MenuKey( &s_startserver_menu, key ); 2727 } 2728 2729 void M_Menu_StartServer_f (void) 2730 { 2731 StartServer_MenuInit(); 2732 M_PushMenu( StartServer_MenuDraw, StartServer_MenuKey ); 2733 } 2734 2735 /* 2736 ============================================================================= 2737 2738 DMOPTIONS BOOK MENU 2739 2740 ============================================================================= 2741 */ 2742 static char dmoptions_statusbar[128]; 2743 2744 static menuframework_s s_dmoptions_menu; 2745 2746 static menulist_s s_friendlyfire_box; 2747 static menulist_s s_falls_box; 2748 static menulist_s s_weapons_stay_box; 2749 static menulist_s s_instant_powerups_box; 2750 static menulist_s s_powerups_box; 2751 static menulist_s s_health_box; 2752 static menulist_s s_spawn_farthest_box; 2753 static menulist_s s_teamplay_box; 2754 static menulist_s s_samelevel_box; 2755 static menulist_s s_force_respawn_box; 2756 static menulist_s s_armor_box; 2757 static menulist_s s_allow_exit_box; 2758 static menulist_s s_infinite_ammo_box; 2759 static menulist_s s_fixed_fov_box; 2760 static menulist_s s_quad_drop_box; 2761 2762 //ROGUE 2763 static menulist_s s_no_mines_box; 2764 static menulist_s s_no_nukes_box; 2765 static menulist_s s_stack_double_box; 2766 static menulist_s s_no_spheres_box; 2767 //ROGUE 2768 2769 static void DMFlagCallback( void *self ) 2770 { 2771 menulist_s *f = ( menulist_s * ) self; 2772 int flags; 2773 int bit = 0; 2774 2775 flags = Cvar_VariableValue( "dmflags" ); 2776 2777 if ( f == &s_friendlyfire_box ) 2778 { 2779 if ( f->curvalue ) 2780 flags &= ~DF_NO_FRIENDLY_FIRE; 2781 else 2782 flags |= DF_NO_FRIENDLY_FIRE; 2783 goto setvalue; 2784 } 2785 else if ( f == &s_falls_box ) 2786 { 2787 if ( f->curvalue ) 2788 flags &= ~DF_NO_FALLING; 2789 else 2790 flags |= DF_NO_FALLING; 2791 goto setvalue; 2792 } 2793 else if ( f == &s_weapons_stay_box ) 2794 { 2795 bit = DF_WEAPONS_STAY; 2796 } 2797 else if ( f == &s_instant_powerups_box ) 2798 { 2799 bit = DF_INSTANT_ITEMS; 2800 } 2801 else if ( f == &s_allow_exit_box ) 2802 { 2803 bit = DF_ALLOW_EXIT; 2804 } 2805 else if ( f == &s_powerups_box ) 2806 { 2807 if ( f->curvalue ) 2808 flags &= ~DF_NO_ITEMS; 2809 else 2810 flags |= DF_NO_ITEMS; 2811 goto setvalue; 2812 } 2813 else if ( f == &s_health_box ) 2814 { 2815 if ( f->curvalue ) 2816 flags &= ~DF_NO_HEALTH; 2817 else 2818 flags |= DF_NO_HEALTH; 2819 goto setvalue; 2820 } 2821 else if ( f == &s_spawn_farthest_box ) 2822 { 2823 bit = DF_SPAWN_FARTHEST; 2824 } 2825 else if ( f == &s_teamplay_box ) 2826 { 2827 if ( f->curvalue == 1 ) 2828 { 2829 flags |= DF_SKINTEAMS; 2830 flags &= ~DF_MODELTEAMS; 2831 } 2832 else if ( f->curvalue == 2 ) 2833 { 2834 flags |= DF_MODELTEAMS; 2835 flags &= ~DF_SKINTEAMS; 2836 } 2837 else 2838 { 2839 flags &= ~( DF_MODELTEAMS | DF_SKINTEAMS ); 2840 } 2841 2842 goto setvalue; 2843 } 2844 else if ( f == &s_samelevel_box ) 2845 { 2846 bit = DF_SAME_LEVEL; 2847 } 2848 else if ( f == &s_force_respawn_box ) 2849 { 2850 bit = DF_FORCE_RESPAWN; 2851 } 2852 else if ( f == &s_armor_box ) 2853 { 2854 if ( f->curvalue ) 2855 flags &= ~DF_NO_ARMOR; 2856 else 2857 flags |= DF_NO_ARMOR; 2858 goto setvalue; 2859 } 2860 else if ( f == &s_infinite_ammo_box ) 2861 { 2862 bit = DF_INFINITE_AMMO; 2863 } 2864 else if ( f == &s_fixed_fov_box ) 2865 { 2866 bit = DF_FIXED_FOV; 2867 } 2868 else if ( f == &s_quad_drop_box ) 2869 { 2870 bit = DF_QUAD_DROP; 2871 } 2872 2873 //======= 2874 //ROGUE 2875 else if (Developer_searchpath(2) == 2) 2876 { 2877 if ( f == &s_no_mines_box) 2878 { 2879 bit = DF_NO_MINES; 2880 } 2881 else if ( f == &s_no_nukes_box) 2882 { 2883 bit = DF_NO_NUKES; 2884 } 2885 else if ( f == &s_stack_double_box) 2886 { 2887 bit = DF_NO_STACK_DOUBLE; 2888 } 2889 else if ( f == &s_no_spheres_box) 2890 { 2891 bit = DF_NO_SPHERES; 2892 } 2893 } 2894 //ROGUE 2895 //======= 2896 2897 if ( f ) 2898 { 2899 if ( f->curvalue == 0 ) 2900 flags &= ~bit; 2901 else 2902 flags |= bit; 2903 } 2904 2905 setvalue: 2906 Cvar_SetValue ("dmflags", flags); 2907 2908 Com_sprintf( dmoptions_statusbar, sizeof( dmoptions_statusbar ), "dmflags = %d", flags ); 2909 2910 } 2911 2912 void DMOptions_MenuInit( void ) 2913 { 2914 static const char *yes_no_names[] = 2915 { 2916 "no", "yes", 0 2917 }; 2918 static const char *teamplay_names[] = 2919 { 2920 "disabled", "by skin", "by model", 0 2921 }; 2922 int dmflags = Cvar_VariableValue( "dmflags" ); 2923 int y = 0; 2924 2925 s_dmoptions_menu.x = viddef.width * 0.50; 2926 s_dmoptions_menu.nitems = 0; 2927 2928 s_falls_box.generic.type = MTYPE_SPINCONTROL; 2929 s_falls_box.generic.x = 0; 2930 s_falls_box.generic.y = y; 2931 s_falls_box.generic.name = "falling damage"; 2932 s_falls_box.generic.callback = DMFlagCallback; 2933 s_falls_box.itemnames = yes_no_names; 2934 s_falls_box.curvalue = ( dmflags & DF_NO_FALLING ) == 0; 2935 2936 s_weapons_stay_box.generic.type = MTYPE_SPINCONTROL; 2937 s_weapons_stay_box.generic.x = 0; 2938 s_weapons_stay_box.generic.y = y += 10; 2939 s_weapons_stay_box.generic.name = "weapons stay"; 2940 s_weapons_stay_box.generic.callback = DMFlagCallback; 2941 s_weapons_stay_box.itemnames = yes_no_names; 2942 s_weapons_stay_box.curvalue = ( dmflags & DF_WEAPONS_STAY ) != 0; 2943 2944 s_instant_powerups_box.generic.type = MTYPE_SPINCONTROL; 2945 s_instant_powerups_box.generic.x = 0; 2946 s_instant_powerups_box.generic.y = y += 10; 2947 s_instant_powerups_box.generic.name = "instant powerups"; 2948 s_instant_powerups_box.generic.callback = DMFlagCallback; 2949 s_instant_powerups_box.itemnames = yes_no_names; 2950 s_instant_powerups_box.curvalue = ( dmflags & DF_INSTANT_ITEMS ) != 0; 2951 2952 s_powerups_box.generic.type = MTYPE_SPINCONTROL; 2953 s_powerups_box.generic.x = 0; 2954 s_powerups_box.generic.y = y += 10; 2955 s_powerups_box.generic.name = "allow powerups"; 2956 s_powerups_box.generic.callback = DMFlagCallback; 2957 s_powerups_box.itemnames = yes_no_names; 2958 s_powerups_box.curvalue = ( dmflags & DF_NO_ITEMS ) == 0; 2959 2960 s_health_box.generic.type = MTYPE_SPINCONTROL; 2961 s_health_box.generic.x = 0; 2962 s_health_box.generic.y = y += 10; 2963 s_health_box.generic.callback = DMFlagCallback; 2964 s_health_box.generic.name = "allow health"; 2965 s_health_box.itemnames = yes_no_names; 2966 s_health_box.curvalue = ( dmflags & DF_NO_HEALTH ) == 0; 2967 2968 s_armor_box.generic.type = MTYPE_SPINCONTROL; 2969 s_armor_box.generic.x = 0; 2970 s_armor_box.generic.y = y += 10; 2971 s_armor_box.generic.name = "allow armor"; 2972 s_armor_box.generic.callback = DMFlagCallback; 2973 s_armor_box.itemnames = yes_no_names; 2974 s_armor_box.curvalue = ( dmflags & DF_NO_ARMOR ) == 0; 2975 2976 s_spawn_farthest_box.generic.type = MTYPE_SPINCONTROL; 2977 s_spawn_farthest_box.generic.x = 0; 2978 s_spawn_farthest_box.generic.y = y += 10; 2979 s_spawn_farthest_box.generic.name = "spawn farthest"; 2980 s_spawn_farthest_box.generic.callback = DMFlagCallback; 2981 s_spawn_farthest_box.itemnames = yes_no_names; 2982 s_spawn_farthest_box.curvalue = ( dmflags & DF_SPAWN_FARTHEST ) != 0; 2983 2984 s_samelevel_box.generic.type = MTYPE_SPINCONTROL; 2985 s_samelevel_box.generic.x = 0; 2986 s_samelevel_box.generic.y = y += 10; 2987 s_samelevel_box.generic.name = "same map"; 2988 s_samelevel_box.generic.callback = DMFlagCallback; 2989 s_samelevel_box.itemnames = yes_no_names; 2990 s_samelevel_box.curvalue = ( dmflags & DF_SAME_LEVEL ) != 0; 2991 2992 s_force_respawn_box.generic.type = MTYPE_SPINCONTROL; 2993 s_force_respawn_box.generic.x = 0; 2994 s_force_respawn_box.generic.y = y += 10; 2995 s_force_respawn_box.generic.name = "force respawn"; 2996 s_force_respawn_box.generic.callback = DMFlagCallback; 2997 s_force_respawn_box.itemnames = yes_no_names; 2998 s_force_respawn_box.curvalue = ( dmflags & DF_FORCE_RESPAWN ) != 0; 2999 3000 s_teamplay_box.generic.type = MTYPE_SPINCONTROL; 3001 s_teamplay_box.generic.x = 0; 3002 s_teamplay_box.generic.y = y += 10; 3003 s_teamplay_box.generic.name = "teamplay"; 3004 s_teamplay_box.generic.callback = DMFlagCallback; 3005 s_teamplay_box.itemnames = teamplay_names; 3006 3007 s_allow_exit_box.generic.type = MTYPE_SPINCONTROL; 3008 s_allow_exit_box.generic.x = 0; 3009 s_allow_exit_box.generic.y = y += 10; 3010 s_allow_exit_box.generic.name = "allow exit"; 3011 s_allow_exit_box.generic.callback = DMFlagCallback; 3012 s_allow_exit_box.itemnames = yes_no_names; 3013 s_allow_exit_box.curvalue = ( dmflags & DF_ALLOW_EXIT ) != 0; 3014 3015 s_infinite_ammo_box.generic.type = MTYPE_SPINCONTROL; 3016 s_infinite_ammo_box.generic.x = 0; 3017 s_infinite_ammo_box.generic.y = y += 10; 3018 s_infinite_ammo_box.generic.name = "infinite ammo"; 3019 s_infinite_ammo_box.generic.callback = DMFlagCallback; 3020 s_infinite_ammo_box.itemnames = yes_no_names; 3021 s_infinite_ammo_box.curvalue = ( dmflags & DF_INFINITE_AMMO ) != 0; 3022 3023 s_fixed_fov_box.generic.type = MTYPE_SPINCONTROL; 3024 s_fixed_fov_box.generic.x = 0; 3025 s_fixed_fov_box.generic.y = y += 10; 3026 s_fixed_fov_box.generic.name = "fixed FOV"; 3027 s_fixed_fov_box.generic.callback = DMFlagCallback; 3028 s_fixed_fov_box.itemnames = yes_no_names; 3029 s_fixed_fov_box.curvalue = ( dmflags & DF_FIXED_FOV ) != 0; 3030 3031 s_quad_drop_box.generic.type = MTYPE_SPINCONTROL; 3032 s_quad_drop_box.generic.x = 0; 3033 s_quad_drop_box.generic.y = y += 10; 3034 s_quad_drop_box.generic.name = "quad drop"; 3035 s_quad_drop_box.generic.callback = DMFlagCallback; 3036 s_quad_drop_box.itemnames = yes_no_names; 3037 s_quad_drop_box.curvalue = ( dmflags & DF_QUAD_DROP ) != 0; 3038 3039 s_friendlyfire_box.generic.type = MTYPE_SPINCONTROL; 3040 s_friendlyfire_box.generic.x = 0; 3041 s_friendlyfire_box.generic.y = y += 10; 3042 s_friendlyfire_box.generic.name = "friendly fire"; 3043 s_friendlyfire_box.generic.callback = DMFlagCallback; 3044 s_friendlyfire_box.itemnames = yes_no_names; 3045 s_friendlyfire_box.curvalue = ( dmflags & DF_NO_FRIENDLY_FIRE ) == 0; 3046 3047 //============ 3048 //ROGUE 3049 if(Developer_searchpath(2) == 2) 3050 { 3051 s_no_mines_box.generic.type = MTYPE_SPINCONTROL; 3052 s_no_mines_box.generic.x = 0; 3053 s_no_mines_box.generic.y = y += 10; 3054 s_no_mines_box.generic.name = "remove mines"; 3055 s_no_mines_box.generic.callback = DMFlagCallback; 3056 s_no_mines_box.itemnames = yes_no_names; 3057 s_no_mines_box.curvalue = ( dmflags & DF_NO_MINES ) != 0; 3058 3059 s_no_nukes_box.generic.type = MTYPE_SPINCONTROL; 3060 s_no_nukes_box.generic.x = 0; 3061 s_no_nukes_box.generic.y = y += 10; 3062 s_no_nukes_box.generic.name = "remove nukes"; 3063 s_no_nukes_box.generic.callback = DMFlagCallback; 3064 s_no_nukes_box.itemnames = yes_no_names; 3065 s_no_nukes_box.curvalue = ( dmflags & DF_NO_NUKES ) != 0; 3066 3067 s_stack_double_box.generic.type = MTYPE_SPINCONTROL; 3068 s_stack_double_box.generic.x = 0; 3069 s_stack_double_box.generic.y = y += 10; 3070 s_stack_double_box.generic.name = "2x/4x stacking off"; 3071 s_stack_double_box.generic.callback = DMFlagCallback; 3072 s_stack_double_box.itemnames = yes_no_names; 3073 s_stack_double_box.curvalue = ( dmflags & DF_NO_STACK_DOUBLE ) != 0; 3074 3075 s_no_spheres_box.generic.type = MTYPE_SPINCONTROL; 3076 s_no_spheres_box.generic.x = 0; 3077 s_no_spheres_box.generic.y = y += 10; 3078 s_no_spheres_box.generic.name = "remove spheres"; 3079 s_no_spheres_box.generic.callback = DMFlagCallback; 3080 s_no_spheres_box.itemnames = yes_no_names; 3081 s_no_spheres_box.curvalue = ( dmflags & DF_NO_SPHERES ) != 0; 3082 3083 } 3084 //ROGUE 3085 //============ 3086 3087 Menu_AddItem( &s_dmoptions_menu, &s_falls_box ); 3088 Menu_AddItem( &s_dmoptions_menu, &s_weapons_stay_box ); 3089 Menu_AddItem( &s_dmoptions_menu, &s_instant_powerups_box ); 3090 Menu_AddItem( &s_dmoptions_menu, &s_powerups_box ); 3091 Menu_AddItem( &s_dmoptions_menu, &s_health_box ); 3092 Menu_AddItem( &s_dmoptions_menu, &s_armor_box ); 3093 Menu_AddItem( &s_dmoptions_menu, &s_spawn_farthest_box ); 3094 Menu_AddItem( &s_dmoptions_menu, &s_samelevel_box ); 3095 Menu_AddItem( &s_dmoptions_menu, &s_force_respawn_box ); 3096 Menu_AddItem( &s_dmoptions_menu, &s_teamplay_box ); 3097 Menu_AddItem( &s_dmoptions_menu, &s_allow_exit_box ); 3098 Menu_AddItem( &s_dmoptions_menu, &s_infinite_ammo_box ); 3099 Menu_AddItem( &s_dmoptions_menu, &s_fixed_fov_box ); 3100 Menu_AddItem( &s_dmoptions_menu, &s_quad_drop_box ); 3101 Menu_AddItem( &s_dmoptions_menu, &s_friendlyfire_box ); 3102 3103 //======= 3104 //ROGUE 3105 if(Developer_searchpath(2) == 2) 3106 { 3107 Menu_AddItem( &s_dmoptions_menu, &s_no_mines_box ); 3108 Menu_AddItem( &s_dmoptions_menu, &s_no_nukes_box ); 3109 Menu_AddItem( &s_dmoptions_menu, &s_stack_double_box ); 3110 Menu_AddItem( &s_dmoptions_menu, &s_no_spheres_box ); 3111 } 3112 //ROGUE 3113 //======= 3114 3115 Menu_Center( &s_dmoptions_menu ); 3116 3117 // set the original dmflags statusbar 3118 DMFlagCallback( 0 ); 3119 Menu_SetStatusBar( &s_dmoptions_menu, dmoptions_statusbar ); 3120 } 3121 3122 void DMOptions_MenuDraw(void) 3123 { 3124 Menu_Draw( &s_dmoptions_menu ); 3125 } 3126 3127 const char *DMOptions_MenuKey( int key ) 3128 { 3129 return Default_MenuKey( &s_dmoptions_menu, key ); 3130 } 3131 3132 void M_Menu_DMOptions_f (void) 3133 { 3134 DMOptions_MenuInit(); 3135 M_PushMenu( DMOptions_MenuDraw, DMOptions_MenuKey ); 3136 } 3137 3138 /* 3139 ============================================================================= 3140 3141 DOWNLOADOPTIONS BOOK MENU 3142 3143 ============================================================================= 3144 */ 3145 static menuframework_s s_downloadoptions_menu; 3146 3147 static menuseparator_s s_download_title; 3148 static menulist_s s_allow_download_box; 3149 static menulist_s s_allow_download_maps_box; 3150 static menulist_s s_allow_download_models_box; 3151 static menulist_s s_allow_download_players_box; 3152 static menulist_s s_allow_download_sounds_box; 3153 3154 static void DownloadCallback( void *self ) 3155 { 3156 menulist_s *f = ( menulist_s * ) self; 3157 3158 if (f == &s_allow_download_box) 3159 { 3160 Cvar_SetValue("allow_download", f->curvalue); 3161 } 3162 3163 else if (f == &s_allow_download_maps_box) 3164 { 3165 Cvar_SetValue("allow_download_maps", f->curvalue); 3166 } 3167 3168 else if (f == &s_allow_download_models_box) 3169 { 3170 Cvar_SetValue("allow_download_models", f->curvalue); 3171 } 3172 3173 else if (f == &s_allow_download_players_box) 3174 { 3175 Cvar_SetValue("allow_download_players", f->curvalue); 3176 } 3177 3178 else if (f == &s_allow_download_sounds_box) 3179 { 3180 Cvar_SetValue("allow_download_sounds", f->curvalue); 3181 } 3182 } 3183 3184 void DownloadOptions_MenuInit( void ) 3185 { 3186 static const char *yes_no_names[] = 3187 { 3188 "no", "yes", 0 3189 }; 3190 int y = 0; 3191 3192 s_downloadoptions_menu.x = viddef.width * 0.50; 3193 s_downloadoptions_menu.nitems = 0; 3194 3195 s_download_title.generic.type = MTYPE_SEPARATOR; 3196 s_download_title.generic.name = "Download Options"; 3197 s_download_title.generic.x = 48; 3198 s_download_title.generic.y = y; 3199 3200 s_allow_download_box.generic.type = MTYPE_SPINCONTROL; 3201 s_allow_download_box.generic.x = 0; 3202 s_allow_download_box.generic.y = y += 20; 3203 s_allow_download_box.generic.name = "allow downloading"; 3204 s_allow_download_box.generic.callback = DownloadCallback; 3205 s_allow_download_box.itemnames = yes_no_names; 3206 s_allow_download_box.curvalue = (Cvar_VariableValue("allow_download") != 0); 3207 3208 s_allow_download_maps_box.generic.type = MTYPE_SPINCONTROL; 3209 s_allow_download_maps_box.generic.x = 0; 3210 s_allow_download_maps_box.generic.y = y += 20; 3211 s_allow_download_maps_box.generic.name = "maps"; 3212 s_allow_download_maps_box.generic.callback = DownloadCallback; 3213 s_allow_download_maps_box.itemnames = yes_no_names; 3214 s_allow_download_maps_box.curvalue = (Cvar_VariableValue("allow_download_maps") != 0); 3215 3216 s_allow_download_players_box.generic.type = MTYPE_SPINCONTROL; 3217 s_allow_download_players_box.generic.x = 0; 3218 s_allow_download_players_box.generic.y = y += 10; 3219 s_allow_download_players_box.generic.name = "player models/skins"; 3220 s_allow_download_players_box.generic.callback = DownloadCallback; 3221 s_allow_download_players_box.itemnames = yes_no_names; 3222 s_allow_download_players_box.curvalue = (Cvar_VariableValue("allow_download_players") != 0); 3223 3224 s_allow_download_models_box.generic.type = MTYPE_SPINCONTROL; 3225 s_allow_download_models_box.generic.x = 0; 3226 s_allow_download_models_box.generic.y = y += 10; 3227 s_allow_download_models_box.generic.name = "models"; 3228 s_allow_download_models_box.generic.callback = DownloadCallback; 3229 s_allow_download_models_box.itemnames = yes_no_names; 3230 s_allow_download_models_box.curvalue = (Cvar_VariableValue("allow_download_models") != 0); 3231 3232 s_allow_download_sounds_box.generic.type = MTYPE_SPINCONTROL; 3233 s_allow_download_sounds_box.generic.x = 0; 3234 s_allow_download_sounds_box.generic.y = y += 10; 3235 s_allow_download_sounds_box.generic.name = "sounds"; 3236 s_allow_download_sounds_box.generic.callback = DownloadCallback; 3237 s_allow_download_sounds_box.itemnames = yes_no_names; 3238 s_allow_download_sounds_box.curvalue = (Cvar_VariableValue("allow_download_sounds") != 0); 3239 3240 Menu_AddItem( &s_downloadoptions_menu, &s_download_title ); 3241 Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_box ); 3242 Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_maps_box ); 3243 Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_players_box ); 3244 Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_models_box ); 3245 Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_sounds_box ); 3246 3247 Menu_Center( &s_downloadoptions_menu ); 3248 3249 // skip over title 3250 if (s_downloadoptions_menu.cursor == 0) 3251 s_downloadoptions_menu.cursor = 1; 3252 } 3253 3254 void DownloadOptions_MenuDraw(void) 3255 { 3256 Menu_Draw( &s_downloadoptions_menu ); 3257 } 3258 3259 const char *DownloadOptions_MenuKey( int key ) 3260 { 3261 return Default_MenuKey( &s_downloadoptions_menu, key ); 3262 } 3263 3264 void M_Menu_DownloadOptions_f (void) 3265 { 3266 DownloadOptions_MenuInit(); 3267 M_PushMenu( DownloadOptions_MenuDraw, DownloadOptions_MenuKey ); 3268 } 3269 /* 3270 ============================================================================= 3271 3272 ADDRESS BOOK MENU 3273 3274 ============================================================================= 3275 */ 3276 #define NUM_ADDRESSBOOK_ENTRIES 9 3277 3278 static menuframework_s s_addressbook_menu; 3279 static menufield_s s_addressbook_fields[NUM_ADDRESSBOOK_ENTRIES]; 3280 3281 void AddressBook_MenuInit( void ) 3282 { 3283 int i; 3284 3285 s_addressbook_menu.x = viddef.width / 2 - 142; 3286 s_addressbook_menu.y = viddef.height / 2 - 58; 3287 s_addressbook_menu.nitems = 0; 3288 3289 for ( i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++ ) 3290 { 3291 cvar_t *adr; 3292 char buffer[20]; 3293 3294 Com_sprintf( buffer, sizeof( buffer ), "adr%d", i ); 3295 3296 adr = Cvar_Get( buffer, "", CVAR_ARCHIVE ); 3297 3298 s_addressbook_fields[i].generic.type = MTYPE_FIELD; 3299 s_addressbook_fields[i].generic.name = 0; 3300 s_addressbook_fields[i].generic.callback = 0; 3301 s_addressbook_fields[i].generic.x = 0; 3302 s_addressbook_fields[i].generic.y = i * 18 + 0; 3303 s_addressbook_fields[i].generic.localdata[0] = i; 3304 s_addressbook_fields[i].cursor = 0; 3305 s_addressbook_fields[i].length = 60; 3306 s_addressbook_fields[i].visible_length = 30; 3307 3308 strcpy( s_addressbook_fields[i].buffer, adr->string ); 3309 3310 Menu_AddItem( &s_addressbook_menu, &s_addressbook_fields[i] ); 3311 } 3312 } 3313 3314 const char *AddressBook_MenuKey( int key ) 3315 { 3316 if ( key == K_ESCAPE ) 3317 { 3318 int index; 3319 char buffer[20]; 3320 3321 for ( index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++ ) 3322 { 3323 Com_sprintf( buffer, sizeof( buffer ), "adr%d", index ); 3324 Cvar_Set( buffer, s_addressbook_fields[index].buffer ); 3325 } 3326 } 3327 return Default_MenuKey( &s_addressbook_menu, key ); 3328 } 3329 3330 void AddressBook_MenuDraw(void) 3331 { 3332 M_Banner( "m_banner_addressbook" ); 3333 Menu_Draw( &s_addressbook_menu ); 3334 } 3335 3336 void M_Menu_AddressBook_f(void) 3337 { 3338 AddressBook_MenuInit(); 3339 M_PushMenu( AddressBook_MenuDraw, AddressBook_MenuKey ); 3340 } 3341 3342 /* 3343 ============================================================================= 3344 3345 PLAYER CONFIG MENU 3346 3347 ============================================================================= 3348 */ 3349 static menuframework_s s_player_config_menu; 3350 static menufield_s s_player_name_field; 3351 static menulist_s s_player_model_box; 3352 static menulist_s s_player_skin_box; 3353 static menulist_s s_player_handedness_box; 3354 static menulist_s s_player_rate_box; 3355 static menuseparator_s s_player_skin_title; 3356 static menuseparator_s s_player_model_title; 3357 static menuseparator_s s_player_hand_title; 3358 static menuseparator_s s_player_rate_title; 3359 static menuaction_s s_player_download_action; 3360 3361 #define MAX_DISPLAYNAME 16 3362 #define MAX_PLAYERMODELS 1024 3363 3364 typedef struct 3365 { 3366 int nskins; 3367 char **skindisplaynames; 3368 char displayname[MAX_DISPLAYNAME]; 3369 char directory[MAX_QPATH]; 3370 } playermodelinfo_s; 3371 3372 static playermodelinfo_s s_pmi[MAX_PLAYERMODELS]; 3373 static char *s_pmnames[MAX_PLAYERMODELS]; 3374 static int s_numplayermodels; 3375 3376 static int rate_tbl[] = { 2500, 3200, 5000, 10000, 25000, 0 }; 3377 static const char *rate_names[] = { "28.8 Modem", "33.6 Modem", "Single ISDN", 3378 "Dual ISDN/Cable", "T1/LAN", "User defined", 0 }; 3379 3380 void DownloadOptionsFunc( void *self ) 3381 { 3382 M_Menu_DownloadOptions_f(); 3383 } 3384 3385 static void HandednessCallback( void *unused ) 3386 { 3387 Cvar_SetValue( "hand", s_player_handedness_box.curvalue ); 3388 } 3389 3390 static void RateCallback( void *unused ) 3391 { 3392 if (s_player_rate_box.curvalue != sizeof(rate_tbl) / sizeof(*rate_tbl) - 1) 3393 Cvar_SetValue( "rate", rate_tbl[s_player_rate_box.curvalue] ); 3394 } 3395 3396 static void ModelCallback( void *unused ) 3397 { 3398 s_player_skin_box.itemnames = s_pmi[s_player_model_box.curvalue].skindisplaynames; 3399 s_player_skin_box.curvalue = 0; 3400 } 3401 3402 static void FreeFileList( char **list, int n ) 3403 { 3404 int i; 3405 3406 for ( i = 0; i < n; i++ ) 3407 { 3408 if ( list[i] ) 3409 { 3410 free( list[i] ); 3411 list[i] = 0; 3412 } 3413 } 3414 free( list ); 3415 } 3416 3417 static qboolean IconOfSkinExists( char *skin, char **pcxfiles, int npcxfiles ) 3418 { 3419 int i; 3420 char scratch[1024]; 3421 3422 strcpy( scratch, skin ); 3423 *strrchr( scratch, '.' ) = 0; 3424 strcat( scratch, "_i.pcx" ); 3425 3426 for ( i = 0; i < npcxfiles; i++ ) 3427 { 3428 if ( strcmp( pcxfiles[i], scratch ) == 0 ) 3429 return true; 3430 } 3431 3432 return false; 3433 } 3434 3435 static qboolean PlayerConfig_ScanDirectories( void ) 3436 { 3437 char findname[1024]; 3438 char scratch[1024]; 3439 int ndirs = 0, npms = 0; 3440 char **dirnames; 3441 char *path = NULL; 3442 int i; 3443 3444 extern char **FS_ListFiles( char *, int *, unsigned, unsigned ); 3445 3446 s_numplayermodels = 0; 3447 3448 /* 3449 ** get a list of directories 3450 */ 3451 do 3452 { 3453 path = FS_NextPath( path ); 3454 Com_sprintf( findname, sizeof(findname), "%s/players/*.*", path ); 3455 3456 if ( ( dirnames = FS_ListFiles( findname, &ndirs, SFF_SUBDIR, 0 ) ) != 0 ) 3457 break; 3458 } while ( path ); 3459 3460 if ( !dirnames ) 3461 return false; 3462 3463 /* 3464 ** go through the subdirectories 3465 */ 3466 npms = ndirs; 3467 if ( npms > MAX_PLAYERMODELS ) 3468 npms = MAX_PLAYERMODELS; 3469 3470 for ( i = 0; i < npms; i++ ) 3471 { 3472 int k, s; 3473 char *a, *b, *c; 3474 char **pcxnames; 3475 char **skinnames; 3476 int npcxfiles; 3477 int nskins = 0; 3478 3479 if ( dirnames[i] == 0 ) 3480 continue; 3481 3482 // verify the existence of tris.md2 3483 strcpy( scratch, dirnames[i] ); 3484 strcat( scratch, "/tris.md2" ); 3485 if ( !Sys_FindFirst( scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM ) ) 3486 { 3487 free( dirnames[i] ); 3488 dirnames[i] = 0; 3489 Sys_FindClose(); 3490 continue; 3491 } 3492 Sys_FindClose(); 3493 3494 // verify the existence of at least one pcx skin 3495 strcpy( scratch, dirnames[i] ); 3496 strcat( scratch, "/*.pcx" ); 3497 pcxnames = FS_ListFiles( scratch, &npcxfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM ); 3498 3499 if ( !pcxnames ) 3500 { 3501 free( dirnames[i] ); 3502 dirnames[i] = 0; 3503 continue; 3504 } 3505 3506 // count valid skins, which consist of a skin with a matching "_i" icon 3507 for ( k = 0; k < npcxfiles-1; k++ ) 3508 { 3509 if ( !strstr( pcxnames[k], "_i.pcx" ) ) 3510 { 3511 if ( IconOfSkinExists( pcxnames[k], pcxnames, npcxfiles - 1 ) ) 3512 { 3513 nskins++; 3514 } 3515 } 3516 } 3517 if ( !nskins ) 3518 continue; 3519 3520 skinnames = malloc( sizeof( char * ) * ( nskins + 1 ) ); 3521 memset( skinnames, 0, sizeof( char * ) * ( nskins + 1 ) ); 3522 3523 // copy the valid skins 3524 for ( s = 0, k = 0; k < npcxfiles-1; k++ ) 3525 { 3526 char *a, *b, *c; 3527 3528 if ( !strstr( pcxnames[k], "_i.pcx" ) ) 3529 { 3530 if ( IconOfSkinExists( pcxnames[k], pcxnames, npcxfiles - 1 ) ) 3531 { 3532 a = strrchr( pcxnames[k], '/' ); 3533 b = strrchr( pcxnames[k], '\\' ); 3534 3535 if ( a > b ) 3536 c = a; 3537 else 3538 c = b; 3539 3540 strcpy( scratch, c + 1 ); 3541 3542 if ( strrchr( scratch, '.' ) ) 3543 *strrchr( scratch, '.' ) = 0; 3544 3545 skinnames[s] = strdup( scratch ); 3546 s++; 3547 } 3548 } 3549 } 3550 3551 // at this point we have a valid player model 3552 s_pmi[s_numplayermodels].nskins = nskins; 3553 s_pmi[s_numplayermodels].skindisplaynames = skinnames; 3554 3555 // make short name for the model 3556 a = strrchr( dirnames[i], '/' ); 3557 b = strrchr( dirnames[i], '\\' ); 3558 3559 if ( a > b ) 3560 c = a; 3561 else 3562 c = b; 3563 3564 strncpy( s_pmi[s_numplayermodels].displayname, c + 1, MAX_DISPLAYNAME-1 ); 3565 strcpy( s_pmi[s_numplayermodels].directory, c + 1 ); 3566 3567 FreeFileList( pcxnames, npcxfiles ); 3568 3569 s_numplayermodels++; 3570 } 3571 if ( dirnames ) 3572 FreeFileList( dirnames, ndirs ); 3573 } 3574 3575 static int pmicmpfnc( const void *_a, const void *_b ) 3576 { 3577 const playermodelinfo_s *a = ( const playermodelinfo_s * ) _a; 3578 const playermodelinfo_s *b = ( const playermodelinfo_s * ) _b; 3579 3580 /* 3581 ** sort by male, female, then alphabetical 3582 */ 3583 if ( strcmp( a->directory, "male" ) == 0 ) 3584 return -1; 3585 else if ( strcmp( b->directory, "male" ) == 0 ) 3586 return 1; 3587 3588 if ( strcmp( a->directory, "female" ) == 0 ) 3589 return -1; 3590 else if ( strcmp( b->directory, "female" ) == 0 ) 3591 return 1; 3592 3593 return strcmp( a->directory, b->directory ); 3594 } 3595 3596 3597 qboolean PlayerConfig_MenuInit( void ) 3598 { 3599 extern cvar_t *name; 3600 extern cvar_t *team; 3601 extern cvar_t *skin; 3602 char currentdirectory[1024]; 3603 char currentskin[1024]; 3604 int i = 0; 3605 3606 int currentdirectoryindex = 0; 3607 int currentskinindex = 0; 3608 3609 cvar_t *hand = Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE ); 3610 3611 static const char *handedness[] = { "right", "left", "center", 0 }; 3612 3613 PlayerConfig_ScanDirectories(); 3614 3615 if (s_numplayermodels == 0) 3616 return false; 3617 3618 if ( hand->value < 0 || hand->value > 2 ) 3619 Cvar_SetValue( "hand", 0 ); 3620 3621 strcpy( currentdirectory, skin->string ); 3622 3623 if ( strchr( currentdirectory, '/' ) ) 3624 { 3625 strcpy( currentskin, strchr( currentdirectory, '/' ) + 1 ); 3626 *strchr( currentdirectory, '/' ) = 0; 3627 } 3628 else if ( strchr( currentdirectory, '\\' ) ) 3629 { 3630 strcpy( currentskin, strchr( currentdirectory, '\\' ) + 1 ); 3631 *strchr( currentdirectory, '\\' ) = 0; 3632 } 3633 else 3634 { 3635 strcpy( currentdirectory, "male" ); 3636 strcpy( currentskin, "grunt" ); 3637 } 3638 3639 qsort( s_pmi, s_numplayermodels, sizeof( s_pmi[0] ), pmicmpfnc ); 3640 3641 memset( s_pmnames, 0, sizeof( s_pmnames ) ); 3642 for ( i = 0; i < s_numplayermodels; i++ ) 3643 { 3644 s_pmnames[i] = s_pmi[i].displayname; 3645 if ( Q_stricmp( s_pmi[i].directory, currentdirectory ) == 0 ) 3646 { 3647 int j; 3648 3649 currentdirectoryindex = i; 3650 3651 for ( j = 0; j < s_pmi[i].nskins; j++ ) 3652 { 3653 if ( Q_stricmp( s_pmi[i].skindisplaynames[j], currentskin ) == 0 ) 3654 { 3655 currentskinindex = j; 3656 break; 3657 } 3658 } 3659 } 3660 } 3661 3662 s_player_config_menu.x = viddef.width / 2 - 95; 3663 s_player_config_menu.y = viddef.height / 2 - 97; 3664 s_player_config_menu.nitems = 0; 3665 3666 s_player_name_field.generic.type = MTYPE_FIELD; 3667 s_player_name_field.generic.name = "name"; 3668 s_player_name_field.generic.callback = 0; 3669 s_player_name_field.generic.x = 0; 3670 s_player_name_field.generic.y = 0; 3671 s_player_name_field.length = 20; 3672 s_player_name_field.visible_length = 20; 3673 strcpy( s_player_name_field.buffer, name->string ); 3674 s_player_name_field.cursor = strlen( name->string ); 3675 3676 s_player_model_title.generic.type = MTYPE_SEPARATOR; 3677 s_player_model_title.generic.name = "model"; 3678 s_player_model_title.generic.x = -8; 3679 s_player_model_title.generic.y = 60; 3680 3681 s_player_model_box.generic.type = MTYPE_SPINCONTROL; 3682 s_player_model_box.generic.x = -56; 3683 s_player_model_box.generic.y = 70; 3684 s_player_model_box.generic.callback = ModelCallback; 3685 s_player_model_box.generic.cursor_offset = -48; 3686 s_player_model_box.curvalue = currentdirectoryindex; 3687 s_player_model_box.itemnames = s_pmnames; 3688 3689 s_player_skin_title.generic.type = MTYPE_SEPARATOR; 3690 s_player_skin_title.generic.name = "skin"; 3691 s_player_skin_title.generic.x = -16; 3692 s_player_skin_title.generic.y = 84; 3693 3694 s_player_skin_box.generic.type = MTYPE_SPINCONTROL; 3695 s_player_skin_box.generic.x = -56; 3696 s_player_skin_box.generic.y = 94; 3697 s_player_skin_box.generic.name = 0; 3698 s_player_skin_box.generic.callback = 0; 3699 s_player_skin_box.generic.cursor_offset = -48; 3700 s_player_skin_box.curvalue = currentskinindex; 3701 s_player_skin_box.itemnames = s_pmi[currentdirectoryindex].skindisplaynames; 3702 3703 s_player_hand_title.generic.type = MTYPE_SEPARATOR; 3704 s_player_hand_title.generic.name = "handedness"; 3705 s_player_hand_title.generic.x = 32; 3706 s_player_hand_title.generic.y = 108; 3707 3708 s_player_handedness_box.generic.type = MTYPE_SPINCONTROL; 3709 s_player_handedness_box.generic.x = -56; 3710 s_player_handedness_box.generic.y = 118; 3711 s_player_handedness_box.generic.name = 0; 3712 s_player_handedness_box.generic.cursor_offset = -48; 3713 s_player_handedness_box.generic.callback = HandednessCallback; 3714 s_player_handedness_box.curvalue = Cvar_VariableValue( "hand" ); 3715 s_player_handedness_box.itemnames = handedness; 3716 3717 for (i = 0; i < sizeof(rate_tbl) / sizeof(*rate_tbl) - 1; i++) 3718 if (Cvar_VariableValue("rate") == rate_tbl[i]) 3719 break; 3720 3721 s_player_rate_title.generic.type = MTYPE_SEPARATOR; 3722 s_player_rate_title.generic.name = "connect speed"; 3723 s_player_rate_title.generic.x = 56; 3724 s_player_rate_title.generic.y = 156; 3725 3726 s_player_rate_box.generic.type = MTYPE_SPINCONTROL; 3727 s_player_rate_box.generic.x = -56; 3728 s_player_rate_box.generic.y = 166; 3729 s_player_rate_box.generic.name = 0; 3730 s_player_rate_box.generic.cursor_offset = -48; 3731 s_player_rate_box.generic.callback = RateCallback; 3732 s_player_rate_box.curvalue = i; 3733 s_player_rate_box.itemnames = rate_names; 3734 3735 s_player_download_action.generic.type = MTYPE_ACTION; 3736 s_player_download_action.generic.name = "download options"; 3737 s_player_download_action.generic.flags= QMF_LEFT_JUSTIFY; 3738 s_player_download_action.generic.x = -24; 3739 s_player_download_action.generic.y = 186; 3740 s_player_download_action.generic.statusbar = NULL; 3741 s_player_download_action.generic.callback = DownloadOptionsFunc; 3742 3743 Menu_AddItem( &s_player_config_menu, &s_player_name_field ); 3744 Menu_AddItem( &s_player_config_menu, &s_player_model_title ); 3745 Menu_AddItem( &s_player_config_menu, &s_player_model_box ); 3746 if ( s_player_skin_box.itemnames ) 3747 { 3748 Menu_AddItem( &s_player_config_menu, &s_player_skin_title ); 3749 Menu_AddItem( &s_player_config_menu, &s_player_skin_box ); 3750 } 3751 Menu_AddItem( &s_player_config_menu, &s_player_hand_title ); 3752 Menu_AddItem( &s_player_config_menu, &s_player_handedness_box ); 3753 Menu_AddItem( &s_player_config_menu, &s_player_rate_title ); 3754 Menu_AddItem( &s_player_config_menu, &s_player_rate_box ); 3755 Menu_AddItem( &s_player_config_menu, &s_player_download_action ); 3756 3757 return true; 3758 } 3759 3760 void PlayerConfig_MenuDraw( void ) 3761 { 3762 extern float CalcFov( float fov_x, float w, float h ); 3763 refdef_t refdef; 3764 char scratch[MAX_QPATH]; 3765 3766 memset( &refdef, 0, sizeof( refdef ) ); 3767 3768 refdef.x = viddef.width / 2; 3769 refdef.y = viddef.height / 2 - 72; 3770 refdef.width = 144; 3771 refdef.height = 168; 3772 refdef.fov_x = 40; 3773 refdef.fov_y = CalcFov( refdef.fov_x, refdef.width, refdef.height ); 3774 refdef.time = cls.realtime*0.001; 3775 3776 if ( s_pmi[s_player_model_box.curvalue].skindisplaynames ) 3777 { 3778 static int yaw; 3779 int maxframe = 29; 3780 entity_t entity; 3781 3782 memset( &entity, 0, sizeof( entity ) ); 3783 3784 Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory ); 3785 entity.model = re.RegisterModel( scratch ); 3786 Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] ); 3787 entity.skin = re.RegisterSkin( scratch ); 3788 entity.flags = RF_FULLBRIGHT; 3789 entity.origin[0] = 80; 3790 entity.origin[1] = 0; 3791 entity.origin[2] = 0; 3792 VectorCopy( entity.origin, entity.oldorigin ); 3793 entity.frame = 0; 3794 entity.oldframe = 0; 3795 entity.backlerp = 0.0; 3796 entity.angles[1] = yaw++; 3797 if ( ++yaw > 360 ) 3798 yaw -= 360; 3799 3800 refdef.areabits = 0; 3801 refdef.num_entities = 1; 3802 refdef.entities = &entity; 3803 refdef.lightstyles = 0; 3804 refdef.rdflags = RDF_NOWORLDMODEL; 3805 3806 Menu_Draw( &s_player_config_menu ); 3807 3808 M_DrawTextBox( ( refdef.x ) * ( 320.0F / viddef.width ) - 8, ( viddef.height / 2 ) * ( 240.0F / viddef.height) - 77, refdef.width / 8, refdef.height / 8 ); 3809 refdef.height += 4; 3810 3811 re.RenderFrame( &refdef ); 3812 3813 Com_sprintf( scratch, sizeof( scratch ), "/players/%s/%s_i.pcx", 3814 s_pmi[s_player_model_box.curvalue].directory, 3815 s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] ); 3816 re.DrawPic( s_player_config_menu.x - 40, refdef.y, scratch ); 3817 } 3818 } 3819 3820 const char *PlayerConfig_MenuKey (int key) 3821 { 3822 int i; 3823 3824 if ( key == K_ESCAPE ) 3825 { 3826 char scratch[1024]; 3827 3828 Cvar_Set( "name", s_player_name_field.buffer ); 3829 3830 Com_sprintf( scratch, sizeof( scratch ), "%s/%s", 3831 s_pmi[s_player_model_box.curvalue].directory, 3832 s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] ); 3833 3834 Cvar_Set( "skin", scratch ); 3835 3836 for ( i = 0; i < s_numplayermodels; i++ ) 3837 { 3838 int j; 3839 3840 for ( j = 0; j < s_pmi[i].nskins; j++ ) 3841 { 3842 if ( s_pmi[i].skindisplaynames[j] ) 3843 free( s_pmi[i].skindisplaynames[j] ); 3844 s_pmi[i].skindisplaynames[j] = 0; 3845 } 3846 free( s_pmi[i].skindisplaynames ); 3847 s_pmi[i].skindisplaynames = 0; 3848 s_pmi[i].nskins = 0; 3849 } 3850 } 3851 return Default_MenuKey( &s_player_config_menu, key ); 3852 } 3853 3854 3855 void M_Menu_PlayerConfig_f (void) 3856 { 3857 if (!PlayerConfig_MenuInit()) 3858 { 3859 Menu_SetStatusBar( &s_multiplayer_menu, "No valid player models found" ); 3860 return; 3861 } 3862 Menu_SetStatusBar( &s_multiplayer_menu, NULL ); 3863 M_PushMenu( PlayerConfig_MenuDraw, PlayerConfig_MenuKey ); 3864 } 3865 3866 3867 /* 3868 ======================================================================= 3869 3870 GALLERY MENU 3871 3872 ======================================================================= 3873 */ 3874 #if 0
3875 void M_Menu_Gallery_f( void ) 3876 { 3877 extern void Gallery_MenuDraw( void ); 3878 extern const char *Gallery_MenuKey( int key ); 3879 3880 M_PushMenu( Gallery_MenuDraw, Gallery_MenuKey ); 3881 }
3882 #endif 3883 3884 /* 3885 ======================================================================= 3886 3887 QUIT MENU 3888 3889 ======================================================================= 3890 */ 3891 3892 const char *M_Quit_Key (int key) 3893 { 3894 switch (key) 3895 { 3896 case K_ESCAPE: 3897 case 'n': 3898 case 'N': 3899 M_PopMenu (); 3900 break; 3901 3902 case 'Y': 3903 case 'y': 3904 cls.key_dest = key_console; 3905 CL_Quit_f (); 3906 break; 3907 3908 default: 3909 break; 3910 } 3911 3912 return NULL; 3913 3914 } 3915 3916 3917 void M_Quit_Draw (void) 3918 { 3919 int w, h; 3920 3921 re.DrawGetPicSize (&w, &h, "quit"); 3922 re.DrawPic ( (viddef.width-w)/2, (viddef.height-h)/2, "quit"); 3923 } 3924 3925 3926 void M_Menu_Quit_f (void) 3927 { 3928 M_PushMenu (M_Quit_Draw, M_Quit_Key); 3929 } 3930 3931 3932 3933 //============================================================================= 3934 /* Menu Subsystem */ 3935 3936 3937 /* 3938 ================= 3939 M_Init 3940 ================= 3941 */ 3942 void M_Init (void) 3943 { 3944 Cmd_AddCommand ("menu_main", M_Menu_Main_f); 3945 Cmd_AddCommand ("menu_game", M_Menu_Game_f); 3946 Cmd_AddCommand ("menu_loadgame", M_Menu_LoadGame_f); 3947 Cmd_AddCommand ("menu_savegame", M_Menu_SaveGame_f); 3948 Cmd_AddCommand ("menu_joinserver", M_Menu_JoinServer_f); 3949 Cmd_AddCommand ("menu_addressbook", M_Menu_AddressBook_f); 3950 Cmd_AddCommand ("menu_startserver", M_Menu_StartServer_f); 3951 Cmd_AddCommand ("menu_dmoptions", M_Menu_DMOptions_f); 3952 Cmd_AddCommand ("menu_playerconfig", M_Menu_PlayerConfig_f); 3953 Cmd_AddCommand ("menu_downloadoptions", M_Menu_DownloadOptions_f); 3954 Cmd_AddCommand ("menu_credits", M_Menu_Credits_f ); 3955 Cmd_AddCommand ("menu_multiplayer", M_Menu_Multiplayer_f ); 3956 Cmd_AddCommand ("menu_video", M_Menu_Video_f); 3957 Cmd_AddCommand ("menu_options", M_Menu_Options_f); 3958 Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); 3959 Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); 3960 } 3961 3962 3963 /* 3964 ================= 3965 M_Draw 3966 ================= 3967 */ 3968 void M_Draw (void) 3969 { 3970 if (cls.key_dest != key_menu) 3971 return; 3972 3973 // repaint everything next frame 3974 SCR_DirtyScreen (); 3975 3976 // dim everything behind it down 3977 if (cl.cinematictime > 0) 3978 re.DrawFill (0,0,viddef.width, viddef.height, 0); 3979 else 3980 re.DrawFadeScreen (); 3981 3982 m_drawfunc (); 3983 3984 // delay playing the enter sound until after the 3985 // menu has been drawn, to avoid delay while 3986 // caching images 3987 if (m_entersound) 3988 { 3989 S_StartLocalSound( menu_in_sound ); 3990 m_entersound = false; 3991 } 3992 } 3993 3994 3995 /* 3996 ================= 3997 M_Keydown 3998 ================= 3999 */ 4000 void M_Keydown (int key) 4001 { 4002 const char *s; 4003 4004 if (m_keyfunc) 4005 if ( ( s = m_keyfunc( key ) ) != 0 ) 4006 S_StartLocalSound( ( char * ) s ); 4007 } 4008 4009 4010