Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ui_startserver.c (59010B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 /*
     24 =============================================================================
     25 
     26 START SERVER MENU *****
     27 
     28 =============================================================================
     29 */
     30 
     31 
     32 #include "ui_local.h"
     33 
     34 
     35 #define GAMESERVER_BACK0		"menu/art/back_0"
     36 #define GAMESERVER_BACK1		"menu/art/back_1"
     37 #define GAMESERVER_NEXT0		"menu/art/next_0"
     38 #define GAMESERVER_NEXT1		"menu/art/next_1"
     39 #define GAMESERVER_FRAMEL		"menu/art/frame2_l"
     40 #define GAMESERVER_FRAMER		"menu/art/frame1_r"
     41 #define GAMESERVER_SELECT		"menu/art/maps_select"
     42 #define GAMESERVER_SELECTED		"menu/art/maps_selected"
     43 #define GAMESERVER_FIGHT0		"menu/art/fight_0"
     44 #define GAMESERVER_FIGHT1		"menu/art/fight_1"
     45 #define GAMESERVER_UNKNOWNMAP	"menu/art/unknownmap"
     46 #define GAMESERVER_ARROWS		"menu/art/gs_arrows_0"
     47 #define GAMESERVER_ARROWSL		"menu/art/gs_arrows_l"
     48 #define GAMESERVER_ARROWSR		"menu/art/gs_arrows_r"
     49 
     50 #define MAX_MAPROWS		2
     51 #define MAX_MAPCOLS		2
     52 #define MAX_MAPSPERPAGE	4
     53 
     54 #define	MAX_SERVERSTEXT	8192
     55 
     56 #define MAX_SERVERMAPS	64
     57 #define MAX_NAMELENGTH	16
     58 
     59 #define ID_GAMETYPE				10
     60 #define ID_PICTURES				11	// 12, 13, 14
     61 #define ID_PREVPAGE				15
     62 #define ID_NEXTPAGE				16
     63 #define ID_STARTSERVERBACK		17
     64 #define ID_STARTSERVERNEXT		18
     65 
     66 typedef struct {
     67 	menuframework_s	menu;
     68 
     69 	menutext_s		banner;
     70 	menubitmap_s	framel;
     71 	menubitmap_s	framer;
     72 
     73 	menulist_s		gametype;
     74 	menubitmap_s	mappics[MAX_MAPSPERPAGE];
     75 	menubitmap_s	mapbuttons[MAX_MAPSPERPAGE];
     76 	menubitmap_s	arrows;
     77 	menubitmap_s	prevpage;
     78 	menubitmap_s	nextpage;
     79 	menubitmap_s	back;
     80 	menubitmap_s	next;
     81 
     82 	menutext_s		mapname;
     83 	menubitmap_s	item_null;
     84 
     85 	qboolean		multiplayer;
     86 	int				currentmap;
     87 	int				nummaps;
     88 	int				page;
     89 	int				maxpages;
     90 	char			maplist[MAX_SERVERMAPS][MAX_NAMELENGTH];
     91 	int				mapGamebits[MAX_SERVERMAPS];
     92 } startserver_t;
     93 
     94 static startserver_t s_startserver;
     95 
     96 static const char *gametype_items[] = {
     97 	"Free For All",
     98 	"Team Deathmatch",
     99 	"Tournament",
    100 	"Capture the Flag",
    101 	0
    102 };
    103 
    104 static int gametype_remap[] = {GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF};
    105 static int gametype_remap2[] = {0, 2, 0, 1, 3};
    106 
    107 // use ui_servers2.c definition
    108 extern const char* punkbuster_items[];
    109 
    110 static void UI_ServerOptionsMenu( qboolean multiplayer );
    111 
    112 
    113 /*
    114 =================
    115 GametypeBits
    116 =================
    117 */
    118 static int GametypeBits( char *string ) {
    119 	int		bits;
    120 	char	*p;
    121 	char	*token;
    122 
    123 	bits = 0;
    124 	p = string;
    125 	while( 1 ) {
    126 		token = COM_ParseExt( &p, qfalse );
    127 		if( token[0] == 0 ) {
    128 			break;
    129 		}
    130 
    131 		if( Q_stricmp( token, "ffa" ) == 0 ) {
    132 			bits |= 1 << GT_FFA;
    133 			continue;
    134 		}
    135 
    136 		if( Q_stricmp( token, "tourney" ) == 0 ) {
    137 			bits |= 1 << GT_TOURNAMENT;
    138 			continue;
    139 		}
    140 
    141 		if( Q_stricmp( token, "single" ) == 0 ) {
    142 			bits |= 1 << GT_SINGLE_PLAYER;
    143 			continue;
    144 		}
    145 
    146 		if( Q_stricmp( token, "team" ) == 0 ) {
    147 			bits |= 1 << GT_TEAM;
    148 			continue;
    149 		}
    150 
    151 		if( Q_stricmp( token, "ctf" ) == 0 ) {
    152 			bits |= 1 << GT_CTF;
    153 			continue;
    154 		}
    155 	}
    156 
    157 	return bits;
    158 }
    159 
    160 
    161 /*
    162 =================
    163 StartServer_Update
    164 =================
    165 */
    166 static void StartServer_Update( void ) {
    167 	int				i;
    168 	int				top;
    169 	static	char	picname[MAX_MAPSPERPAGE][64];
    170 
    171 	top = s_startserver.page*MAX_MAPSPERPAGE;
    172 
    173 	for (i=0; i<MAX_MAPSPERPAGE; i++)
    174 	{
    175 		if (top+i >= s_startserver.nummaps)
    176 			break;
    177 
    178 		Com_sprintf( picname[i], sizeof(picname[i]), "levelshots/%s", s_startserver.maplist[top+i] );
    179 
    180 		s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
    181 		s_startserver.mappics[i].generic.name   = picname[i];
    182 		s_startserver.mappics[i].shader         = 0;
    183 
    184 		// reset
    185 		s_startserver.mapbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
    186 		s_startserver.mapbuttons[i].generic.flags &= ~QMF_INACTIVE;
    187 	}
    188 
    189 	for (; i<MAX_MAPSPERPAGE; i++)
    190 	{
    191 		s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
    192 		s_startserver.mappics[i].generic.name   = NULL;
    193 		s_startserver.mappics[i].shader         = 0;
    194 
    195 		// disable
    196 		s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
    197 		s_startserver.mapbuttons[i].generic.flags |= QMF_INACTIVE;
    198 	}
    199 
    200 
    201 	// no servers to start
    202 	if( !s_startserver.nummaps ) {
    203 		s_startserver.next.generic.flags |= QMF_INACTIVE;
    204 
    205 		// set the map name
    206 		strcpy( s_startserver.mapname.string, "NO MAPS FOUND" );
    207 	}
    208 	else {
    209 		// set the highlight
    210 		s_startserver.next.generic.flags &= ~QMF_INACTIVE;
    211 		i = s_startserver.currentmap - top;
    212 		if ( i >=0 && i < MAX_MAPSPERPAGE ) 
    213 		{
    214 			s_startserver.mappics[i].generic.flags    |= QMF_HIGHLIGHT;
    215 			s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
    216 		}
    217 
    218 		// set the map name
    219 		strcpy( s_startserver.mapname.string, s_startserver.maplist[s_startserver.currentmap] );
    220 	}
    221 	
    222 	Q_strupr( s_startserver.mapname.string );
    223 }
    224 
    225 
    226 /*
    227 =================
    228 StartServer_MapEvent
    229 =================
    230 */
    231 static void StartServer_MapEvent( void* ptr, int event ) {
    232 	if( event != QM_ACTIVATED) {
    233 		return;
    234 	}
    235 
    236 	s_startserver.currentmap = (s_startserver.page*MAX_MAPSPERPAGE) + (((menucommon_s*)ptr)->id - ID_PICTURES);
    237 	StartServer_Update();
    238 }
    239 
    240 
    241 /*
    242 =================
    243 StartServer_GametypeEvent
    244 =================
    245 */
    246 static void StartServer_GametypeEvent( void* ptr, int event ) {
    247 	int			i;
    248 	int			count;
    249 	int			gamebits;
    250 	int			matchbits;
    251 	const char	*info;
    252 
    253 	if( event != QM_ACTIVATED) {
    254 		return;
    255 	}
    256 
    257 	count = UI_GetNumArenas();
    258 	s_startserver.nummaps = 0;
    259 	matchbits = 1 << gametype_remap[s_startserver.gametype.curvalue];
    260 	if( gametype_remap[s_startserver.gametype.curvalue] == GT_FFA ) {
    261 		matchbits |= ( 1 << GT_SINGLE_PLAYER );
    262 	}
    263 	for( i = 0; i < count; i++ ) {
    264 		info = UI_GetArenaInfoByNumber( i );
    265 
    266 		gamebits = GametypeBits( Info_ValueForKey( info, "type") );
    267 		if( !( gamebits & matchbits ) ) {
    268 			continue;
    269 		}
    270 
    271 		Q_strncpyz( s_startserver.maplist[s_startserver.nummaps], Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
    272 		Q_strupr( s_startserver.maplist[s_startserver.nummaps] );
    273 		s_startserver.mapGamebits[s_startserver.nummaps] = gamebits;
    274 		s_startserver.nummaps++;
    275 	}
    276 	s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE;
    277 	s_startserver.page = 0;
    278 	s_startserver.currentmap = 0;
    279 
    280 	StartServer_Update();
    281 }
    282 
    283 
    284 /*
    285 =================
    286 StartServer_MenuEvent
    287 =================
    288 */
    289 static void StartServer_MenuEvent( void* ptr, int event ) {
    290 	if( event != QM_ACTIVATED ) {
    291 		return;
    292 	}
    293 
    294 	switch( ((menucommon_s*)ptr)->id ) {
    295 	case ID_PREVPAGE:
    296 		if( s_startserver.page > 0 ) {
    297 			s_startserver.page--;
    298 			StartServer_Update();
    299 		}
    300 		break;
    301 
    302 	case ID_NEXTPAGE:
    303 		if( s_startserver.page < s_startserver.maxpages - 1 ) {
    304 			s_startserver.page++;
    305 			StartServer_Update();
    306 		}
    307 		break;
    308 
    309 	case ID_STARTSERVERNEXT:
    310 		trap_Cvar_SetValue( "g_gameType", gametype_remap[s_startserver.gametype.curvalue] );
    311 		UI_ServerOptionsMenu( s_startserver.multiplayer );
    312 		break;
    313 
    314 	case ID_STARTSERVERBACK:
    315 		UI_PopMenu();
    316 		break;
    317 	}
    318 }
    319 
    320 
    321 /*
    322 ===============
    323 StartServer_LevelshotDraw
    324 ===============
    325 */
    326 static void StartServer_LevelshotDraw( void *self ) {
    327 	menubitmap_s	*b;
    328 	int				x;
    329 	int				y;
    330 	int				w;
    331 	int				h;
    332 	int				n;
    333 
    334 	b = (menubitmap_s *)self;
    335 
    336 	if( !b->generic.name ) {
    337 		return;
    338 	}
    339 
    340 	if( b->generic.name && !b->shader ) {
    341 		b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
    342 		if( !b->shader && b->errorpic ) {
    343 			b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
    344 		}
    345 	}
    346 
    347 	if( b->focuspic && !b->focusshader ) {
    348 		b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
    349 	}
    350 
    351 	x = b->generic.x;
    352 	y = b->generic.y;
    353 	w = b->width;
    354 	h =	b->height;
    355 	if( b->shader ) {
    356 		UI_DrawHandlePic( x, y, w, h, b->shader );
    357 	}
    358 
    359 	x = b->generic.x;
    360 	y = b->generic.y + b->height;
    361 	UI_FillRect( x, y, b->width, 28, colorBlack );
    362 
    363 	x += b->width / 2;
    364 	y += 4;
    365 	n = s_startserver.page * MAX_MAPSPERPAGE + b->generic.id - ID_PICTURES;
    366 	UI_DrawString( x, y, s_startserver.maplist[n], UI_CENTER|UI_SMALLFONT, color_orange );
    367 
    368 	x = b->generic.x;
    369 	y = b->generic.y;
    370 	w = b->width;
    371 	h =	b->height + 28;
    372 	if( b->generic.flags & QMF_HIGHLIGHT ) {	
    373 		UI_DrawHandlePic( x, y, w, h, b->focusshader );
    374 	}
    375 }
    376 
    377 
    378 /*
    379 =================
    380 StartServer_MenuInit
    381 =================
    382 */
    383 static void StartServer_MenuInit( void ) {
    384 	int	i;
    385 	int	x;
    386 	int	y;
    387 	static char mapnamebuffer[64];
    388 
    389 	// zero set all our globals
    390 	memset( &s_startserver, 0 ,sizeof(startserver_t) );
    391 
    392 	StartServer_Cache();
    393 
    394 	s_startserver.menu.wrapAround = qtrue;
    395 	s_startserver.menu.fullscreen = qtrue;
    396 
    397 	s_startserver.banner.generic.type  = MTYPE_BTEXT;
    398 	s_startserver.banner.generic.x	   = 320;
    399 	s_startserver.banner.generic.y	   = 16;
    400 	s_startserver.banner.string        = "GAME SERVER";
    401 	s_startserver.banner.color         = color_white;
    402 	s_startserver.banner.style         = UI_CENTER;
    403 
    404 	s_startserver.framel.generic.type  = MTYPE_BITMAP;
    405 	s_startserver.framel.generic.name  = GAMESERVER_FRAMEL;
    406 	s_startserver.framel.generic.flags = QMF_INACTIVE;
    407 	s_startserver.framel.generic.x	   = 0;  
    408 	s_startserver.framel.generic.y	   = 78;
    409 	s_startserver.framel.width  	   = 256;
    410 	s_startserver.framel.height  	   = 329;
    411 
    412 	s_startserver.framer.generic.type  = MTYPE_BITMAP;
    413 	s_startserver.framer.generic.name  = GAMESERVER_FRAMER;
    414 	s_startserver.framer.generic.flags = QMF_INACTIVE;
    415 	s_startserver.framer.generic.x	   = 376;
    416 	s_startserver.framer.generic.y	   = 76;
    417 	s_startserver.framer.width  	   = 256;
    418 	s_startserver.framer.height  	   = 334;
    419 
    420 	s_startserver.gametype.generic.type		= MTYPE_SPINCONTROL;
    421 	s_startserver.gametype.generic.name		= "Game Type:";
    422 	s_startserver.gametype.generic.flags	= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
    423 	s_startserver.gametype.generic.callback	= StartServer_GametypeEvent;
    424 	s_startserver.gametype.generic.id		= ID_GAMETYPE;
    425 	s_startserver.gametype.generic.x		= 320 - 24;
    426 	s_startserver.gametype.generic.y		= 368;
    427 	s_startserver.gametype.itemnames		= gametype_items;
    428 
    429 	for (i=0; i<MAX_MAPSPERPAGE; i++)
    430 	{
    431 		x =	(i % MAX_MAPCOLS) * (128+8) + 188;
    432 		y = (i / MAX_MAPROWS) * (128+8) + 96;
    433 
    434 		s_startserver.mappics[i].generic.type   = MTYPE_BITMAP;
    435 		s_startserver.mappics[i].generic.flags  = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
    436 		s_startserver.mappics[i].generic.x	    = x;
    437 		s_startserver.mappics[i].generic.y	    = y;
    438 		s_startserver.mappics[i].generic.id		= ID_PICTURES+i;
    439 		s_startserver.mappics[i].width  		= 128;
    440 		s_startserver.mappics[i].height  	    = 96;
    441 		s_startserver.mappics[i].focuspic       = GAMESERVER_SELECTED;
    442 		s_startserver.mappics[i].errorpic       = GAMESERVER_UNKNOWNMAP;
    443 		s_startserver.mappics[i].generic.ownerdraw = StartServer_LevelshotDraw;
    444 
    445 		s_startserver.mapbuttons[i].generic.type     = MTYPE_BITMAP;
    446 		s_startserver.mapbuttons[i].generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_NODEFAULTINIT;
    447 		s_startserver.mapbuttons[i].generic.id       = ID_PICTURES+i;
    448 		s_startserver.mapbuttons[i].generic.callback = StartServer_MapEvent;
    449 		s_startserver.mapbuttons[i].generic.x	     = x - 30;
    450 		s_startserver.mapbuttons[i].generic.y	     = y - 32;
    451 		s_startserver.mapbuttons[i].width  		     = 256;
    452 		s_startserver.mapbuttons[i].height  	     = 248;
    453 		s_startserver.mapbuttons[i].generic.left     = x;
    454 		s_startserver.mapbuttons[i].generic.top  	 = y;
    455 		s_startserver.mapbuttons[i].generic.right    = x + 128;
    456 		s_startserver.mapbuttons[i].generic.bottom   = y + 128;
    457 		s_startserver.mapbuttons[i].focuspic         = GAMESERVER_SELECT;
    458 	}
    459 
    460 	s_startserver.arrows.generic.type  = MTYPE_BITMAP;
    461 	s_startserver.arrows.generic.name  = GAMESERVER_ARROWS;
    462 	s_startserver.arrows.generic.flags = QMF_INACTIVE;
    463 	s_startserver.arrows.generic.x	   = 260;
    464 	s_startserver.arrows.generic.y	   = 400;
    465 	s_startserver.arrows.width  	   = 128;
    466 	s_startserver.arrows.height  	   = 32;
    467 
    468 	s_startserver.prevpage.generic.type	    = MTYPE_BITMAP;
    469 	s_startserver.prevpage.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    470 	s_startserver.prevpage.generic.callback = StartServer_MenuEvent;
    471 	s_startserver.prevpage.generic.id	    = ID_PREVPAGE;
    472 	s_startserver.prevpage.generic.x		= 260;
    473 	s_startserver.prevpage.generic.y		= 400;
    474 	s_startserver.prevpage.width  		    = 64;
    475 	s_startserver.prevpage.height  		    = 32;
    476 	s_startserver.prevpage.focuspic         = GAMESERVER_ARROWSL;
    477 
    478 	s_startserver.nextpage.generic.type	    = MTYPE_BITMAP;
    479 	s_startserver.nextpage.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    480 	s_startserver.nextpage.generic.callback = StartServer_MenuEvent;
    481 	s_startserver.nextpage.generic.id	    = ID_NEXTPAGE;
    482 	s_startserver.nextpage.generic.x		= 321;
    483 	s_startserver.nextpage.generic.y		= 400;
    484 	s_startserver.nextpage.width  		    = 64;
    485 	s_startserver.nextpage.height  		    = 32;
    486 	s_startserver.nextpage.focuspic         = GAMESERVER_ARROWSR;
    487 
    488 	s_startserver.mapname.generic.type  = MTYPE_PTEXT;
    489 	s_startserver.mapname.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
    490 	s_startserver.mapname.generic.x	    = 320;
    491 	s_startserver.mapname.generic.y	    = 440;
    492 	s_startserver.mapname.string        = mapnamebuffer;
    493 	s_startserver.mapname.style         = UI_CENTER|UI_BIGFONT;
    494 	s_startserver.mapname.color         = text_color_normal;
    495 
    496 	s_startserver.back.generic.type	    = MTYPE_BITMAP;
    497 	s_startserver.back.generic.name     = GAMESERVER_BACK0;
    498 	s_startserver.back.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    499 	s_startserver.back.generic.callback = StartServer_MenuEvent;
    500 	s_startserver.back.generic.id	    = ID_STARTSERVERBACK;
    501 	s_startserver.back.generic.x		= 0;
    502 	s_startserver.back.generic.y		= 480-64;
    503 	s_startserver.back.width  		    = 128;
    504 	s_startserver.back.height  		    = 64;
    505 	s_startserver.back.focuspic         = GAMESERVER_BACK1;
    506 
    507 	s_startserver.next.generic.type	    = MTYPE_BITMAP;
    508 	s_startserver.next.generic.name     = GAMESERVER_NEXT0;
    509 	s_startserver.next.generic.flags    = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
    510 	s_startserver.next.generic.callback = StartServer_MenuEvent;
    511 	s_startserver.next.generic.id	    = ID_STARTSERVERNEXT;
    512 	s_startserver.next.generic.x		= 640;
    513 	s_startserver.next.generic.y		= 480-64;
    514 	s_startserver.next.width  		    = 128;
    515 	s_startserver.next.height  		    = 64;
    516 	s_startserver.next.focuspic         = GAMESERVER_NEXT1;
    517 
    518 	s_startserver.item_null.generic.type	= MTYPE_BITMAP;
    519 	s_startserver.item_null.generic.flags	= QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
    520 	s_startserver.item_null.generic.x		= 0;
    521 	s_startserver.item_null.generic.y		= 0;
    522 	s_startserver.item_null.width			= 640;
    523 	s_startserver.item_null.height			= 480;
    524 
    525 	Menu_AddItem( &s_startserver.menu, &s_startserver.banner );
    526 	Menu_AddItem( &s_startserver.menu, &s_startserver.framel );
    527 	Menu_AddItem( &s_startserver.menu, &s_startserver.framer );
    528 
    529 	Menu_AddItem( &s_startserver.menu, &s_startserver.gametype );
    530 	for (i=0; i<MAX_MAPSPERPAGE; i++)
    531 	{
    532 		Menu_AddItem( &s_startserver.menu, &s_startserver.mappics[i] );
    533 		Menu_AddItem( &s_startserver.menu, &s_startserver.mapbuttons[i] );
    534 	}
    535 
    536 	Menu_AddItem( &s_startserver.menu, &s_startserver.arrows );
    537 	Menu_AddItem( &s_startserver.menu, &s_startserver.prevpage );
    538 	Menu_AddItem( &s_startserver.menu, &s_startserver.nextpage );
    539 	Menu_AddItem( &s_startserver.menu, &s_startserver.back );
    540 	Menu_AddItem( &s_startserver.menu, &s_startserver.next );
    541 	Menu_AddItem( &s_startserver.menu, &s_startserver.mapname );
    542 	Menu_AddItem( &s_startserver.menu, &s_startserver.item_null );
    543 
    544 	StartServer_GametypeEvent( NULL, QM_ACTIVATED );
    545 }
    546 
    547 
    548 /*
    549 =================
    550 StartServer_Cache
    551 =================
    552 */
    553 void StartServer_Cache( void )
    554 {
    555 	int				i;
    556 	const char		*info;
    557 	qboolean		precache;
    558 	char			picname[64];
    559 
    560 	trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );	
    561 	trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );	
    562 	trap_R_RegisterShaderNoMip( GAMESERVER_NEXT0 );	
    563 	trap_R_RegisterShaderNoMip( GAMESERVER_NEXT1 );	
    564 	trap_R_RegisterShaderNoMip( GAMESERVER_FRAMEL );	
    565 	trap_R_RegisterShaderNoMip( GAMESERVER_FRAMER );	
    566 	trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );	
    567 	trap_R_RegisterShaderNoMip( GAMESERVER_SELECTED );	
    568 	trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
    569 	trap_R_RegisterShaderNoMip( GAMESERVER_ARROWS );
    570 	trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSL );
    571 	trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSR );
    572 
    573 	precache = trap_Cvar_VariableValue("com_buildscript");
    574 
    575 	s_startserver.nummaps = UI_GetNumArenas();
    576 
    577 	for( i = 0; i < s_startserver.nummaps; i++ ) {
    578 		info = UI_GetArenaInfoByNumber( i );
    579 
    580 		Q_strncpyz( s_startserver.maplist[i], Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
    581 		Q_strupr( s_startserver.maplist[i] );
    582 		s_startserver.mapGamebits[i] = GametypeBits( Info_ValueForKey( info, "type") );
    583 
    584 		if( precache ) {
    585 			Com_sprintf( picname, sizeof(picname), "levelshots/%s", s_startserver.maplist[i] );
    586 			trap_R_RegisterShaderNoMip(picname);
    587 		}
    588 	}
    589 
    590 	s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE;
    591 }
    592 
    593 
    594 /*
    595 =================
    596 UI_StartServerMenu
    597 =================
    598 */
    599 void UI_StartServerMenu( qboolean multiplayer ) {
    600 	StartServer_MenuInit();
    601 	s_startserver.multiplayer = multiplayer;
    602 	UI_PushMenu( &s_startserver.menu );
    603 }
    604 
    605 
    606 
    607 /*
    608 =============================================================================
    609 
    610 SERVER OPTIONS MENU *****
    611 
    612 =============================================================================
    613 */
    614 
    615 #define ID_PLAYER_TYPE			20
    616 #define ID_MAXCLIENTS			21
    617 #define ID_DEDICATED			22
    618 #define ID_GO					23
    619 #define ID_BACK					24
    620 
    621 #define PLAYER_SLOTS			12
    622 
    623 
    624 typedef struct {
    625 	menuframework_s		menu;
    626 
    627 	menutext_s			banner;
    628 
    629 	menubitmap_s		mappic;
    630 	menubitmap_s		picframe;
    631 
    632 	menulist_s			dedicated;
    633 	menufield_s			timelimit;
    634 	menufield_s			fraglimit;
    635 	menufield_s			flaglimit;
    636 	menuradiobutton_s	friendlyfire;
    637 	menufield_s			hostname;
    638 	menuradiobutton_s	pure;
    639 	menulist_s			botSkill;
    640 
    641 	menutext_s			player0;
    642 	menulist_s			playerType[PLAYER_SLOTS];
    643 	menutext_s			playerName[PLAYER_SLOTS];
    644 	menulist_s			playerTeam[PLAYER_SLOTS];
    645 
    646 	menubitmap_s		go;
    647 	menubitmap_s		next;
    648 	menubitmap_s		back;
    649 
    650 	qboolean			multiplayer;
    651 	int					gametype;
    652 	char				mapnamebuffer[32];
    653 	char				playerNameBuffers[PLAYER_SLOTS][16];
    654 
    655 	qboolean			newBot;
    656 	int					newBotIndex;
    657 	char				newBotName[16];
    658 	
    659 	menulist_s		punkbuster;
    660 } serveroptions_t;
    661 
    662 static serveroptions_t s_serveroptions;
    663 
    664 static const char *dedicated_list[] = {
    665 	"No",
    666 	"LAN",
    667 	"Internet",
    668 	0
    669 };
    670 
    671 static const char *playerType_list[] = {
    672 	"Open",
    673 	"Bot",
    674 	"----",
    675 	0
    676 };
    677 
    678 static const char *playerTeam_list[] = {
    679 	"Blue",
    680 	"Red",
    681 	0
    682 };
    683 
    684 static const char *botSkill_list[] = {
    685 	"I Can Win",
    686 	"Bring It On",
    687 	"Hurt Me Plenty",
    688 	"Hardcore",
    689 	"Nightmare!",
    690 	0
    691 };
    692 
    693 
    694 /*
    695 =================
    696 BotAlreadySelected
    697 =================
    698 */
    699 static qboolean BotAlreadySelected( const char *checkName ) {
    700 	int		n;
    701 
    702 	for( n = 1; n < PLAYER_SLOTS; n++ ) {
    703 		if( s_serveroptions.playerType[n].curvalue != 1 ) {
    704 			continue;
    705 		}
    706 		if( (s_serveroptions.gametype >= GT_TEAM) &&
    707 			(s_serveroptions.playerTeam[n].curvalue != s_serveroptions.playerTeam[s_serveroptions.newBotIndex].curvalue ) ) {
    708 			continue;
    709 		}
    710 		if( Q_stricmp( checkName, s_serveroptions.playerNameBuffers[n] ) == 0 ) {
    711 			return qtrue;
    712 		}
    713 	}
    714 
    715 	return qfalse;
    716 }
    717 
    718 
    719 /*
    720 =================
    721 ServerOptions_Start
    722 =================
    723 */
    724 static void ServerOptions_Start( void ) {
    725 	int		timelimit;
    726 	int		fraglimit;
    727 	int		maxclients;
    728 	int		dedicated;
    729 	int		friendlyfire;
    730 	int		flaglimit;
    731 	int		pure;
    732 	int		skill;
    733 	int		n;
    734 	char	buf[64];
    735 
    736 
    737 	timelimit	 = atoi( s_serveroptions.timelimit.field.buffer );
    738 	fraglimit	 = atoi( s_serveroptions.fraglimit.field.buffer );
    739 	flaglimit	 = atoi( s_serveroptions.flaglimit.field.buffer );
    740 	dedicated	 = s_serveroptions.dedicated.curvalue;
    741 	friendlyfire = s_serveroptions.friendlyfire.curvalue;
    742 	pure		 = s_serveroptions.pure.curvalue;
    743 	skill		 = s_serveroptions.botSkill.curvalue + 1;
    744 
    745 	//set maxclients
    746 	for( n = 0, maxclients = 0; n < PLAYER_SLOTS; n++ ) {
    747 		if( s_serveroptions.playerType[n].curvalue == 2 ) {
    748 			continue;
    749 		}
    750 		if( (s_serveroptions.playerType[n].curvalue == 1) && (s_serveroptions.playerNameBuffers[n][0] == 0) ) {
    751 			continue;
    752 		}
    753 		maxclients++;
    754 	}
    755 
    756 	switch( s_serveroptions.gametype ) {
    757 	case GT_FFA:
    758 	default:
    759 		trap_Cvar_SetValue( "ui_ffa_fraglimit", fraglimit );
    760 		trap_Cvar_SetValue( "ui_ffa_timelimit", timelimit );
    761 		break;
    762 
    763 	case GT_TOURNAMENT:
    764 		trap_Cvar_SetValue( "ui_tourney_fraglimit", fraglimit );
    765 		trap_Cvar_SetValue( "ui_tourney_timelimit", timelimit );
    766 		break;
    767 
    768 	case GT_TEAM:
    769 		trap_Cvar_SetValue( "ui_team_fraglimit", fraglimit );
    770 		trap_Cvar_SetValue( "ui_team_timelimit", timelimit );
    771 		trap_Cvar_SetValue( "ui_team_friendlt", friendlyfire );
    772 		break;
    773 
    774 	case GT_CTF:
    775 		trap_Cvar_SetValue( "ui_ctf_fraglimit", fraglimit );
    776 		trap_Cvar_SetValue( "ui_ctf_timelimit", timelimit );
    777 		trap_Cvar_SetValue( "ui_ctf_friendlt", friendlyfire );
    778 		break;
    779 	}
    780 
    781 	trap_Cvar_SetValue( "sv_maxclients", Com_Clamp( 0, 12, maxclients ) );
    782 	trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, dedicated ) );
    783 	trap_Cvar_SetValue ("timelimit", Com_Clamp( 0, timelimit, timelimit ) );
    784 	trap_Cvar_SetValue ("fraglimit", Com_Clamp( 0, fraglimit, fraglimit ) );
    785 	trap_Cvar_SetValue ("capturelimit", Com_Clamp( 0, flaglimit, flaglimit ) );
    786 	trap_Cvar_SetValue( "g_friendlyfire", friendlyfire );
    787 	trap_Cvar_SetValue( "sv_pure", pure );
    788 	trap_Cvar_Set("sv_hostname", s_serveroptions.hostname.field.buffer );
    789 	
    790 	trap_Cvar_SetValue( "sv_punkbuster", s_serveroptions.punkbuster.curvalue );
    791 
    792 	// the wait commands will allow the dedicated to take effect
    793 	trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", s_startserver.maplist[s_startserver.currentmap] ) );
    794 
    795 	// add bots
    796 	trap_Cmd_ExecuteText( EXEC_APPEND, "wait 3\n" );
    797 	for( n = 1; n < PLAYER_SLOTS; n++ ) {
    798 		if( s_serveroptions.playerType[n].curvalue != 1 ) {
    799 			continue;
    800 		}
    801 		if( s_serveroptions.playerNameBuffers[n][0] == 0 ) {
    802 			continue;
    803 		}
    804 		if( s_serveroptions.playerNameBuffers[n][0] == '-' ) {
    805 			continue;
    806 		}
    807 		if( s_serveroptions.gametype >= GT_TEAM ) {
    808 			Com_sprintf( buf, sizeof(buf), "addbot %s %i %s\n", s_serveroptions.playerNameBuffers[n], skill,
    809 				playerTeam_list[s_serveroptions.playerTeam[n].curvalue] );
    810 		}
    811 		else {
    812 			Com_sprintf( buf, sizeof(buf), "addbot %s %i\n", s_serveroptions.playerNameBuffers[n], skill );
    813 		}
    814 		trap_Cmd_ExecuteText( EXEC_APPEND, buf );
    815 	}
    816 
    817 	// set player's team
    818 	if( dedicated == 0 && s_serveroptions.gametype >= GT_TEAM ) {
    819 		trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait 5; team %s\n", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ) );
    820 	}
    821 }
    822 
    823 
    824 /*
    825 =================
    826 ServerOptions_InitPlayerItems
    827 =================
    828 */
    829 static void ServerOptions_InitPlayerItems( void ) {
    830 	int		n;
    831 	int		v;
    832 
    833 	// init types
    834 	if( s_serveroptions.multiplayer ) {
    835 		v = 0;	// open
    836 	}
    837 	else {
    838 		v = 1;	// bot
    839 	}
    840 	
    841 	for( n = 0; n < PLAYER_SLOTS; n++ ) {
    842 		s_serveroptions.playerType[n].curvalue = v;
    843 	}
    844 
    845 	if( s_serveroptions.multiplayer && (s_serveroptions.gametype < GT_TEAM) ) {
    846 		for( n = 8; n < PLAYER_SLOTS; n++ ) {
    847 			s_serveroptions.playerType[n].curvalue = 2;
    848 		}
    849 	}
    850 
    851 	// if not a dedicated server, first slot is reserved for the human on the server
    852 	if( s_serveroptions.dedicated.curvalue == 0 ) {
    853 		// human
    854 		s_serveroptions.playerType[0].generic.flags |= QMF_INACTIVE;
    855 		s_serveroptions.playerType[0].curvalue = 0;
    856 		trap_Cvar_VariableStringBuffer( "name", s_serveroptions.playerNameBuffers[0], sizeof(s_serveroptions.playerNameBuffers[0]) );
    857 		Q_CleanStr( s_serveroptions.playerNameBuffers[0] );
    858 	}
    859 
    860 	// init teams
    861 	if( s_serveroptions.gametype >= GT_TEAM ) {
    862 		for( n = 0; n < (PLAYER_SLOTS / 2); n++ ) {
    863 			s_serveroptions.playerTeam[n].curvalue = 0;
    864 		}
    865 		for( ; n < PLAYER_SLOTS; n++ ) {
    866 			s_serveroptions.playerTeam[n].curvalue = 1;
    867 		}
    868 	}
    869 	else {
    870 		for( n = 0; n < PLAYER_SLOTS; n++ ) {
    871 			s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
    872 		}
    873 	}
    874 }
    875 
    876 
    877 /*
    878 =================
    879 ServerOptions_SetPlayerItems
    880 =================
    881 */
    882 static void ServerOptions_SetPlayerItems( void ) {
    883 	int		start;
    884 	int		n;
    885 
    886 	// types
    887 //	for( n = 0; n < PLAYER_SLOTS; n++ ) {
    888 //		if( (!s_serveroptions.multiplayer) && (n > 0) && (s_serveroptions.playerType[n].curvalue == 0) ) {
    889 //			s_serveroptions.playerType[n].curvalue = 1;
    890 //		}
    891 //	}
    892 
    893 	// names
    894 	if( s_serveroptions.dedicated.curvalue == 0 ) {
    895 		s_serveroptions.player0.string = "Human";
    896 		s_serveroptions.playerName[0].generic.flags &= ~QMF_HIDDEN;
    897 
    898 		start = 1;
    899 	}
    900 	else {
    901 		s_serveroptions.player0.string = "Open";
    902 		start = 0;
    903 	}
    904 	for( n = start; n < PLAYER_SLOTS; n++ ) {
    905 		if( s_serveroptions.playerType[n].curvalue == 1 ) {
    906 			s_serveroptions.playerName[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
    907 		}
    908 		else {
    909 			s_serveroptions.playerName[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
    910 		}
    911 	}
    912 
    913 	// teams
    914 	if( s_serveroptions.gametype < GT_TEAM ) {
    915 		return;
    916 	}
    917 	for( n = start; n < PLAYER_SLOTS; n++ ) {
    918 		if( s_serveroptions.playerType[n].curvalue == 2 ) {
    919 			s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
    920 		}
    921 		else {
    922 			s_serveroptions.playerTeam[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
    923 		}
    924 	}
    925 }
    926 
    927 
    928 /*
    929 =================
    930 ServerOptions_Event
    931 =================
    932 */
    933 static void ServerOptions_Event( void* ptr, int event ) {
    934 	switch( ((menucommon_s*)ptr)->id ) {
    935 	
    936 	//if( event != QM_ACTIVATED && event != QM_LOSTFOCUS) {
    937 	//	return;
    938 	//}
    939 	case ID_PLAYER_TYPE:
    940 		if( event != QM_ACTIVATED ) {
    941 			break;
    942 		}
    943 		ServerOptions_SetPlayerItems();
    944 		break;
    945 
    946 	case ID_MAXCLIENTS:
    947 	case ID_DEDICATED:
    948 		ServerOptions_SetPlayerItems();
    949 		break;
    950 	case ID_GO:
    951 		if( event != QM_ACTIVATED ) {
    952 			break;
    953 		}
    954 		ServerOptions_Start();
    955 		break;
    956 
    957 	case ID_STARTSERVERNEXT:
    958 		if( event != QM_ACTIVATED ) {
    959 			break;
    960 		}
    961 		break;
    962 	case ID_BACK:
    963 		if( event != QM_ACTIVATED ) {
    964 			break;
    965 		}
    966 		UI_PopMenu();
    967 		break;
    968 	}
    969 }
    970 
    971 
    972 static void ServerOptions_PlayerNameEvent( void* ptr, int event ) {
    973 	int		n;
    974 
    975 	if( event != QM_ACTIVATED ) {
    976 		return;
    977 	}
    978 	n = ((menutext_s*)ptr)->generic.id;
    979 	s_serveroptions.newBotIndex = n;
    980 	UI_BotSelectMenu( s_serveroptions.playerNameBuffers[n] );
    981 }
    982 
    983 
    984 /*
    985 =================
    986 ServerOptions_StatusBar
    987 =================
    988 */
    989 static void ServerOptions_StatusBar( void* ptr ) {
    990 	switch( ((menucommon_s*)ptr)->id ) {
    991 	default:
    992 		UI_DrawString( 320, 440, "0 = NO LIMIT", UI_CENTER|UI_SMALLFONT, colorWhite );
    993 		break;
    994 	}
    995 }
    996 
    997 
    998 /*
    999 ===============
   1000 ServerOptions_LevelshotDraw
   1001 ===============
   1002 */
   1003 static void ServerOptions_LevelshotDraw( void *self ) {
   1004 	menubitmap_s	*b;
   1005 	int				x;
   1006 	int				y;
   1007 
   1008 	// strange place for this, but it works
   1009 	if( s_serveroptions.newBot ) {
   1010 		Q_strncpyz( s_serveroptions.playerNameBuffers[s_serveroptions.newBotIndex], s_serveroptions.newBotName, 16 );
   1011 		s_serveroptions.newBot = qfalse;
   1012 	}
   1013 
   1014 	b = (menubitmap_s *)self;
   1015 
   1016 	Bitmap_Draw( b );
   1017 
   1018 	x = b->generic.x;
   1019 	y = b->generic.y + b->height;
   1020 	UI_FillRect( x, y, b->width, 40, colorBlack );
   1021 
   1022 	x += b->width / 2;
   1023 	y += 4;
   1024 	UI_DrawString( x, y, s_serveroptions.mapnamebuffer, UI_CENTER|UI_SMALLFONT, color_orange );
   1025 
   1026 	y += SMALLCHAR_HEIGHT;
   1027 	UI_DrawString( x, y, gametype_items[gametype_remap2[s_serveroptions.gametype]], UI_CENTER|UI_SMALLFONT, color_orange );
   1028 }
   1029 
   1030 
   1031 static void ServerOptions_InitBotNames( void ) {
   1032 	int			count;
   1033 	int			n;
   1034 	const char	*arenaInfo;
   1035 	const char	*botInfo;
   1036 	char		*p;
   1037 	char		*bot;
   1038 	char		bots[MAX_INFO_STRING];
   1039 
   1040 	if( s_serveroptions.gametype >= GT_TEAM ) {
   1041 		Q_strncpyz( s_serveroptions.playerNameBuffers[1], "grunt", 16 );
   1042 		Q_strncpyz( s_serveroptions.playerNameBuffers[2], "major", 16 );
   1043 		if( s_serveroptions.gametype == GT_TEAM ) {
   1044 			Q_strncpyz( s_serveroptions.playerNameBuffers[3], "visor", 16 );
   1045 		}
   1046 		else {
   1047 			s_serveroptions.playerType[3].curvalue = 2;
   1048 		}
   1049 		s_serveroptions.playerType[4].curvalue = 2;
   1050 		s_serveroptions.playerType[5].curvalue = 2;
   1051 
   1052 		Q_strncpyz( s_serveroptions.playerNameBuffers[6], "sarge", 16 );
   1053 		Q_strncpyz( s_serveroptions.playerNameBuffers[7], "grunt", 16 );
   1054 		Q_strncpyz( s_serveroptions.playerNameBuffers[8], "major", 16 );
   1055 		if( s_serveroptions.gametype == GT_TEAM ) {
   1056 			Q_strncpyz( s_serveroptions.playerNameBuffers[9], "visor", 16 );
   1057 		}
   1058 		else {
   1059 			s_serveroptions.playerType[9].curvalue = 2;
   1060 		}
   1061 		s_serveroptions.playerType[10].curvalue = 2;
   1062 		s_serveroptions.playerType[11].curvalue = 2;
   1063 
   1064 		return;
   1065 	}
   1066 
   1067 	count = 1;	// skip the first slot, reserved for a human
   1068 
   1069 	// get info for this map
   1070 	arenaInfo = UI_GetArenaInfoByMap( s_serveroptions.mapnamebuffer );
   1071 
   1072 	// get the bot info - we'll seed with them if any are listed
   1073 	Q_strncpyz( bots, Info_ValueForKey( arenaInfo, "bots" ), sizeof(bots) );
   1074 	p = &bots[0];
   1075 	while( *p && count < PLAYER_SLOTS ) {
   1076 		//skip spaces
   1077 		while( *p && *p == ' ' ) {
   1078 			p++;
   1079 		}
   1080 		if( !p ) {
   1081 			break;
   1082 		}
   1083 
   1084 		// mark start of bot name
   1085 		bot = p;
   1086 
   1087 		// skip until space of null
   1088 		while( *p && *p != ' ' ) {
   1089 			p++;
   1090 		}
   1091 		if( *p ) {
   1092 			*p++ = 0;
   1093 		}
   1094 
   1095 		botInfo = UI_GetBotInfoByName( bot );
   1096 		bot = Info_ValueForKey( botInfo, "name" );
   1097 
   1098 		Q_strncpyz( s_serveroptions.playerNameBuffers[count], bot, sizeof(s_serveroptions.playerNameBuffers[count]) );
   1099 		count++;
   1100 	}
   1101 
   1102 	// set the rest of the bot slots to "---"
   1103 	for( n = count; n < PLAYER_SLOTS; n++ ) {
   1104 		strcpy( s_serveroptions.playerNameBuffers[n], "--------" );
   1105 	}
   1106 
   1107 	// pad up to #8 as open slots
   1108 	for( ;count < 8; count++ ) {
   1109 		s_serveroptions.playerType[count].curvalue = 0;
   1110 	}
   1111 
   1112 	// close off the rest by default
   1113 	for( ;count < PLAYER_SLOTS; count++ ) {
   1114 		if( s_serveroptions.playerType[count].curvalue == 1 ) {
   1115 			s_serveroptions.playerType[count].curvalue = 2;
   1116 		}
   1117 	}
   1118 }
   1119 
   1120 
   1121 /*
   1122 =================
   1123 ServerOptions_SetMenuItems
   1124 =================
   1125 */
   1126 static void ServerOptions_SetMenuItems( void ) {
   1127 	static char picname[64];
   1128 
   1129 	switch( s_serveroptions.gametype ) {
   1130 	case GT_FFA:
   1131 	default:
   1132 		Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ) ) );
   1133 		Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_timelimit" ) ) );
   1134 		break;
   1135 
   1136 	case GT_TOURNAMENT:
   1137 		Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ) ) );
   1138 		Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_timelimit" ) ) );
   1139 		break;
   1140 
   1141 	case GT_TEAM:
   1142 		Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_fraglimit" ) ) );
   1143 		Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_timelimit" ) ) );
   1144 		s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_team_friendly" ) );
   1145 		break;
   1146 
   1147 	case GT_CTF:
   1148 		Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ) ) );
   1149 		Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_timelimit" ) ) );
   1150 		s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_friendly" ) );
   1151 		break;
   1152 	}
   1153 
   1154 	Q_strncpyz( s_serveroptions.hostname.field.buffer, UI_Cvar_VariableString( "sv_hostname" ), sizeof( s_serveroptions.hostname.field.buffer ) );
   1155 	s_serveroptions.pure.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_pure" ) );
   1156 
   1157 	// set the map pic
   1158 	Com_sprintf( picname, 64, "levelshots/%s", s_startserver.maplist[s_startserver.currentmap] );
   1159 	s_serveroptions.mappic.generic.name = picname;
   1160 
   1161 	// set the map name
   1162 	strcpy( s_serveroptions.mapnamebuffer, s_startserver.mapname.string );
   1163 	Q_strupr( s_serveroptions.mapnamebuffer );
   1164 
   1165 	// get the player selections initialized
   1166 	ServerOptions_InitPlayerItems();
   1167 	ServerOptions_SetPlayerItems();
   1168 
   1169 	// seed bot names
   1170 	ServerOptions_InitBotNames();
   1171 	ServerOptions_SetPlayerItems();
   1172 }
   1173 
   1174 /*
   1175 =================
   1176 PlayerName_Draw
   1177 =================
   1178 */
   1179 static void PlayerName_Draw( void *item ) {
   1180 	menutext_s	*s;
   1181 	float		*color;
   1182 	int			x, y;
   1183 	int			style;
   1184 	qboolean	focus;
   1185 
   1186 	s = (menutext_s *)item;
   1187 
   1188 	x = s->generic.x;
   1189 	y =	s->generic.y;
   1190 
   1191 	style = UI_SMALLFONT;
   1192 	focus = (s->generic.parent->cursor == s->generic.menuPosition);
   1193 
   1194 	if ( s->generic.flags & QMF_GRAYED )
   1195 		color = text_color_disabled;
   1196 	else if ( focus )
   1197 	{
   1198 		color = text_color_highlight;
   1199 		style |= UI_PULSE;
   1200 	}
   1201 	else if ( s->generic.flags & QMF_BLINK )
   1202 	{
   1203 		color = text_color_highlight;
   1204 		style |= UI_BLINK;
   1205 	}
   1206 	else
   1207 		color = text_color_normal;
   1208 
   1209 	if ( focus )
   1210 	{
   1211 		// draw cursor
   1212 		UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color ); 
   1213 		UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
   1214 	}
   1215 
   1216 	UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
   1217 	UI_DrawString( x + SMALLCHAR_WIDTH, y, s->string, style|UI_LEFT, color );
   1218 }
   1219 
   1220 
   1221 /*
   1222 =================
   1223 ServerOptions_MenuInit
   1224 =================
   1225 */
   1226 #define OPTIONS_X	456
   1227 
   1228 static void ServerOptions_MenuInit( qboolean multiplayer ) {
   1229 	int		y;
   1230 	int		n;
   1231 
   1232 	memset( &s_serveroptions, 0 ,sizeof(serveroptions_t) );
   1233 	s_serveroptions.multiplayer = multiplayer;
   1234 	s_serveroptions.gametype = (int)Com_Clamp( 0, 5, trap_Cvar_VariableValue( "g_gameType" ) );
   1235 	s_serveroptions.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_punkbuster" ) );
   1236 
   1237 	ServerOptions_Cache();
   1238 
   1239 	s_serveroptions.menu.wrapAround = qtrue;
   1240 	s_serveroptions.menu.fullscreen = qtrue;
   1241 
   1242 	s_serveroptions.banner.generic.type			= MTYPE_BTEXT;
   1243 	s_serveroptions.banner.generic.x			= 320;
   1244 	s_serveroptions.banner.generic.y			= 16;
   1245 	s_serveroptions.banner.string  				= "GAME SERVER";
   1246 	s_serveroptions.banner.color  				= color_white;
   1247 	s_serveroptions.banner.style  				= UI_CENTER;
   1248 
   1249 	s_serveroptions.mappic.generic.type			= MTYPE_BITMAP;
   1250 	s_serveroptions.mappic.generic.flags		= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
   1251 	s_serveroptions.mappic.generic.x			= 352;
   1252 	s_serveroptions.mappic.generic.y			= 80;
   1253 	s_serveroptions.mappic.width				= 160;
   1254 	s_serveroptions.mappic.height				= 120;
   1255 	s_serveroptions.mappic.errorpic				= GAMESERVER_UNKNOWNMAP;
   1256 	s_serveroptions.mappic.generic.ownerdraw	= ServerOptions_LevelshotDraw;
   1257 
   1258 	s_serveroptions.picframe.generic.type		= MTYPE_BITMAP;
   1259 	s_serveroptions.picframe.generic.flags		= QMF_LEFT_JUSTIFY|QMF_INACTIVE|QMF_HIGHLIGHT;
   1260 	s_serveroptions.picframe.generic.x			= 352 - 38;
   1261 	s_serveroptions.picframe.generic.y			= 80 - 40;
   1262 	s_serveroptions.picframe.width  			= 320;
   1263 	s_serveroptions.picframe.height  			= 320;
   1264 	s_serveroptions.picframe.focuspic			= GAMESERVER_SELECT;
   1265 
   1266 	y = 272;
   1267 	if( s_serveroptions.gametype != GT_CTF ) {
   1268 		s_serveroptions.fraglimit.generic.type       = MTYPE_FIELD;
   1269 		s_serveroptions.fraglimit.generic.name       = "Frag Limit:";
   1270 		s_serveroptions.fraglimit.generic.flags      = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1271 		s_serveroptions.fraglimit.generic.x	         = OPTIONS_X;
   1272 		s_serveroptions.fraglimit.generic.y	         = y;
   1273 		s_serveroptions.fraglimit.generic.statusbar  = ServerOptions_StatusBar;
   1274 		s_serveroptions.fraglimit.field.widthInChars = 3;
   1275 		s_serveroptions.fraglimit.field.maxchars     = 3;
   1276 	}
   1277 	else {
   1278 		s_serveroptions.flaglimit.generic.type       = MTYPE_FIELD;
   1279 		s_serveroptions.flaglimit.generic.name       = "Capture Limit:";
   1280 		s_serveroptions.flaglimit.generic.flags      = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1281 		s_serveroptions.flaglimit.generic.x	         = OPTIONS_X;
   1282 		s_serveroptions.flaglimit.generic.y	         = y;
   1283 		s_serveroptions.flaglimit.generic.statusbar  = ServerOptions_StatusBar;
   1284 		s_serveroptions.flaglimit.field.widthInChars = 3;
   1285 		s_serveroptions.flaglimit.field.maxchars     = 3;
   1286 	}
   1287 
   1288 	y += BIGCHAR_HEIGHT+2;
   1289 	s_serveroptions.timelimit.generic.type       = MTYPE_FIELD;
   1290 	s_serveroptions.timelimit.generic.name       = "Time Limit:";
   1291 	s_serveroptions.timelimit.generic.flags      = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1292 	s_serveroptions.timelimit.generic.x	         = OPTIONS_X;
   1293 	s_serveroptions.timelimit.generic.y	         = y;
   1294 	s_serveroptions.timelimit.generic.statusbar  = ServerOptions_StatusBar;
   1295 	s_serveroptions.timelimit.field.widthInChars = 3;
   1296 	s_serveroptions.timelimit.field.maxchars     = 3;
   1297 
   1298 	if( s_serveroptions.gametype >= GT_TEAM ) {
   1299 		y += BIGCHAR_HEIGHT+2;
   1300 		s_serveroptions.friendlyfire.generic.type     = MTYPE_RADIOBUTTON;
   1301 		s_serveroptions.friendlyfire.generic.flags    = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1302 		s_serveroptions.friendlyfire.generic.x	      = OPTIONS_X;
   1303 		s_serveroptions.friendlyfire.generic.y	      = y;
   1304 		s_serveroptions.friendlyfire.generic.name	  = "Friendly Fire:";
   1305 	}
   1306 
   1307 	y += BIGCHAR_HEIGHT+2;
   1308 	s_serveroptions.pure.generic.type			= MTYPE_RADIOBUTTON;
   1309 	s_serveroptions.pure.generic.flags			= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1310 	s_serveroptions.pure.generic.x				= OPTIONS_X;
   1311 	s_serveroptions.pure.generic.y				= y;
   1312 	s_serveroptions.pure.generic.name			= "Pure Server:";
   1313 
   1314 	if( s_serveroptions.multiplayer ) {
   1315 		y += BIGCHAR_HEIGHT+2;
   1316 		s_serveroptions.dedicated.generic.type		= MTYPE_SPINCONTROL;
   1317 		s_serveroptions.dedicated.generic.id		= ID_DEDICATED;
   1318 		s_serveroptions.dedicated.generic.flags		= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1319 		s_serveroptions.dedicated.generic.callback	= ServerOptions_Event;
   1320 		s_serveroptions.dedicated.generic.x			= OPTIONS_X;
   1321 		s_serveroptions.dedicated.generic.y			= y;
   1322 		s_serveroptions.dedicated.generic.name		= "Dedicated:";
   1323 		s_serveroptions.dedicated.itemnames			= dedicated_list;
   1324 	}
   1325 
   1326 	if( s_serveroptions.multiplayer ) {
   1327 		y += BIGCHAR_HEIGHT+2;
   1328 		s_serveroptions.hostname.generic.type       = MTYPE_FIELD;
   1329 		s_serveroptions.hostname.generic.name       = "Hostname:";
   1330 		s_serveroptions.hostname.generic.flags      = QMF_SMALLFONT;
   1331 		s_serveroptions.hostname.generic.x          = OPTIONS_X;
   1332 		s_serveroptions.hostname.generic.y	        = y;
   1333 		s_serveroptions.hostname.field.widthInChars = 18;
   1334 		s_serveroptions.hostname.field.maxchars     = 64;
   1335 	}
   1336 
   1337 	y += BIGCHAR_HEIGHT+2;
   1338 	s_serveroptions.punkbuster.generic.type			= MTYPE_SPINCONTROL;
   1339 	s_serveroptions.punkbuster.generic.name			= "Punkbuster:";
   1340 	s_serveroptions.punkbuster.generic.flags			= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1341 	s_serveroptions.punkbuster.generic.id			= 0;
   1342 	s_serveroptions.punkbuster.generic.x				= OPTIONS_X;
   1343 	s_serveroptions.punkbuster.generic.y				= y;
   1344 	s_serveroptions.punkbuster.itemnames				= punkbuster_items;
   1345 	
   1346 	y = 80;
   1347 	s_serveroptions.botSkill.generic.type			= MTYPE_SPINCONTROL;
   1348 	s_serveroptions.botSkill.generic.flags			= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1349 	s_serveroptions.botSkill.generic.name			= "Bot Skill:  ";
   1350 	s_serveroptions.botSkill.generic.x				= 32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH;
   1351 	s_serveroptions.botSkill.generic.y				= y;
   1352 	s_serveroptions.botSkill.itemnames				= botSkill_list;
   1353 	s_serveroptions.botSkill.curvalue				= 1;
   1354 
   1355 	y += ( 2 * SMALLCHAR_HEIGHT );
   1356 	s_serveroptions.player0.generic.type			= MTYPE_TEXT;
   1357 	s_serveroptions.player0.generic.flags			= QMF_SMALLFONT;
   1358 	s_serveroptions.player0.generic.x				= 32 + SMALLCHAR_WIDTH;
   1359 	s_serveroptions.player0.generic.y				= y;
   1360 	s_serveroptions.player0.color					= color_orange;
   1361 	s_serveroptions.player0.style					= UI_LEFT|UI_SMALLFONT;
   1362 
   1363 	for( n = 0; n < PLAYER_SLOTS; n++ ) {
   1364 		s_serveroptions.playerType[n].generic.type		= MTYPE_SPINCONTROL;
   1365 		s_serveroptions.playerType[n].generic.flags		= QMF_SMALLFONT;
   1366 		s_serveroptions.playerType[n].generic.id		= ID_PLAYER_TYPE;
   1367 		s_serveroptions.playerType[n].generic.callback	= ServerOptions_Event;
   1368 		s_serveroptions.playerType[n].generic.x			= 32;
   1369 		s_serveroptions.playerType[n].generic.y			= y;
   1370 		s_serveroptions.playerType[n].itemnames			= playerType_list;
   1371 
   1372 		s_serveroptions.playerName[n].generic.type		= MTYPE_TEXT;
   1373 		s_serveroptions.playerName[n].generic.flags		= QMF_SMALLFONT;
   1374 		s_serveroptions.playerName[n].generic.x			= 96;
   1375 		s_serveroptions.playerName[n].generic.y			= y;
   1376 		s_serveroptions.playerName[n].generic.callback	= ServerOptions_PlayerNameEvent;
   1377 		s_serveroptions.playerName[n].generic.id		= n;
   1378 		s_serveroptions.playerName[n].generic.ownerdraw	= PlayerName_Draw;
   1379 		s_serveroptions.playerName[n].color				= color_orange;
   1380 		s_serveroptions.playerName[n].style				= UI_SMALLFONT;
   1381 		s_serveroptions.playerName[n].string			= s_serveroptions.playerNameBuffers[n];
   1382 		s_serveroptions.playerName[n].generic.top		= s_serveroptions.playerName[n].generic.y;
   1383 		s_serveroptions.playerName[n].generic.bottom	= s_serveroptions.playerName[n].generic.y + SMALLCHAR_HEIGHT;
   1384 		s_serveroptions.playerName[n].generic.left		= s_serveroptions.playerName[n].generic.x - SMALLCHAR_HEIGHT/ 2;
   1385 		s_serveroptions.playerName[n].generic.right		= s_serveroptions.playerName[n].generic.x + 16 * SMALLCHAR_WIDTH;
   1386 
   1387 		s_serveroptions.playerTeam[n].generic.type		= MTYPE_SPINCONTROL;
   1388 		s_serveroptions.playerTeam[n].generic.flags		= QMF_SMALLFONT;
   1389 		s_serveroptions.playerTeam[n].generic.x			= 240;
   1390 		s_serveroptions.playerTeam[n].generic.y			= y;
   1391 		s_serveroptions.playerTeam[n].itemnames			= playerTeam_list;
   1392 
   1393 		y += ( SMALLCHAR_HEIGHT + 4 );
   1394 	}
   1395 
   1396 	s_serveroptions.back.generic.type	  = MTYPE_BITMAP;
   1397 	s_serveroptions.back.generic.name     = GAMESERVER_BACK0;
   1398 	s_serveroptions.back.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1399 	s_serveroptions.back.generic.callback = ServerOptions_Event;
   1400 	s_serveroptions.back.generic.id	      = ID_BACK;
   1401 	s_serveroptions.back.generic.x		  = 0;
   1402 	s_serveroptions.back.generic.y		  = 480-64;
   1403 	s_serveroptions.back.width  		  = 128;
   1404 	s_serveroptions.back.height  		  = 64;
   1405 	s_serveroptions.back.focuspic         = GAMESERVER_BACK1;
   1406 
   1407 	s_serveroptions.next.generic.type	  = MTYPE_BITMAP;
   1408 	s_serveroptions.next.generic.name     = GAMESERVER_NEXT0;
   1409 	s_serveroptions.next.generic.flags    = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE|QMF_GRAYED|QMF_HIDDEN;
   1410 	s_serveroptions.next.generic.callback = ServerOptions_Event;
   1411 	s_serveroptions.next.generic.id	      = ID_STARTSERVERNEXT;
   1412 	s_serveroptions.next.generic.x		  = 640;
   1413 	s_serveroptions.next.generic.y		  = 480-64-72;
   1414 	s_serveroptions.next.generic.statusbar  = ServerOptions_StatusBar;
   1415 	s_serveroptions.next.width  		  = 128;
   1416 	s_serveroptions.next.height  		  = 64;
   1417 	s_serveroptions.next.focuspic         = GAMESERVER_NEXT1;
   1418 
   1419 	s_serveroptions.go.generic.type	    = MTYPE_BITMAP;
   1420 	s_serveroptions.go.generic.name     = GAMESERVER_FIGHT0;
   1421 	s_serveroptions.go.generic.flags    = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
   1422 	s_serveroptions.go.generic.callback = ServerOptions_Event;
   1423 	s_serveroptions.go.generic.id	    = ID_GO;
   1424 	s_serveroptions.go.generic.x		= 640;
   1425 	s_serveroptions.go.generic.y		= 480-64;
   1426 	s_serveroptions.go.width  		    = 128;
   1427 	s_serveroptions.go.height  		    = 64;
   1428 	s_serveroptions.go.focuspic         = GAMESERVER_FIGHT1;
   1429 
   1430 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.banner );
   1431 
   1432 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.mappic );
   1433 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.picframe );
   1434 
   1435 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.botSkill );
   1436 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.player0 );
   1437 	for( n = 0; n < PLAYER_SLOTS; n++ ) {
   1438 		if( n != 0 ) {
   1439 			Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerType[n] );
   1440 		}
   1441 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerName[n] );
   1442 		if( s_serveroptions.gametype >= GT_TEAM ) {
   1443 			Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerTeam[n] );
   1444 		}
   1445 	}
   1446 
   1447 	if( s_serveroptions.gametype != GT_CTF ) {
   1448 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.fraglimit );
   1449 	}
   1450 	else {
   1451 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.flaglimit );
   1452 	}
   1453 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.timelimit );
   1454 	if( s_serveroptions.gametype >= GT_TEAM ) {
   1455 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.friendlyfire );
   1456 	}
   1457 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.pure );
   1458 	if( s_serveroptions.multiplayer ) {
   1459 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.dedicated );
   1460 	}
   1461 	if( s_serveroptions.multiplayer ) {
   1462 		Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.hostname );
   1463 	}
   1464 
   1465 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.back );
   1466 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.next );
   1467 	Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.go );
   1468 
   1469 	Menu_AddItem( &s_serveroptions.menu, (void*) &s_serveroptions.punkbuster );
   1470 	
   1471 	ServerOptions_SetMenuItems();
   1472 }
   1473 
   1474 /*
   1475 =================
   1476 ServerOptions_Cache
   1477 =================
   1478 */
   1479 void ServerOptions_Cache( void ) {
   1480 	trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );
   1481 	trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );
   1482 	trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT0 );
   1483 	trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT1 );
   1484 	trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );
   1485 	trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
   1486 }
   1487 
   1488 
   1489 /*
   1490 =================
   1491 UI_ServerOptionsMenu
   1492 =================
   1493 */
   1494 static void UI_ServerOptionsMenu( qboolean multiplayer ) {
   1495 	ServerOptions_MenuInit( multiplayer );
   1496 	UI_PushMenu( &s_serveroptions.menu );
   1497 }
   1498 
   1499 
   1500 
   1501 /*
   1502 =============================================================================
   1503 
   1504 BOT SELECT MENU *****
   1505 
   1506 =============================================================================
   1507 */
   1508 
   1509 
   1510 #define BOTSELECT_BACK0			"menu/art/back_0"
   1511 #define BOTSELECT_BACK1			"menu/art/back_1"
   1512 #define BOTSELECT_ACCEPT0		"menu/art/accept_0"
   1513 #define BOTSELECT_ACCEPT1		"menu/art/accept_1"
   1514 #define BOTSELECT_SELECT		"menu/art/opponents_select"
   1515 #define BOTSELECT_SELECTED		"menu/art/opponents_selected"
   1516 #define BOTSELECT_ARROWS		"menu/art/gs_arrows_0"
   1517 #define BOTSELECT_ARROWSL		"menu/art/gs_arrows_l"
   1518 #define BOTSELECT_ARROWSR		"menu/art/gs_arrows_r"
   1519 
   1520 #define PLAYERGRID_COLS			4
   1521 #define PLAYERGRID_ROWS			4
   1522 #define MAX_MODELSPERPAGE		(PLAYERGRID_ROWS * PLAYERGRID_COLS)
   1523 
   1524 
   1525 typedef struct {
   1526 	menuframework_s	menu;
   1527 
   1528 	menutext_s		banner;
   1529 
   1530 	menubitmap_s	pics[MAX_MODELSPERPAGE];
   1531 	menubitmap_s	picbuttons[MAX_MODELSPERPAGE];
   1532 	menutext_s		picnames[MAX_MODELSPERPAGE];
   1533 
   1534 	menubitmap_s	arrows;
   1535 	menubitmap_s	left;
   1536 	menubitmap_s	right;
   1537 
   1538 	menubitmap_s	go;
   1539 	menubitmap_s	back;
   1540 
   1541 	int				numBots;
   1542 	int				modelpage;
   1543 	int				numpages;
   1544 	int				selectedmodel;
   1545 	int				sortedBotNums[MAX_BOTS];
   1546 	char			boticons[MAX_MODELSPERPAGE][MAX_QPATH];
   1547 	char			botnames[MAX_MODELSPERPAGE][16];
   1548 } botSelectInfo_t;
   1549 
   1550 static botSelectInfo_t	botSelectInfo;
   1551 
   1552 
   1553 /*
   1554 =================
   1555 UI_BotSelectMenu_SortCompare
   1556 =================
   1557 */
   1558 static int QDECL UI_BotSelectMenu_SortCompare( const void *arg1, const void *arg2 ) {
   1559 	int			num1, num2;
   1560 	const char	*info1, *info2;
   1561 	const char	*name1, *name2;
   1562 
   1563 	num1 = *(int *)arg1;
   1564 	num2 = *(int *)arg2;
   1565 
   1566 	info1 = UI_GetBotInfoByNumber( num1 );
   1567 	info2 = UI_GetBotInfoByNumber( num2 );
   1568 
   1569 	name1 = Info_ValueForKey( info1, "name" );
   1570 	name2 = Info_ValueForKey( info2, "name" );
   1571 
   1572 	return Q_stricmp( name1, name2 );
   1573 }
   1574 
   1575 
   1576 /*
   1577 =================
   1578 UI_BotSelectMenu_BuildList
   1579 =================
   1580 */
   1581 static void UI_BotSelectMenu_BuildList( void ) {
   1582 	int		n;
   1583 
   1584 	botSelectInfo.modelpage = 0;
   1585 	botSelectInfo.numBots = UI_GetNumBots();
   1586 	botSelectInfo.numpages = botSelectInfo.numBots / MAX_MODELSPERPAGE;
   1587 	if( botSelectInfo.numBots % MAX_MODELSPERPAGE ) {
   1588 		botSelectInfo.numpages++;
   1589 	}
   1590 
   1591 	// initialize the array
   1592 	for( n = 0; n < botSelectInfo.numBots; n++ ) {
   1593 		botSelectInfo.sortedBotNums[n] = n;
   1594 	}
   1595 
   1596 	// now sort it
   1597 	qsort( botSelectInfo.sortedBotNums, botSelectInfo.numBots, sizeof(botSelectInfo.sortedBotNums[0]), UI_BotSelectMenu_SortCompare );
   1598 }
   1599 
   1600 
   1601 /*
   1602 =================
   1603 ServerPlayerIcon
   1604 =================
   1605 */
   1606 static void ServerPlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) {
   1607 	char	*skin;
   1608 	char	model[MAX_QPATH];
   1609 
   1610 	Q_strncpyz( model, modelAndSkin, sizeof(model));
   1611 	skin = Q_strrchr( model, '/' );
   1612 	if ( skin ) {
   1613 		*skin++ = '\0';
   1614 	}
   1615 	else {
   1616 		skin = "default";
   1617 	}
   1618 
   1619 	Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin );
   1620 
   1621 	if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) {
   1622 		Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model );
   1623 	}
   1624 }
   1625 
   1626 
   1627 /*
   1628 =================
   1629 UI_BotSelectMenu_UpdateGrid
   1630 =================
   1631 */
   1632 static void UI_BotSelectMenu_UpdateGrid( void ) {
   1633 	const char	*info;
   1634 	int			i;
   1635     int			j;
   1636 
   1637 	j = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
   1638 	for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++, j++) {
   1639 		if( j < botSelectInfo.numBots ) { 
   1640 			info = UI_GetBotInfoByNumber( botSelectInfo.sortedBotNums[j] );
   1641 			ServerPlayerIcon( Info_ValueForKey( info, "model" ), botSelectInfo.boticons[i], MAX_QPATH );
   1642 			Q_strncpyz( botSelectInfo.botnames[i], Info_ValueForKey( info, "name" ), 16 );
   1643 			Q_CleanStr( botSelectInfo.botnames[i] );
   1644  			botSelectInfo.pics[i].generic.name = botSelectInfo.boticons[i];
   1645 			if( BotAlreadySelected( botSelectInfo.botnames[i] ) ) {
   1646 				botSelectInfo.picnames[i].color = color_red;
   1647 			}
   1648 			else {
   1649 				botSelectInfo.picnames[i].color = color_orange;
   1650 			}
   1651 			botSelectInfo.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
   1652 		}
   1653 		else {
   1654 			// dead slot
   1655  			botSelectInfo.pics[i].generic.name         = NULL;
   1656 			botSelectInfo.picbuttons[i].generic.flags |= QMF_INACTIVE;
   1657 			botSelectInfo.botnames[i][0] = 0;
   1658 		}
   1659 
   1660  		botSelectInfo.pics[i].generic.flags       &= ~QMF_HIGHLIGHT;
   1661  		botSelectInfo.pics[i].shader               = 0;
   1662  		botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
   1663 	}
   1664 
   1665 	// set selected model
   1666 	i = botSelectInfo.selectedmodel % MAX_MODELSPERPAGE;
   1667 	botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
   1668 	botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
   1669 
   1670 	if( botSelectInfo.numpages > 1 ) {
   1671 		if( botSelectInfo.modelpage > 0 ) {
   1672 			botSelectInfo.left.generic.flags &= ~QMF_INACTIVE;
   1673 		}
   1674 		else {
   1675 			botSelectInfo.left.generic.flags |= QMF_INACTIVE;
   1676 		}
   1677 
   1678 		if( botSelectInfo.modelpage < (botSelectInfo.numpages - 1) ) {
   1679 			botSelectInfo.right.generic.flags &= ~QMF_INACTIVE;
   1680 		}
   1681 		else {
   1682 			botSelectInfo.right.generic.flags |= QMF_INACTIVE;
   1683 		}
   1684 	}
   1685 	else {
   1686 		// hide left/right markers
   1687 		botSelectInfo.left.generic.flags |= QMF_INACTIVE;
   1688 		botSelectInfo.right.generic.flags |= QMF_INACTIVE;
   1689 	}
   1690 }
   1691 
   1692 
   1693 /*
   1694 =================
   1695 UI_BotSelectMenu_Default
   1696 =================
   1697 */
   1698 static void UI_BotSelectMenu_Default( char *bot ) {
   1699 	const char	*botInfo;
   1700 	const char	*test;
   1701 	int			n;
   1702 	int			i;
   1703 
   1704 	for( n = 0; n < botSelectInfo.numBots; n++ ) {
   1705 		botInfo = UI_GetBotInfoByNumber( n );
   1706 		test = Info_ValueForKey( botInfo, "name" );
   1707 		if( Q_stricmp( bot, test ) == 0 ) {
   1708 			break;
   1709 		}
   1710 	}
   1711 	if( n == botSelectInfo.numBots ) {
   1712 		botSelectInfo.selectedmodel = 0;
   1713 		return;
   1714 	}
   1715 
   1716 	for( i = 0; i < botSelectInfo.numBots; i++ ) {
   1717 		if( botSelectInfo.sortedBotNums[i] == n ) {
   1718 			break;
   1719 		}
   1720 	}
   1721 	if( i == botSelectInfo.numBots ) {
   1722 		botSelectInfo.selectedmodel = 0;
   1723 		return;
   1724 	}
   1725 
   1726 	botSelectInfo.selectedmodel = i;
   1727 }
   1728 
   1729 
   1730 /*
   1731 =================
   1732 UI_BotSelectMenu_LeftEvent
   1733 =================
   1734 */
   1735 static void UI_BotSelectMenu_LeftEvent( void* ptr, int event ) {
   1736 	if( event != QM_ACTIVATED ) {
   1737 		return;
   1738 	}
   1739 	if( botSelectInfo.modelpage > 0 ) {
   1740 		botSelectInfo.modelpage--;
   1741 		botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
   1742 		UI_BotSelectMenu_UpdateGrid();
   1743 	}
   1744 }
   1745 
   1746 
   1747 /*
   1748 =================
   1749 UI_BotSelectMenu_RightEvent
   1750 =================
   1751 */
   1752 static void UI_BotSelectMenu_RightEvent( void* ptr, int event ) {
   1753 	if( event != QM_ACTIVATED ) {
   1754 		return;
   1755 	}
   1756 	if( botSelectInfo.modelpage < botSelectInfo.numpages - 1 ) {
   1757 		botSelectInfo.modelpage++;
   1758 		botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
   1759 		UI_BotSelectMenu_UpdateGrid();
   1760 	}
   1761 }
   1762 
   1763 
   1764 /*
   1765 =================
   1766 UI_BotSelectMenu_BotEvent
   1767 =================
   1768 */
   1769 static void UI_BotSelectMenu_BotEvent( void* ptr, int event ) {
   1770 	int		i;
   1771 
   1772 	if( event != QM_ACTIVATED ) {
   1773 		return;
   1774 	}
   1775 
   1776 	for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++ ) {
   1777  		botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
   1778  		botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
   1779 	}
   1780 
   1781 	// set selected
   1782 	i = ((menucommon_s*)ptr)->id;
   1783 	botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
   1784 	botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
   1785 	botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE + i;
   1786 }
   1787 
   1788 
   1789 /*
   1790 =================
   1791 UI_BotSelectMenu_BackEvent
   1792 =================
   1793 */
   1794 static void UI_BotSelectMenu_BackEvent( void* ptr, int event ) {
   1795 	if( event != QM_ACTIVATED ) {
   1796 		return;
   1797 	}
   1798 	UI_PopMenu();
   1799 }
   1800 
   1801 
   1802 /*
   1803 =================
   1804 UI_BotSelectMenu_SelectEvent
   1805 =================
   1806 */
   1807 static void UI_BotSelectMenu_SelectEvent( void* ptr, int event ) {
   1808 	if( event != QM_ACTIVATED ) {
   1809 		return;
   1810 	}
   1811 	UI_PopMenu();
   1812 
   1813 	s_serveroptions.newBot = qtrue;
   1814 	Q_strncpyz( s_serveroptions.newBotName, botSelectInfo.botnames[botSelectInfo.selectedmodel % MAX_MODELSPERPAGE], 16 );
   1815 }
   1816 
   1817 
   1818 /*
   1819 =================
   1820 UI_BotSelectMenu_Cache
   1821 =================
   1822 */
   1823 void UI_BotSelectMenu_Cache( void ) {
   1824 	trap_R_RegisterShaderNoMip( BOTSELECT_BACK0 );
   1825 	trap_R_RegisterShaderNoMip( BOTSELECT_BACK1 );
   1826 	trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT0 );
   1827 	trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT1 );
   1828 	trap_R_RegisterShaderNoMip( BOTSELECT_SELECT );
   1829 	trap_R_RegisterShaderNoMip( BOTSELECT_SELECTED );
   1830 	trap_R_RegisterShaderNoMip( BOTSELECT_ARROWS );
   1831 	trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSL );
   1832 	trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSR );
   1833 }
   1834 
   1835 
   1836 static void UI_BotSelectMenu_Init( char *bot ) {
   1837 	int		i, j, k;
   1838 	int		x, y;
   1839 
   1840 	memset( &botSelectInfo, 0 ,sizeof(botSelectInfo) );
   1841 	botSelectInfo.menu.wrapAround = qtrue;
   1842 	botSelectInfo.menu.fullscreen = qtrue;
   1843 
   1844 	UI_BotSelectMenu_Cache();
   1845 
   1846 	botSelectInfo.banner.generic.type	= MTYPE_BTEXT;
   1847 	botSelectInfo.banner.generic.x		= 320;
   1848 	botSelectInfo.banner.generic.y		= 16;
   1849 	botSelectInfo.banner.string			= "SELECT BOT";
   1850 	botSelectInfo.banner.color			= color_white;
   1851 	botSelectInfo.banner.style			= UI_CENTER;
   1852 
   1853 	y =	80;
   1854 	for( i = 0, k = 0; i < PLAYERGRID_ROWS; i++) {
   1855 		x =	180;
   1856 		for( j = 0; j < PLAYERGRID_COLS; j++, k++ ) {
   1857 			botSelectInfo.pics[k].generic.type				= MTYPE_BITMAP;
   1858 			botSelectInfo.pics[k].generic.flags				= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
   1859 			botSelectInfo.pics[k].generic.x					= x;
   1860 			botSelectInfo.pics[k].generic.y					= y;
   1861  			botSelectInfo.pics[k].generic.name				= botSelectInfo.boticons[k];
   1862 			botSelectInfo.pics[k].width						= 64;
   1863 			botSelectInfo.pics[k].height					= 64;
   1864 			botSelectInfo.pics[k].focuspic					= BOTSELECT_SELECTED;
   1865 			botSelectInfo.pics[k].focuscolor				= colorRed;
   1866 
   1867 			botSelectInfo.picbuttons[k].generic.type		= MTYPE_BITMAP;
   1868 			botSelectInfo.picbuttons[k].generic.flags		= QMF_LEFT_JUSTIFY|QMF_NODEFAULTINIT|QMF_PULSEIFFOCUS;
   1869 			botSelectInfo.picbuttons[k].generic.callback	= UI_BotSelectMenu_BotEvent;
   1870 			botSelectInfo.picbuttons[k].generic.id			= k;
   1871 			botSelectInfo.picbuttons[k].generic.x			= x - 16;
   1872 			botSelectInfo.picbuttons[k].generic.y			= y - 16;
   1873 			botSelectInfo.picbuttons[k].generic.left		= x;
   1874 			botSelectInfo.picbuttons[k].generic.top			= y;
   1875 			botSelectInfo.picbuttons[k].generic.right		= x + 64;
   1876 			botSelectInfo.picbuttons[k].generic.bottom		= y + 64;
   1877 			botSelectInfo.picbuttons[k].width				= 128;
   1878 			botSelectInfo.picbuttons[k].height				= 128;
   1879 			botSelectInfo.picbuttons[k].focuspic			= BOTSELECT_SELECT;
   1880 			botSelectInfo.picbuttons[k].focuscolor			= colorRed;
   1881 
   1882 			botSelectInfo.picnames[k].generic.type			= MTYPE_TEXT;
   1883 			botSelectInfo.picnames[k].generic.flags			= QMF_SMALLFONT;
   1884 			botSelectInfo.picnames[k].generic.x				= x + 32;
   1885 			botSelectInfo.picnames[k].generic.y				= y + 64;
   1886 			botSelectInfo.picnames[k].string				= botSelectInfo.botnames[k];
   1887 			botSelectInfo.picnames[k].color					= color_orange;
   1888 			botSelectInfo.picnames[k].style					= UI_CENTER|UI_SMALLFONT;
   1889 
   1890 			x += (64 + 6);
   1891 		}
   1892 		y += (64 + SMALLCHAR_HEIGHT + 6);
   1893 	}
   1894 
   1895 	botSelectInfo.arrows.generic.type		= MTYPE_BITMAP;
   1896 	botSelectInfo.arrows.generic.name		= BOTSELECT_ARROWS;
   1897 	botSelectInfo.arrows.generic.flags		= QMF_INACTIVE;
   1898 	botSelectInfo.arrows.generic.x			= 260;
   1899 	botSelectInfo.arrows.generic.y			= 440;
   1900 	botSelectInfo.arrows.width				= 128;
   1901 	botSelectInfo.arrows.height				= 32;
   1902 
   1903 	botSelectInfo.left.generic.type			= MTYPE_BITMAP;
   1904 	botSelectInfo.left.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1905 	botSelectInfo.left.generic.callback		= UI_BotSelectMenu_LeftEvent;
   1906 	botSelectInfo.left.generic.x			= 260;
   1907 	botSelectInfo.left.generic.y			= 440;
   1908 	botSelectInfo.left.width  				= 64;
   1909 	botSelectInfo.left.height  				= 32;
   1910 	botSelectInfo.left.focuspic				= BOTSELECT_ARROWSL;
   1911 
   1912 	botSelectInfo.right.generic.type	    = MTYPE_BITMAP;
   1913 	botSelectInfo.right.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1914 	botSelectInfo.right.generic.callback	= UI_BotSelectMenu_RightEvent;
   1915 	botSelectInfo.right.generic.x			= 321;
   1916 	botSelectInfo.right.generic.y			= 440;
   1917 	botSelectInfo.right.width  				= 64;
   1918 	botSelectInfo.right.height  		    = 32;
   1919 	botSelectInfo.right.focuspic			= BOTSELECT_ARROWSR;
   1920 
   1921 	botSelectInfo.back.generic.type		= MTYPE_BITMAP;
   1922 	botSelectInfo.back.generic.name		= BOTSELECT_BACK0;
   1923 	botSelectInfo.back.generic.flags	= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1924 	botSelectInfo.back.generic.callback	= UI_BotSelectMenu_BackEvent;
   1925 	botSelectInfo.back.generic.x		= 0;
   1926 	botSelectInfo.back.generic.y		= 480-64;
   1927 	botSelectInfo.back.width			= 128;
   1928 	botSelectInfo.back.height			= 64;
   1929 	botSelectInfo.back.focuspic			= BOTSELECT_BACK1;
   1930 
   1931 	botSelectInfo.go.generic.type		= MTYPE_BITMAP;
   1932 	botSelectInfo.go.generic.name		= BOTSELECT_ACCEPT0;
   1933 	botSelectInfo.go.generic.flags		= QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
   1934 	botSelectInfo.go.generic.callback	= UI_BotSelectMenu_SelectEvent;
   1935 	botSelectInfo.go.generic.x			= 640;
   1936 	botSelectInfo.go.generic.y			= 480-64;
   1937 	botSelectInfo.go.width				= 128;
   1938 	botSelectInfo.go.height				= 64;
   1939 	botSelectInfo.go.focuspic			= BOTSELECT_ACCEPT1;
   1940 
   1941 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.banner );
   1942 	for( i = 0; i < MAX_MODELSPERPAGE; i++ ) {
   1943 		Menu_AddItem( &botSelectInfo.menu,	&botSelectInfo.pics[i] );
   1944 		Menu_AddItem( &botSelectInfo.menu,	&botSelectInfo.picbuttons[i] );
   1945 		Menu_AddItem( &botSelectInfo.menu,	&botSelectInfo.picnames[i] );
   1946 	}
   1947 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.arrows );
   1948 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.left );
   1949 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.right );
   1950 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.back );
   1951 	Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.go );
   1952 
   1953 	UI_BotSelectMenu_BuildList();
   1954 	UI_BotSelectMenu_Default( bot );
   1955 	botSelectInfo.modelpage = botSelectInfo.selectedmodel / MAX_MODELSPERPAGE;
   1956 	UI_BotSelectMenu_UpdateGrid();
   1957 }
   1958 
   1959 
   1960 /*
   1961 =================
   1962 UI_BotSelectMenu
   1963 =================
   1964 */
   1965 void UI_BotSelectMenu( char *bot ) {
   1966 	UI_BotSelectMenu_Init( bot );
   1967 	UI_PushMenu( &botSelectInfo.menu );
   1968 }