Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ui_splevel.c (30569B)


      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 SINGLE PLAYER LEVEL SELECT MENU
     27 
     28 =============================================================================
     29 */
     30 
     31 #include "ui_local.h"
     32 
     33 
     34 #define ART_LEVELFRAME_FOCUS		"menu/art/maps_select"
     35 #define ART_LEVELFRAME_SELECTED		"menu/art/maps_selected"
     36 #define ART_ARROW					"menu/art/narrow_0"
     37 #define ART_ARROW_FOCUS				"menu/art/narrow_1"
     38 #define ART_MAP_UNKNOWN				"menu/art/unknownmap"
     39 #define ART_MAP_COMPLETE1			"menu/art/level_complete1"
     40 #define ART_MAP_COMPLETE2			"menu/art/level_complete2"
     41 #define ART_MAP_COMPLETE3			"menu/art/level_complete3"
     42 #define ART_MAP_COMPLETE4			"menu/art/level_complete4"
     43 #define ART_MAP_COMPLETE5			"menu/art/level_complete5"
     44 #define ART_BACK0					"menu/art/back_0"
     45 #define ART_BACK1					"menu/art/back_1"	
     46 #define ART_FIGHT0					"menu/art/fight_0"
     47 #define ART_FIGHT1					"menu/art/fight_1"
     48 #define ART_RESET0					"menu/art/reset_0"
     49 #define ART_RESET1					"menu/art/reset_1"	
     50 #define ART_CUSTOM0					"menu/art/skirmish_0"
     51 #define ART_CUSTOM1					"menu/art/skirmish_1"
     52 
     53 #define ID_LEFTARROW		10
     54 #define ID_PICTURE0			11
     55 #define ID_PICTURE1			12
     56 #define ID_PICTURE2			13
     57 #define ID_PICTURE3			14
     58 #define ID_RIGHTARROW		15
     59 #define ID_PLAYERPIC		16
     60 #define ID_AWARD1			17
     61 #define ID_AWARD2			18
     62 #define ID_AWARD3			19
     63 #define ID_AWARD4			20
     64 #define ID_AWARD5			21
     65 #define ID_AWARD6			22
     66 #define ID_BACK				23
     67 #define ID_RESET			24
     68 #define ID_CUSTOM			25
     69 #define ID_NEXT				26
     70 
     71 #define PLAYER_Y			314
     72 #define AWARDS_Y			(PLAYER_Y + 26)
     73 
     74 
     75 typedef struct {
     76 	menuframework_s	menu;
     77 	menutext_s		item_banner;
     78 	menubitmap_s	item_leftarrow;
     79 	menubitmap_s	item_maps[4];
     80 	menubitmap_s	item_rightarrow;
     81 	menubitmap_s	item_player;
     82 	menubitmap_s	item_awards[6];
     83 	menubitmap_s	item_back;
     84 	menubitmap_s	item_reset;
     85 	menubitmap_s	item_custom;
     86 	menubitmap_s	item_next;
     87 	menubitmap_s	item_null;
     88 
     89 	qboolean		reinit;
     90 
     91 	const char *	selectedArenaInfo;
     92 	int				numMaps;
     93 	char			levelPicNames[4][MAX_QPATH];
     94 	char			levelNames[4][16];
     95 	int				levelScores[4];
     96 	int				levelScoresSkill[4];
     97 	qhandle_t		levelSelectedPic;
     98 	qhandle_t		levelFocusPic;
     99 	qhandle_t		levelCompletePic[5];
    100 
    101 	char			playerModel[MAX_QPATH];
    102 	char			playerPicName[MAX_QPATH];
    103 	int				awardLevels[6];
    104 	sfxHandle_t		awardSounds[6];
    105 
    106 	int				numBots;
    107 	qhandle_t		botPics[7];
    108 	char			botNames[7][10];
    109 } levelMenuInfo_t;
    110 
    111 static levelMenuInfo_t	levelMenuInfo;
    112 
    113 static int	selectedArenaSet;
    114 static int	selectedArena;
    115 static int	currentSet;
    116 static int	currentGame;
    117 static int	trainingTier;
    118 static int	finalTier;
    119 static int	minTier;
    120 static int	maxTier;
    121 
    122 
    123 /*
    124 =================
    125 PlayerIcon
    126 =================
    127 */
    128 static void PlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) {
    129 	char	*skin;
    130 	char	model[MAX_QPATH];
    131 
    132 	Q_strncpyz( model, modelAndSkin, sizeof(model));
    133 	skin = Q_strrchr( model, '/' );
    134 	if ( skin ) {
    135 		*skin++ = '\0';
    136 	}
    137 	else {
    138 		skin = "default";
    139 	}
    140 
    141 	Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin );
    142 
    143 	if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) {
    144 		Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model );
    145 	}
    146 }
    147 
    148 
    149 /*
    150 =================
    151 PlayerIconhandle
    152 =================
    153 */
    154 static qhandle_t PlayerIconHandle( const char *modelAndSkin ) {
    155 	char	iconName[MAX_QPATH];
    156 
    157 	PlayerIcon( modelAndSkin, iconName, sizeof(iconName) );
    158 	return trap_R_RegisterShaderNoMip( iconName );
    159 }
    160 
    161 
    162 /*
    163 =================
    164 UI_SPLevelMenu_SetBots
    165 =================
    166 */
    167 static void UI_SPLevelMenu_SetBots( void ) {
    168 	char	*p;
    169 	char	*bot;
    170 	char	*botInfo;
    171 	char	bots[MAX_INFO_STRING];
    172 
    173 	levelMenuInfo.numBots = 0;
    174 	if ( selectedArenaSet > currentSet ) {
    175 		return;
    176 	}
    177 
    178 	Q_strncpyz( bots, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "bots" ), sizeof(bots) );
    179 
    180 	p = &bots[0];
    181 	while( *p && levelMenuInfo.numBots < 7 ) {
    182 		//skip spaces
    183 		while( *p && *p == ' ' ) {
    184 			p++;
    185 		}
    186 		if( !p ) {
    187 			break;
    188 		}
    189 
    190 		// mark start of bot name
    191 		bot = p;
    192 
    193 		// skip until space of null
    194 		while( *p && *p != ' ' ) {
    195 			p++;
    196 		}
    197 		if( *p ) {
    198 			*p++ = 0;
    199 		}
    200 
    201 		botInfo = UI_GetBotInfoByName( bot );
    202 		if( botInfo ) {
    203 			levelMenuInfo.botPics[levelMenuInfo.numBots] = PlayerIconHandle( Info_ValueForKey( botInfo, "model" ) );
    204 			Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], Info_ValueForKey( botInfo, "name" ), 10 );
    205 		}
    206 		else {
    207 			levelMenuInfo.botPics[levelMenuInfo.numBots] = 0;
    208 			Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], bot, 10 );
    209 		}
    210 		Q_CleanStr( levelMenuInfo.botNames[levelMenuInfo.numBots] );
    211 		levelMenuInfo.numBots++;
    212 	}
    213 }
    214 
    215 
    216 /*
    217 =================
    218 UI_SPLevelMenu_SetMenuItems
    219 =================
    220 */
    221 static void UI_SPLevelMenu_SetMenuArena( int n, int level, const char *arenaInfo ) {
    222 	char		map[MAX_QPATH];
    223 
    224 	Q_strncpyz( map, Info_ValueForKey( arenaInfo, "map" ), sizeof(map) );
    225 
    226 	Q_strncpyz( levelMenuInfo.levelNames[n], map, sizeof(levelMenuInfo.levelNames[n]) );
    227 	Q_strupr( levelMenuInfo.levelNames[n] );
    228 
    229 	UI_GetBestScore( level, &levelMenuInfo.levelScores[n], &levelMenuInfo.levelScoresSkill[n] );
    230 	if( levelMenuInfo.levelScores[n] > 8 ) {
    231 		levelMenuInfo.levelScores[n] = 8;
    232 	}
    233 
    234 	strcpy( levelMenuInfo.levelPicNames[n], va( "levelshots/%s.tga", map ) );
    235 	if( !trap_R_RegisterShaderNoMip( levelMenuInfo.levelPicNames[n] ) ) {
    236 		strcpy( levelMenuInfo.levelPicNames[n], ART_MAP_UNKNOWN );
    237 	}
    238 	levelMenuInfo.item_maps[n].shader = 0;
    239 	if ( selectedArenaSet > currentSet ) {
    240 		levelMenuInfo.item_maps[n].generic.flags |= QMF_GRAYED;
    241 	}
    242 	else {
    243 		levelMenuInfo.item_maps[n].generic.flags &= ~QMF_GRAYED;
    244 	}
    245 
    246 	levelMenuInfo.item_maps[n].generic.flags &= ~QMF_INACTIVE;
    247 }
    248 
    249 static void UI_SPLevelMenu_SetMenuItems( void ) {
    250 	int			n;
    251 	int			level;
    252 	const char	*arenaInfo;
    253 
    254 	if ( selectedArenaSet > currentSet ) {
    255 		selectedArena = -1;
    256 	}
    257 	else if ( selectedArena == -1 ) {
    258 		selectedArena = 0;
    259 	}
    260 
    261 	if( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) {
    262 		selectedArena = 0;
    263 	}
    264 
    265 	if( selectedArena != -1 ) {
    266 		trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena );
    267 	}
    268 
    269 	if( selectedArenaSet == trainingTier ) {
    270 		arenaInfo = UI_GetSpecialArenaInfo( "training" );
    271 		level = atoi( Info_ValueForKey( arenaInfo, "num" ) );
    272 		UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo );
    273 		levelMenuInfo.selectedArenaInfo = arenaInfo;
    274 
    275 		levelMenuInfo.item_maps[0].generic.x = 256;
    276 		Bitmap_Init( &levelMenuInfo.item_maps[0] );
    277 		levelMenuInfo.item_maps[0].generic.bottom += 32;
    278 		levelMenuInfo.numMaps = 1;
    279 
    280 		levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE;
    281 		levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE;
    282 		levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE;
    283 		levelMenuInfo.levelPicNames[1][0] = 0;
    284 		levelMenuInfo.levelPicNames[2][0] = 0;
    285 		levelMenuInfo.levelPicNames[3][0] = 0;
    286 		levelMenuInfo.item_maps[1].shader = 0;
    287 		levelMenuInfo.item_maps[2].shader = 0;
    288 		levelMenuInfo.item_maps[3].shader = 0;
    289 	}
    290 	else if( selectedArenaSet == finalTier ) {
    291 		arenaInfo = UI_GetSpecialArenaInfo( "final" );
    292 		level = atoi( Info_ValueForKey( arenaInfo, "num" ) );
    293 		UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo );
    294 		levelMenuInfo.selectedArenaInfo = arenaInfo;
    295 
    296 		levelMenuInfo.item_maps[0].generic.x = 256;
    297 		Bitmap_Init( &levelMenuInfo.item_maps[0] );
    298 		levelMenuInfo.item_maps[0].generic.bottom += 32;
    299 		levelMenuInfo.numMaps = 1;
    300 
    301 		levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE;
    302 		levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE;
    303 		levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE;
    304 		levelMenuInfo.levelPicNames[1][0] = 0;
    305 		levelMenuInfo.levelPicNames[2][0] = 0;
    306 		levelMenuInfo.levelPicNames[3][0] = 0;
    307 		levelMenuInfo.item_maps[1].shader = 0;
    308 		levelMenuInfo.item_maps[2].shader = 0;
    309 		levelMenuInfo.item_maps[3].shader = 0;
    310 	}
    311 	else {
    312 		levelMenuInfo.item_maps[0].generic.x = 46;
    313 		Bitmap_Init( &levelMenuInfo.item_maps[0] );
    314 		levelMenuInfo.item_maps[0].generic.bottom += 18;
    315 		levelMenuInfo.numMaps = 4;
    316 
    317 		for ( n = 0; n < 4; n++ ) {
    318 			level = selectedArenaSet * ARENAS_PER_TIER + n;
    319 			arenaInfo = UI_GetArenaInfoByNumber( level );
    320 			UI_SPLevelMenu_SetMenuArena( n, level, arenaInfo );
    321 		}
    322 
    323 		if( selectedArena != -1 ) {
    324 			levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena );
    325 		}
    326 	}
    327 
    328 	// enable/disable arrows when they are valid/invalid
    329 	if ( selectedArenaSet == minTier ) {
    330 		levelMenuInfo.item_leftarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN );
    331 	}
    332 	else {
    333 		levelMenuInfo.item_leftarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN );
    334 	}
    335 
    336 	if ( selectedArenaSet == maxTier ) {
    337 		levelMenuInfo.item_rightarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN );
    338 	}
    339 	else {
    340 		levelMenuInfo.item_rightarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN );
    341 	}
    342 
    343 	UI_SPLevelMenu_SetBots();
    344 }
    345 
    346 
    347 /*
    348 =================
    349 UI_SPLevelMenu_ResetEvent
    350 =================
    351 */
    352 static void UI_SPLevelMenu_ResetDraw( void ) {
    353 	UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This resets all of the", UI_CENTER|UI_SMALLFONT, color_yellow );
    354 	UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "single player game variables.", UI_CENTER|UI_SMALLFONT, color_yellow );
    355 	UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 2, "Do this only if you want to", UI_CENTER|UI_SMALLFONT, color_yellow );
    356 	UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 3, "start over from the beginning.", UI_CENTER|UI_SMALLFONT, color_yellow );
    357 }
    358 
    359 static void UI_SPLevelMenu_ResetAction( qboolean result ) {
    360 	if( !result ) {
    361 		return;
    362 	}
    363 
    364 	// clear game variables
    365 	UI_NewGame();
    366 	trap_Cvar_SetValue( "ui_spSelection", -4 );
    367 
    368 	// make the level select menu re-initialize
    369 	UI_PopMenu();
    370 	UI_SPLevelMenu();
    371 }
    372 
    373 static void UI_SPLevelMenu_ResetEvent( void* ptr, int event )
    374 {
    375 	if (event != QM_ACTIVATED) {
    376 		return;
    377 	}
    378 
    379 	UI_ConfirmMenu( "RESET GAME?", UI_SPLevelMenu_ResetDraw, UI_SPLevelMenu_ResetAction );
    380 }
    381 
    382 
    383 /*
    384 =================
    385 UI_SPLevelMenu_LevelEvent
    386 =================
    387 */
    388 static void UI_SPLevelMenu_LevelEvent( void* ptr, int notification ) {
    389 	if (notification != QM_ACTIVATED) {
    390 		return;
    391 	}
    392 
    393 	if ( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) {
    394 		return;
    395 	}
    396 
    397 	selectedArena = ((menucommon_s*)ptr)->id - ID_PICTURE0;
    398 	levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena );
    399 	UI_SPLevelMenu_SetBots();
    400 
    401 	trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena );
    402 }
    403 
    404 
    405 /*
    406 =================
    407 UI_SPLevelMenu_LeftArrowEvent
    408 =================
    409 */
    410 static void UI_SPLevelMenu_LeftArrowEvent( void* ptr, int notification ) {
    411 	if (notification != QM_ACTIVATED) {
    412 		return;
    413 	}
    414 
    415 	if ( selectedArenaSet == minTier ) {
    416 		return;
    417 	}
    418 
    419 	selectedArenaSet--;
    420 	UI_SPLevelMenu_SetMenuItems();
    421 }
    422 
    423 
    424 /*
    425 =================
    426 UI_SPLevelMenu_RightArrowEvent
    427 =================
    428 */
    429 static void UI_SPLevelMenu_RightArrowEvent( void* ptr, int notification ) {
    430 	if (notification != QM_ACTIVATED) {
    431 		return;
    432 	}
    433 
    434 	if ( selectedArenaSet == maxTier ) {
    435 		return;
    436 	}
    437 
    438 	selectedArenaSet++;
    439 	UI_SPLevelMenu_SetMenuItems();
    440 }
    441 
    442 
    443 /*
    444 =================
    445 UI_SPLevelMenu_PlayerEvent
    446 =================
    447 */
    448 static void UI_SPLevelMenu_PlayerEvent( void* ptr, int notification ) {
    449 	if (notification != QM_ACTIVATED) {
    450 		return;
    451 	}
    452 
    453 	UI_PlayerSettingsMenu();
    454 }
    455 
    456 
    457 /*
    458 =================
    459 UI_SPLevelMenu_AwardEvent
    460 =================
    461 */
    462 static void UI_SPLevelMenu_AwardEvent( void* ptr, int notification ) {
    463 	int		n;
    464 
    465 	if (notification != QM_ACTIVATED) {
    466 		return;
    467 	}
    468 
    469 	n = ((menucommon_s*)ptr)->id - ID_AWARD1;
    470 	trap_S_StartLocalSound( levelMenuInfo.awardSounds[n], CHAN_ANNOUNCER );
    471 }
    472 
    473 
    474 /*
    475 =================
    476 UI_SPLevelMenu_NextEvent
    477 =================
    478 */
    479 static void UI_SPLevelMenu_NextEvent( void* ptr, int notification ) {
    480 	if (notification != QM_ACTIVATED) {
    481 		return;
    482 	}
    483 
    484 	if ( selectedArenaSet > currentSet ) {
    485 		return;
    486 	}
    487 
    488 	if ( selectedArena == -1 ) {
    489 		selectedArena = 0;
    490 	}
    491 
    492 	UI_SPSkillMenu( levelMenuInfo.selectedArenaInfo );
    493 }
    494 
    495 
    496 /*
    497 =================
    498 UI_SPLevelMenu_BackEvent
    499 =================
    500 */
    501 static void UI_SPLevelMenu_BackEvent( void* ptr, int notification ) {
    502 	if (notification != QM_ACTIVATED) {
    503 		return;
    504 	}
    505 
    506 	if ( selectedArena == -1 ) {
    507 		selectedArena = 0;
    508 	}
    509 
    510 	UI_PopMenu();
    511 }
    512 
    513 
    514 /*
    515 =================
    516 UI_SPLevelMenu_CustomEvent
    517 =================
    518 */
    519 static void UI_SPLevelMenu_CustomEvent( void* ptr, int notification ) {
    520 	if (notification != QM_ACTIVATED) {
    521 		return;
    522 	}
    523 
    524 	UI_StartServerMenu( qfalse );
    525 }
    526 
    527 
    528 /*
    529 =================
    530 UI_SPLevelMenu_MenuDraw
    531 =================
    532 */
    533 #define LEVEL_DESC_LEFT_MARGIN		332
    534 
    535 static void UI_SPLevelMenu_MenuDraw( void ) {
    536 	int				n, i;
    537 	int				x, y;
    538 	vec4_t			color;
    539 	int				level;
    540 //	int				fraglimit;
    541 	int				pad;
    542 	char			buf[MAX_INFO_VALUE];
    543 	char			string[64];
    544 
    545 	if(	levelMenuInfo.reinit ) {
    546 		UI_PopMenu();
    547 		UI_SPLevelMenu();
    548 		return;
    549 	}
    550 
    551 	// draw player name
    552 	trap_Cvar_VariableStringBuffer( "name", string, 32 );
    553 	Q_CleanStr( string );
    554 	UI_DrawProportionalString( 320, PLAYER_Y, string, UI_CENTER|UI_SMALLFONT, color_orange );
    555 
    556 	// check for model changes
    557 	trap_Cvar_VariableStringBuffer( "model", buf, sizeof(buf) );
    558 	if( Q_stricmp( buf, levelMenuInfo.playerModel ) != 0 ) {
    559 		Q_strncpyz( levelMenuInfo.playerModel, buf, sizeof(levelMenuInfo.playerModel) );
    560 		PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) );
    561 		levelMenuInfo.item_player.shader = 0;
    562 	}
    563 
    564 	// standard menu drawing
    565 	Menu_Draw( &levelMenuInfo.menu );
    566 
    567 	// draw player award levels
    568 	y = AWARDS_Y;
    569 	i = 0;
    570 	for( n = 0; n < 6; n++ ) {
    571 		level = levelMenuInfo.awardLevels[n];
    572 		if( level > 0 ) {
    573 			if( i & 1 ) {
    574 				x = 224 - (i - 1 ) / 2 * (48 + 16);
    575 			}
    576 			else {
    577 				x = 368 + i / 2 * (48 + 16);
    578 			}
    579 			i++;
    580 
    581 			if( level == 1 ) {
    582 				continue;
    583 			}
    584 
    585 			if( level >= 1000000 ) {
    586 				Com_sprintf( string, sizeof(string), "%im", level / 1000000 );
    587 			}
    588 			else if( level >= 1000 ) {
    589 				Com_sprintf( string, sizeof(string), "%ik", level / 1000 );
    590 			}
    591 			else {
    592 				Com_sprintf( string, sizeof(string), "%i", level );
    593 			}
    594 
    595 			UI_DrawString( x + 24, y + 48, string, UI_CENTER, color_yellow );
    596 		}
    597 	}
    598 
    599 	UI_DrawProportionalString( 18, 38, va( "Tier %i", selectedArenaSet + 1 ), UI_LEFT|UI_SMALLFONT, color_orange );
    600 
    601 	for ( n = 0; n < levelMenuInfo.numMaps; n++ ) {
    602 		x = levelMenuInfo.item_maps[n].generic.x;
    603 		y = levelMenuInfo.item_maps[n].generic.y;
    604 		UI_FillRect( x, y + 96, 128, 18, color_black );
    605 	}
    606 
    607 	if ( selectedArenaSet > currentSet ) {
    608 		UI_DrawProportionalString( 320, 216, "ACCESS DENIED", UI_CENTER|UI_BIGFONT, color_red );
    609 		return;
    610 	}
    611 
    612 	// show levelshots for levels of current tier
    613 	Vector4Copy( color_white, color );
    614 	color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
    615 	for ( n = 0; n < levelMenuInfo.numMaps; n++ ) {
    616 		x = levelMenuInfo.item_maps[n].generic.x;
    617 		y = levelMenuInfo.item_maps[n].generic.y;
    618 
    619 		UI_DrawString( x + 64, y + 96, levelMenuInfo.levelNames[n], UI_CENTER|UI_SMALLFONT, color_orange );
    620 
    621 		if( levelMenuInfo.levelScores[n] == 1 ) {
    622 			UI_DrawHandlePic( x, y, 128, 96, levelMenuInfo.levelCompletePic[levelMenuInfo.levelScoresSkill[n] - 1] ); 
    623 		}
    624 
    625 		if ( n == selectedArena ) {
    626 			if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) {
    627 				trap_R_SetColor( color );
    628 			}
    629 			UI_DrawHandlePic( x-1, y-1, 130, 130 - 14, levelMenuInfo.levelSelectedPic ); 
    630 			trap_R_SetColor( NULL );
    631 		}
    632 		else if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) {
    633 			trap_R_SetColor( color );
    634 			UI_DrawHandlePic( x-31, y-30, 256, 256-27, levelMenuInfo.levelFocusPic); 
    635 			trap_R_SetColor( NULL );
    636 		}
    637 	}
    638 
    639 	// show map name and long name of selected level
    640 	y = 192;
    641 	Q_strncpyz( buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "map" ), 20 );
    642 	Q_strupr( buf );
    643 	Com_sprintf( string, sizeof(string), "%s: %s", buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "longname" ) );
    644 	UI_DrawProportionalString( 320, y, string, UI_CENTER|UI_SMALLFONT, color_orange );
    645 
    646 //	fraglimit = atoi( Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "fraglimit" ) );
    647 //	UI_DrawString( 18, 212, va("Frags %i", fraglimit) , UI_LEFT|UI_SMALLFONT, color_orange );
    648 
    649 	// draw bot opponents
    650 	y += 24;
    651 	pad = (7 - levelMenuInfo.numBots) * (64 + 26) / 2;
    652 	for( n = 0; n < levelMenuInfo.numBots; n++ ) {
    653 		x = 18 + pad + (64 + 26) * n;
    654 		if( levelMenuInfo.botPics[n] ) {
    655 			UI_DrawHandlePic( x, y, 64, 64, levelMenuInfo.botPics[n]);
    656 		}
    657 		else {
    658 			UI_FillRect( x, y, 64, 64, color_black );
    659 			UI_DrawProportionalString( x+22, y+18, "?", UI_BIGFONT, color_orange );
    660 		}
    661 		UI_DrawString( x, y + 64, levelMenuInfo.botNames[n], UI_SMALLFONT|UI_LEFT, color_orange );
    662 	}
    663 }
    664 
    665 
    666 /*
    667 =================
    668 UI_SPLevelMenu_Cache
    669 =================
    670 */
    671 void UI_SPLevelMenu_Cache( void ) {
    672 	int				n;
    673 
    674 	trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS );
    675 	trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED );
    676 	trap_R_RegisterShaderNoMip( ART_ARROW );
    677 	trap_R_RegisterShaderNoMip( ART_ARROW_FOCUS );
    678 	trap_R_RegisterShaderNoMip( ART_MAP_UNKNOWN );
    679 	trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 );
    680 	trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 );
    681 	trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 );
    682 	trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 );
    683 	trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 );
    684 	trap_R_RegisterShaderNoMip( ART_BACK0 );
    685 	trap_R_RegisterShaderNoMip( ART_BACK1 );
    686 	trap_R_RegisterShaderNoMip( ART_FIGHT0 );
    687 	trap_R_RegisterShaderNoMip( ART_FIGHT1 );
    688 	trap_R_RegisterShaderNoMip( ART_RESET0 );
    689 	trap_R_RegisterShaderNoMip( ART_RESET1 );
    690 	trap_R_RegisterShaderNoMip( ART_CUSTOM0 );
    691 	trap_R_RegisterShaderNoMip( ART_CUSTOM1 );
    692 
    693 	for( n = 0; n < 6; n++ ) {
    694 		trap_R_RegisterShaderNoMip( ui_medalPicNames[n] );
    695 		levelMenuInfo.awardSounds[n] = trap_S_RegisterSound( ui_medalSounds[n], qfalse );
    696 	}
    697 
    698 	levelMenuInfo.levelSelectedPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED );
    699 	levelMenuInfo.levelFocusPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS );
    700 	levelMenuInfo.levelCompletePic[0] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 );
    701 	levelMenuInfo.levelCompletePic[1] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 );
    702 	levelMenuInfo.levelCompletePic[2] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 );
    703 	levelMenuInfo.levelCompletePic[3] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 );
    704 	levelMenuInfo.levelCompletePic[4] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 );
    705 }
    706 
    707 
    708 /*
    709 =================
    710 UI_SPLevelMenu_Init
    711 =================
    712 */
    713 static void UI_SPLevelMenu_Init( void ) {
    714 	int		skill;
    715 	int		n;
    716 	int		x, y;
    717 	int		count;
    718 	char	buf[MAX_QPATH];
    719 
    720 	skill = (int)trap_Cvar_VariableValue( "g_spSkill" );
    721 	if( skill < 1 || skill > 5 ) {
    722 		trap_Cvar_Set( "g_spSkill", "2" );
    723 		skill = 2;
    724 	}
    725 
    726 	memset( &levelMenuInfo, 0, sizeof(levelMenuInfo) );
    727 	levelMenuInfo.menu.fullscreen = qtrue;
    728 	levelMenuInfo.menu.wrapAround = qtrue;
    729 	levelMenuInfo.menu.draw = UI_SPLevelMenu_MenuDraw;
    730 
    731 	UI_SPLevelMenu_Cache();
    732 
    733 	levelMenuInfo.item_banner.generic.type			= MTYPE_BTEXT;
    734 	levelMenuInfo.item_banner.generic.x				= 320;
    735 	levelMenuInfo.item_banner.generic.y				= 16;
    736 	levelMenuInfo.item_banner.string				= "CHOOSE LEVEL";
    737 	levelMenuInfo.item_banner.color					= color_red;
    738 	levelMenuInfo.item_banner.style					= UI_CENTER;
    739 
    740 	levelMenuInfo.item_leftarrow.generic.type		= MTYPE_BITMAP;
    741 	levelMenuInfo.item_leftarrow.generic.name		= ART_ARROW;
    742 	levelMenuInfo.item_leftarrow.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    743 	levelMenuInfo.item_leftarrow.generic.x			= 18;
    744 	levelMenuInfo.item_leftarrow.generic.y			= 64;
    745 	levelMenuInfo.item_leftarrow.generic.callback	= UI_SPLevelMenu_LeftArrowEvent;
    746 	levelMenuInfo.item_leftarrow.generic.id			= ID_LEFTARROW;
    747 	levelMenuInfo.item_leftarrow.width				= 16;
    748 	levelMenuInfo.item_leftarrow.height				= 114;
    749 	levelMenuInfo.item_leftarrow.focuspic			= ART_ARROW_FOCUS;
    750 
    751 	levelMenuInfo.item_maps[0].generic.type			= MTYPE_BITMAP;
    752 	levelMenuInfo.item_maps[0].generic.name			= levelMenuInfo.levelPicNames[0];
    753 	levelMenuInfo.item_maps[0].generic.flags		= QMF_LEFT_JUSTIFY;
    754 	levelMenuInfo.item_maps[0].generic.x			= 46;
    755 	levelMenuInfo.item_maps[0].generic.y			= 64;
    756 	levelMenuInfo.item_maps[0].generic.id			= ID_PICTURE0;
    757 	levelMenuInfo.item_maps[0].generic.callback		= UI_SPLevelMenu_LevelEvent;
    758 	levelMenuInfo.item_maps[0].width				= 128;
    759 	levelMenuInfo.item_maps[0].height				= 96;
    760 
    761 	levelMenuInfo.item_maps[1].generic.type			= MTYPE_BITMAP;
    762 	levelMenuInfo.item_maps[1].generic.name			= levelMenuInfo.levelPicNames[1];
    763 	levelMenuInfo.item_maps[1].generic.flags		= QMF_LEFT_JUSTIFY;
    764 	levelMenuInfo.item_maps[1].generic.x			= 186;
    765 	levelMenuInfo.item_maps[1].generic.y			= 64;
    766 	levelMenuInfo.item_maps[1].generic.id			= ID_PICTURE1;
    767 	levelMenuInfo.item_maps[1].generic.callback		= UI_SPLevelMenu_LevelEvent;
    768 	levelMenuInfo.item_maps[1].width				= 128;
    769 	levelMenuInfo.item_maps[1].height				= 96;
    770 
    771 	levelMenuInfo.item_maps[2].generic.type			= MTYPE_BITMAP;
    772 	levelMenuInfo.item_maps[2].generic.name			= levelMenuInfo.levelPicNames[2];
    773 	levelMenuInfo.item_maps[2].generic.flags		= QMF_LEFT_JUSTIFY;
    774 	levelMenuInfo.item_maps[2].generic.x			= 326;
    775 	levelMenuInfo.item_maps[2].generic.y			= 64;
    776 	levelMenuInfo.item_maps[2].generic.id			= ID_PICTURE2;
    777 	levelMenuInfo.item_maps[2].generic.callback		= UI_SPLevelMenu_LevelEvent;
    778 	levelMenuInfo.item_maps[2].width				= 128;
    779 	levelMenuInfo.item_maps[2].height				= 96;
    780 
    781 	levelMenuInfo.item_maps[3].generic.type			= MTYPE_BITMAP;
    782 	levelMenuInfo.item_maps[3].generic.name			= levelMenuInfo.levelPicNames[3];
    783 	levelMenuInfo.item_maps[3].generic.flags		= QMF_LEFT_JUSTIFY;
    784 	levelMenuInfo.item_maps[3].generic.x			= 466;
    785 	levelMenuInfo.item_maps[3].generic.y			= 64;
    786 	levelMenuInfo.item_maps[3].generic.id			= ID_PICTURE3;
    787 	levelMenuInfo.item_maps[3].generic.callback		= UI_SPLevelMenu_LevelEvent;
    788 	levelMenuInfo.item_maps[3].width				= 128;
    789 	levelMenuInfo.item_maps[3].height				= 96;
    790 
    791 	levelMenuInfo.item_rightarrow.generic.type		= MTYPE_BITMAP;
    792 	levelMenuInfo.item_rightarrow.generic.name		= ART_ARROW;
    793 	levelMenuInfo.item_rightarrow.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    794 	levelMenuInfo.item_rightarrow.generic.x			= 606;
    795 	levelMenuInfo.item_rightarrow.generic.y			= 64;
    796 	levelMenuInfo.item_rightarrow.generic.callback	= UI_SPLevelMenu_RightArrowEvent;
    797 	levelMenuInfo.item_rightarrow.generic.id		= ID_RIGHTARROW;
    798 	levelMenuInfo.item_rightarrow.width				= -16;
    799 	levelMenuInfo.item_rightarrow.height			= 114;
    800 	levelMenuInfo.item_rightarrow.focuspic			= ART_ARROW_FOCUS;
    801 
    802 	trap_Cvar_VariableStringBuffer( "model", levelMenuInfo.playerModel, sizeof(levelMenuInfo.playerModel) );
    803 	PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) );
    804 	levelMenuInfo.item_player.generic.type			= MTYPE_BITMAP;
    805 	levelMenuInfo.item_player.generic.name			= levelMenuInfo.playerPicName;
    806 	levelMenuInfo.item_player.generic.flags			= QMF_LEFT_JUSTIFY|QMF_MOUSEONLY;
    807 	levelMenuInfo.item_player.generic.x				= 288;
    808 	levelMenuInfo.item_player.generic.y				= AWARDS_Y;
    809 	levelMenuInfo.item_player.generic.id			= ID_PLAYERPIC;
    810 	levelMenuInfo.item_player.generic.callback		= UI_SPLevelMenu_PlayerEvent;
    811 	levelMenuInfo.item_player.width					= 64;
    812 	levelMenuInfo.item_player.height				= 64;
    813 
    814 	for( n = 0; n < 6; n++ ) {
    815 		levelMenuInfo.awardLevels[n] = UI_GetAwardLevel( n );
    816 	}
    817 	levelMenuInfo.awardLevels[AWARD_FRAGS] = 100 * (levelMenuInfo.awardLevels[AWARD_FRAGS] / 100);
    818 
    819 	y = AWARDS_Y;
    820 	count = 0;
    821 	for( n = 0; n < 6; n++ ) {
    822 		if( levelMenuInfo.awardLevels[n] ) {
    823 			if( count & 1 ) {
    824 				x = 224 - (count - 1 ) / 2 * (48 + 16);
    825 			}
    826 			else {
    827 				x = 368 + count / 2 * (48 + 16);
    828 			}
    829 
    830 			levelMenuInfo.item_awards[count].generic.type		= MTYPE_BITMAP;
    831 			levelMenuInfo.item_awards[count].generic.name		= ui_medalPicNames[n];
    832 			levelMenuInfo.item_awards[count].generic.flags		= QMF_LEFT_JUSTIFY|QMF_SILENT|QMF_MOUSEONLY;
    833 			levelMenuInfo.item_awards[count].generic.x			= x;
    834 			levelMenuInfo.item_awards[count].generic.y			= y;
    835 			levelMenuInfo.item_awards[count].generic.id			= ID_AWARD1 + n;
    836 			levelMenuInfo.item_awards[count].generic.callback	= UI_SPLevelMenu_AwardEvent;
    837 			levelMenuInfo.item_awards[count].width				= 48;
    838 			levelMenuInfo.item_awards[count].height				= 48;
    839 			count++;
    840 		}
    841 	}
    842 
    843 	levelMenuInfo.item_back.generic.type			= MTYPE_BITMAP;
    844 	levelMenuInfo.item_back.generic.name			= ART_BACK0;
    845 	levelMenuInfo.item_back.generic.flags			= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    846 	levelMenuInfo.item_back.generic.x				= 0;
    847 	levelMenuInfo.item_back.generic.y				= 480-64;
    848 	levelMenuInfo.item_back.generic.callback		= UI_SPLevelMenu_BackEvent;
    849 	levelMenuInfo.item_back.generic.id				= ID_BACK;
    850 	levelMenuInfo.item_back.width					= 128;
    851 	levelMenuInfo.item_back.height					= 64;
    852 	levelMenuInfo.item_back.focuspic				= ART_BACK1;
    853 
    854 	levelMenuInfo.item_reset.generic.type			= MTYPE_BITMAP;
    855 	levelMenuInfo.item_reset.generic.name			= ART_RESET0;
    856 	levelMenuInfo.item_reset.generic.flags			= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    857 	levelMenuInfo.item_reset.generic.x				= 170;
    858 	levelMenuInfo.item_reset.generic.y				= 480-64;
    859 	levelMenuInfo.item_reset.generic.callback		= UI_SPLevelMenu_ResetEvent;
    860 	levelMenuInfo.item_reset.generic.id				= ID_RESET;
    861 	levelMenuInfo.item_reset.width					= 128;
    862 	levelMenuInfo.item_reset.height					= 64;
    863 	levelMenuInfo.item_reset.focuspic				= ART_RESET1;
    864 
    865 	levelMenuInfo.item_custom.generic.type			= MTYPE_BITMAP;
    866 	levelMenuInfo.item_custom.generic.name			= ART_CUSTOM0;
    867 	levelMenuInfo.item_custom.generic.flags			= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
    868 	levelMenuInfo.item_custom.generic.x				= 342;
    869 	levelMenuInfo.item_custom.generic.y				= 480-64;
    870 	levelMenuInfo.item_custom.generic.callback		= UI_SPLevelMenu_CustomEvent;
    871 	levelMenuInfo.item_custom.generic.id			= ID_CUSTOM;
    872 	levelMenuInfo.item_custom.width					= 128;
    873 	levelMenuInfo.item_custom.height				= 64;
    874 	levelMenuInfo.item_custom.focuspic				= ART_CUSTOM1;
    875 
    876 	levelMenuInfo.item_next.generic.type			= MTYPE_BITMAP;
    877 	levelMenuInfo.item_next.generic.name			= ART_FIGHT0;
    878 	levelMenuInfo.item_next.generic.flags			= QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
    879 	levelMenuInfo.item_next.generic.x				= 640;
    880 	levelMenuInfo.item_next.generic.y				= 480-64;
    881 	levelMenuInfo.item_next.generic.callback		= UI_SPLevelMenu_NextEvent;
    882 	levelMenuInfo.item_next.generic.id				= ID_NEXT;
    883 	levelMenuInfo.item_next.width					= 128;
    884 	levelMenuInfo.item_next.height					= 64;
    885 	levelMenuInfo.item_next.focuspic				= ART_FIGHT1;
    886 
    887 	levelMenuInfo.item_null.generic.type			= MTYPE_BITMAP;
    888 	levelMenuInfo.item_null.generic.flags			= QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
    889 	levelMenuInfo.item_null.generic.x				= 0;
    890 	levelMenuInfo.item_null.generic.y				= 0;
    891 	levelMenuInfo.item_null.width					= 640;
    892 	levelMenuInfo.item_null.height					= 480;
    893 
    894 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_banner );
    895 
    896 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_leftarrow );
    897 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[0] );
    898 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[1] );
    899 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[2] );
    900 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[3] );
    901 	levelMenuInfo.item_maps[0].generic.bottom += 18;
    902 	levelMenuInfo.item_maps[1].generic.bottom += 18;
    903 	levelMenuInfo.item_maps[2].generic.bottom += 18;
    904 	levelMenuInfo.item_maps[3].generic.bottom += 18;
    905 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_rightarrow );
    906 
    907 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_player );
    908 
    909 	for( n = 0; n < count; n++ ) {
    910 		Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_awards[n] );
    911 	}
    912 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_back );
    913 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_reset );
    914 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_custom );
    915 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_next );
    916 	Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_null );
    917 
    918 	trap_Cvar_VariableStringBuffer( "ui_spSelection", buf, sizeof(buf) );
    919 	if( *buf ) {
    920 		n = atoi( buf );
    921 		selectedArenaSet = n / ARENAS_PER_TIER;
    922 		selectedArena = n % ARENAS_PER_TIER;
    923 	}
    924 	else {
    925 		selectedArenaSet = currentSet;
    926 		selectedArena = currentGame;
    927 	}
    928 
    929 	UI_SPLevelMenu_SetMenuItems();
    930 }
    931 
    932 
    933 /*
    934 =================
    935 UI_SPLevelMenu
    936 =================
    937 */
    938 void UI_SPLevelMenu( void ) {
    939 	int			level;
    940 	int			trainingLevel;
    941 	const char	*arenaInfo;
    942 
    943 	trainingTier = -1;
    944 	arenaInfo = UI_GetSpecialArenaInfo( "training" );
    945 	if( arenaInfo ) {
    946 		minTier = trainingTier;
    947 		trainingLevel = atoi( Info_ValueForKey( arenaInfo, "num" ) );
    948 	}
    949 	else {
    950 		minTier = 0;
    951 		trainingLevel = -2;
    952 	}
    953 
    954 	finalTier = UI_GetNumSPTiers();
    955 	arenaInfo = UI_GetSpecialArenaInfo( "final" );
    956 	if( arenaInfo ) {
    957 		maxTier = finalTier;
    958 	}
    959 	else {
    960 		maxTier = finalTier - 1;
    961 		if( maxTier < minTier ) {
    962 			maxTier = minTier;
    963 		}
    964 	}
    965 
    966 	level = UI_GetCurrentGame();
    967 	if ( level == -1 ) {
    968 		level = UI_GetNumSPArenas() - 1;
    969 		if( maxTier == finalTier ) {
    970 			level++;
    971 		}
    972 	}
    973 
    974 	if( level == trainingLevel ) {
    975 		currentSet = -1;
    976 		currentGame = 0;
    977 	}
    978 	else {
    979 		currentSet = level / ARENAS_PER_TIER;
    980 		currentGame = level % ARENAS_PER_TIER;
    981 	}
    982 
    983 	UI_SPLevelMenu_Init();
    984 	UI_PushMenu( &levelMenuInfo.menu );
    985 	Menu_SetCursorToItem( &levelMenuInfo.menu, &levelMenuInfo.item_next );
    986 }
    987 
    988 
    989 /*
    990 =================
    991 UI_SPLevelMenu_f
    992 =================
    993 */
    994 void UI_SPLevelMenu_f( void ) {
    995 	trap_Key_SetCatcher( KEYCATCH_UI );
    996 	uis.menusp = 0;
    997 	UI_SPLevelMenu();
    998 }
    999 
   1000 
   1001 /*
   1002 =================
   1003 UI_SPLevelMenu_ReInit
   1004 =================
   1005 */
   1006 void UI_SPLevelMenu_ReInit( void ) {
   1007 	levelMenuInfo.reinit = qtrue;
   1008 }