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 ) );
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