Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ui_gameinfo.c (16828B)


      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 // gameinfo.c
     25 //
     26 
     27 #include "ui_local.h"
     28 
     29 
     30 //
     31 // arena and bot info
     32 //
     33 
     34 #define POOLSIZE	128 * 1024
     35 
     36 int				ui_numBots;
     37 static char		*ui_botInfos[MAX_BOTS];
     38 
     39 static int		ui_numArenas;
     40 static char		*ui_arenaInfos[MAX_ARENAS];
     41 
     42 static int		ui_numSinglePlayerArenas;
     43 static int		ui_numSpecialSinglePlayerArenas;
     44 
     45 static char		memoryPool[POOLSIZE];
     46 static int		allocPoint, outOfMemory;
     47 
     48 
     49 /*
     50 ===============
     51 UI_Alloc
     52 ===============
     53 */
     54 void *UI_Alloc( int size ) {
     55 	char	*p;
     56 
     57 	if ( allocPoint + size > POOLSIZE ) {
     58 		outOfMemory = qtrue;
     59 		return NULL;
     60 	}
     61 
     62 	p = &memoryPool[allocPoint];
     63 
     64 	allocPoint += ( size + 31 ) & ~31;
     65 
     66 	return p;
     67 }
     68 
     69 /*
     70 ===============
     71 UI_InitMemory
     72 ===============
     73 */
     74 void UI_InitMemory( void ) {
     75 	allocPoint = 0;
     76 	outOfMemory = qfalse;
     77 }
     78 
     79 /*
     80 ===============
     81 UI_ParseInfos
     82 ===============
     83 */
     84 int UI_ParseInfos( char *buf, int max, char *infos[] ) {
     85 	char	*token;
     86 	int		count;
     87 	char	key[MAX_TOKEN_CHARS];
     88 	char	info[MAX_INFO_STRING];
     89 
     90 	count = 0;
     91 
     92 	while ( 1 ) {
     93 		token = COM_Parse( &buf );
     94 		if ( !token[0] ) {
     95 			break;
     96 		}
     97 		if ( strcmp( token, "{" ) ) {
     98 			Com_Printf( "Missing { in info file\n" );
     99 			break;
    100 		}
    101 
    102 		if ( count == max ) {
    103 			Com_Printf( "Max infos exceeded\n" );
    104 			break;
    105 		}
    106 
    107 		info[0] = '\0';
    108 		while ( 1 ) {
    109 			token = COM_ParseExt( &buf, qtrue );
    110 			if ( !token[0] ) {
    111 				Com_Printf( "Unexpected end of info file\n" );
    112 				break;
    113 			}
    114 			if ( !strcmp( token, "}" ) ) {
    115 				break;
    116 			}
    117 			Q_strncpyz( key, token, sizeof( key ) );
    118 
    119 			token = COM_ParseExt( &buf, qfalse );
    120 			if ( !token[0] ) {
    121 				strcpy( token, "<NULL>" );
    122 			}
    123 			Info_SetValueForKey( info, key, token );
    124 		}
    125 		//NOTE: extra space for arena number
    126 		infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
    127 		if (infos[count]) {
    128 			strcpy(infos[count], info);
    129 			count++;
    130 		}
    131 	}
    132 	return count;
    133 }
    134 
    135 /*
    136 ===============
    137 UI_LoadArenasFromFile
    138 ===============
    139 */
    140 static void UI_LoadArenasFromFile( char *filename ) {
    141 	int				len;
    142 	fileHandle_t	f;
    143 	char			buf[MAX_ARENAS_TEXT];
    144 
    145 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
    146 	if ( !f ) {
    147 		trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
    148 		return;
    149 	}
    150 	if ( len >= MAX_ARENAS_TEXT ) {
    151 		trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
    152 		trap_FS_FCloseFile( f );
    153 		return;
    154 	}
    155 
    156 	trap_FS_Read( buf, len, f );
    157 	buf[len] = 0;
    158 	trap_FS_FCloseFile( f );
    159 
    160 	ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] );
    161 }
    162 
    163 /*
    164 ===============
    165 UI_LoadArenas
    166 ===============
    167 */
    168 static void UI_LoadArenas( void ) {
    169 	int			numdirs;
    170 	vmCvar_t	arenasFile;
    171 	char		filename[128];
    172 	char		dirlist[1024];
    173 	char*		dirptr;
    174 	int			i, n;
    175 	int			dirlen;
    176 	char		*type;
    177 	char		*tag;
    178 	int			singlePlayerNum, specialNum, otherNum;
    179 
    180 	ui_numArenas = 0;
    181 
    182 	trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM );
    183 	if( *arenasFile.string ) {
    184 		UI_LoadArenasFromFile(arenasFile.string);
    185 	}
    186 	else {
    187 		UI_LoadArenasFromFile("scripts/arenas.txt");
    188 	}
    189 
    190 	// get all arenas from .arena files
    191 	numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
    192 	dirptr  = dirlist;
    193 	for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
    194 		dirlen = strlen(dirptr);
    195 		strcpy(filename, "scripts/");
    196 		strcat(filename, dirptr);
    197 		UI_LoadArenasFromFile(filename);
    198 	}
    199 	trap_Print( va( "%i arenas parsed\n", ui_numArenas ) );
    200 	if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n");
    201 
    202 	// set initial numbers
    203 	for( n = 0; n < ui_numArenas; n++ ) {
    204 		Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", n ) );
    205 	}
    206 
    207 	// go through and count single players levels
    208 	ui_numSinglePlayerArenas = 0;
    209 	ui_numSpecialSinglePlayerArenas = 0;
    210 	for( n = 0; n < ui_numArenas; n++ ) {
    211 		// determine type
    212 		type = Info_ValueForKey( ui_arenaInfos[n], "type" );
    213 
    214 		// if no type specified, it will be treated as "ffa"
    215 		if( !*type ) {
    216 			continue;
    217 		}
    218 
    219 		if( strstr( type, "single" ) ) {
    220 			// check for special single player arenas (training, final)
    221 			tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
    222 			if( *tag ) {
    223 				ui_numSpecialSinglePlayerArenas++;
    224 				continue;
    225 			}
    226 
    227 			ui_numSinglePlayerArenas++;
    228 		}
    229 	}
    230 
    231 	n = ui_numSinglePlayerArenas % ARENAS_PER_TIER;
    232 	if( n != 0 ) {
    233 		ui_numSinglePlayerArenas -= n;
    234 		trap_Print( va( "%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER ) );
    235 	}
    236 
    237 	// go through once more and assign number to the levels
    238 	singlePlayerNum = 0;
    239 	specialNum = singlePlayerNum + ui_numSinglePlayerArenas;
    240 	otherNum = specialNum + ui_numSpecialSinglePlayerArenas;
    241 	for( n = 0; n < ui_numArenas; n++ ) {
    242 		// determine type
    243 		type = Info_ValueForKey( ui_arenaInfos[n], "type" );
    244 
    245 		// if no type specified, it will be treated as "ffa"
    246 		if( *type ) {
    247 			if( strstr( type, "single" ) ) {
    248 				// check for special single player arenas (training, final)
    249 				tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
    250 				if( *tag ) {
    251 					Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", specialNum++ ) );
    252 					continue;
    253 				}
    254 
    255 				Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", singlePlayerNum++ ) );
    256 				continue;
    257 			}
    258 		}
    259 
    260 		Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", otherNum++ ) );
    261 	}
    262 }
    263 
    264 /*
    265 ===============
    266 UI_GetArenaInfoByNumber
    267 ===============
    268 */
    269 const char *UI_GetArenaInfoByNumber( int num ) {
    270 	int		n;
    271 	char	*value;
    272 
    273 	if( num < 0 || num >= ui_numArenas ) {
    274 		trap_Print( va( S_COLOR_RED "Invalid arena number: %i\n", num ) );
    275 		return NULL;
    276 	}
    277 
    278 	for( n = 0; n < ui_numArenas; n++ ) {
    279 		value = Info_ValueForKey( ui_arenaInfos[n], "num" );
    280 		if( *value && atoi(value) == num ) {
    281 			return ui_arenaInfos[n];
    282 		}
    283 	}
    284 
    285 	return NULL;
    286 }
    287 
    288 
    289 /*
    290 ===============
    291 UI_GetArenaInfoByNumber
    292 ===============
    293 */
    294 const char *UI_GetArenaInfoByMap( const char *map ) {
    295 	int			n;
    296 
    297 	for( n = 0; n < ui_numArenas; n++ ) {
    298 		if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "map" ), map ) == 0 ) {
    299 			return ui_arenaInfos[n];
    300 		}
    301 	}
    302 
    303 	return NULL;
    304 }
    305 
    306 
    307 /*
    308 ===============
    309 UI_GetSpecialArenaInfo
    310 ===============
    311 */
    312 const char *UI_GetSpecialArenaInfo( const char *tag ) {
    313 	int			n;
    314 
    315 	for( n = 0; n < ui_numArenas; n++ ) {
    316 		if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "special" ), tag ) == 0 ) {
    317 			return ui_arenaInfos[n];
    318 		}
    319 	}
    320 
    321 	return NULL;
    322 }
    323 
    324 /*
    325 ===============
    326 UI_LoadBotsFromFile
    327 ===============
    328 */
    329 static void UI_LoadBotsFromFile( char *filename ) {
    330 	int				len;
    331 	fileHandle_t	f;
    332 	char			buf[MAX_BOTS_TEXT];
    333 
    334 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
    335 	if ( !f ) {
    336 		trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
    337 		return;
    338 	}
    339 	if ( len >= MAX_BOTS_TEXT ) {
    340 		trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
    341 		trap_FS_FCloseFile( f );
    342 		return;
    343 	}
    344 
    345 	trap_FS_Read( buf, len, f );
    346 	buf[len] = 0;
    347 	trap_FS_FCloseFile( f );
    348 
    349 	ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] );
    350 	if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all bots\n");
    351 }
    352 
    353 /*
    354 ===============
    355 UI_LoadBots
    356 ===============
    357 */
    358 static void UI_LoadBots( void ) {
    359 	vmCvar_t	botsFile;
    360 	int			numdirs;
    361 	char		filename[128];
    362 	char		dirlist[1024];
    363 	char*		dirptr;
    364 	int			i;
    365 	int			dirlen;
    366 
    367 	ui_numBots = 0;
    368 
    369 	trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
    370 	if( *botsFile.string ) {
    371 		UI_LoadBotsFromFile(botsFile.string);
    372 	}
    373 	else {
    374 		UI_LoadBotsFromFile("scripts/bots.txt");
    375 	}
    376 
    377 	// get all bots from .bot files
    378 	numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
    379 	dirptr  = dirlist;
    380 	for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
    381 		dirlen = strlen(dirptr);
    382 		strcpy(filename, "scripts/");
    383 		strcat(filename, dirptr);
    384 		UI_LoadBotsFromFile(filename);
    385 	}
    386 	trap_Print( va( "%i bots parsed\n", ui_numBots ) );
    387 }
    388 
    389 
    390 /*
    391 ===============
    392 UI_GetBotInfoByNumber
    393 ===============
    394 */
    395 char *UI_GetBotInfoByNumber( int num ) {
    396 	if( num < 0 || num >= ui_numBots ) {
    397 		trap_Print( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
    398 		return NULL;
    399 	}
    400 	return ui_botInfos[num];
    401 }
    402 
    403 
    404 /*
    405 ===============
    406 UI_GetBotInfoByName
    407 ===============
    408 */
    409 char *UI_GetBotInfoByName( const char *name ) {
    410 	int		n;
    411 	char	*value;
    412 
    413 	for ( n = 0; n < ui_numBots ; n++ ) {
    414 		value = Info_ValueForKey( ui_botInfos[n], "name" );
    415 		if ( !Q_stricmp( value, name ) ) {
    416 			return ui_botInfos[n];
    417 		}
    418 	}
    419 
    420 	return NULL;
    421 }
    422 
    423 
    424 //
    425 // single player game info
    426 //
    427 
    428 /*
    429 ===============
    430 UI_GetBestScore
    431 
    432 Returns the player's best finish on a given level, 0 if the have not played the level
    433 ===============
    434 */
    435 void UI_GetBestScore( int level, int *score, int *skill ) {
    436 	int		n;
    437 	int		skillScore;
    438 	int		bestScore;
    439 	int		bestScoreSkill;
    440 	char	arenaKey[16];
    441 	char	scores[MAX_INFO_VALUE];
    442 
    443 	if( !score || !skill ) {
    444 		return;
    445 	}
    446 
    447 	if( level < 0 || level > ui_numArenas ) {
    448 		return;
    449 	}
    450 
    451 	bestScore = 0;
    452 	bestScoreSkill = 0;
    453 
    454 	for( n = 1; n <= 5; n++ ) {
    455 		trap_Cvar_VariableStringBuffer( va( "g_spScores%i", n ), scores, MAX_INFO_VALUE );
    456 
    457 		Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
    458 		skillScore = atoi( Info_ValueForKey( scores, arenaKey ) );
    459 
    460 		if( skillScore < 1 || skillScore > 8 ) {
    461 			continue;
    462 		}
    463 
    464 		if( !bestScore || skillScore <= bestScore ) {
    465 			bestScore = skillScore;
    466 			bestScoreSkill = n;
    467 		}
    468 	}
    469 
    470 	*score = bestScore;
    471 	*skill = bestScoreSkill;
    472 }
    473 
    474 
    475 /*
    476 ===============
    477 UI_SetBestScore
    478 
    479 Set the player's best finish for a level
    480 ===============
    481 */
    482 void UI_SetBestScore( int level, int score ) {
    483 	int		skill;
    484 	int		oldScore;
    485 	char	arenaKey[16];
    486 	char	scores[MAX_INFO_VALUE];
    487 
    488 	// validate score
    489 	if( score < 1 || score > 8 ) {
    490 		return;
    491 	}
    492 
    493 	// validate skill
    494 	skill = (int)trap_Cvar_VariableValue( "g_spSkill" );
    495 	if( skill < 1 || skill > 5 ) {
    496 		return;
    497 	}
    498 
    499 	// get scores
    500 	trap_Cvar_VariableStringBuffer( va( "g_spScores%i", skill ), scores, MAX_INFO_VALUE );
    501 
    502 	// see if this is better
    503 	Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
    504 	oldScore = atoi( Info_ValueForKey( scores, arenaKey ) );
    505 	if( oldScore && oldScore <= score ) {
    506 		return;
    507 	}
    508 
    509 	// update scores
    510 	Info_SetValueForKey( scores, arenaKey, va( "%i", score ) );
    511 	trap_Cvar_Set( va( "g_spScores%i", skill ), scores );
    512 }
    513 
    514 
    515 /*
    516 ===============
    517 UI_LogAwardData
    518 ===============
    519 */
    520 void UI_LogAwardData( int award, int data ) {
    521 	char	key[16];
    522 	char	awardData[MAX_INFO_VALUE];
    523 	int		oldValue;
    524 
    525 	if( data == 0 ) {
    526 		return;
    527 	}
    528 
    529 	if( award > AWARD_PERFECT ) {
    530 		trap_Print( va( S_COLOR_RED "Bad award %i in UI_LogAwardData\n", award ) );
    531 		return;
    532 	}
    533 
    534 	trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );
    535 
    536 	Com_sprintf( key, sizeof(key), "a%i", award );
    537 	oldValue = atoi( Info_ValueForKey( awardData, key ) );
    538 
    539 	Info_SetValueForKey( awardData, key, va( "%i", oldValue + data ) );
    540 	trap_Cvar_Set( "g_spAwards", awardData );
    541 }
    542 
    543 
    544 /*
    545 ===============
    546 UI_GetAwardLevel
    547 ===============
    548 */
    549 int UI_GetAwardLevel( int award ) {
    550 	char	key[16];
    551 	char	awardData[MAX_INFO_VALUE];
    552 
    553 	trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );
    554 
    555 	Com_sprintf( key, sizeof(key), "a%i", award );
    556 	return atoi( Info_ValueForKey( awardData, key ) );
    557 }
    558 
    559 
    560 /*
    561 ===============
    562 UI_TierCompleted
    563 ===============
    564 */
    565 int UI_TierCompleted( int levelWon ) {
    566 	int			level;
    567 	int			n;
    568 	int			tier;
    569 	int			score;
    570 	int			skill;
    571 	const char	*info;
    572 
    573 	tier = levelWon / ARENAS_PER_TIER;
    574 	level = tier * ARENAS_PER_TIER;
    575 
    576 	if( tier == UI_GetNumSPTiers() ) {
    577 		info = UI_GetSpecialArenaInfo( "training" );
    578 		if( levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) {
    579 			return 0;
    580 		}
    581 		info = UI_GetSpecialArenaInfo( "final" );
    582 		if( !info || levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) {
    583 			return tier + 1;
    584 		}
    585 		return -1;
    586 	}
    587 
    588 	for( n = 0; n < ARENAS_PER_TIER; n++, level++ ) {
    589 		UI_GetBestScore( level, &score, &skill );
    590 		if ( score != 1 ) {
    591 			return -1;
    592 		}
    593 	}
    594 	return tier + 1;
    595 }
    596 
    597 
    598 /*
    599 ===============
    600 UI_ShowTierVideo
    601 ===============
    602 */
    603 qboolean UI_ShowTierVideo( int tier ) {
    604 	char	key[16];
    605 	char	videos[MAX_INFO_VALUE];
    606 
    607 	if( tier <= 0 ) {
    608 		return qfalse;
    609 	}
    610 
    611 	trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );
    612 
    613 	Com_sprintf( key, sizeof(key), "tier%i", tier );
    614 	if( atoi( Info_ValueForKey( videos, key ) ) ) {
    615 		return qfalse;
    616 	}
    617 
    618 	Info_SetValueForKey( videos, key, va( "%i", 1 ) );
    619 	trap_Cvar_Set( "g_spVideos", videos );
    620 
    621 	return qtrue;
    622 }
    623 
    624 
    625 /*
    626 ===============
    627 UI_CanShowTierVideo
    628 ===============
    629 */
    630 qboolean UI_CanShowTierVideo( int tier ) {
    631 	char	key[16];
    632 	char	videos[MAX_INFO_VALUE];
    633 
    634 	if( !tier ) {
    635 		return qfalse;
    636 	}
    637 
    638 	if( uis.demoversion && tier != 8 ) {
    639 		return qfalse;
    640 	}
    641 
    642 	trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );
    643 
    644 	Com_sprintf( key, sizeof(key), "tier%i", tier );
    645 	if( atoi( Info_ValueForKey( videos, key ) ) ) {
    646 		return qtrue;
    647 	}
    648 
    649 	return qfalse;
    650 }
    651 
    652 
    653 /*
    654 ===============
    655 UI_GetCurrentGame
    656 
    657 Returns the next level the player has not won
    658 ===============
    659 */
    660 int UI_GetCurrentGame( void ) {
    661 	int		level;
    662 	int		rank;
    663 	int		skill;
    664 	const char *info;
    665 
    666 	info = UI_GetSpecialArenaInfo( "training" );
    667 	if( info ) {
    668 		level = atoi( Info_ValueForKey( info, "num" ) );
    669 		UI_GetBestScore( level, &rank, &skill );
    670 		if ( !rank || rank > 1 ) {
    671 			return level;
    672 		}
    673 	}
    674 
    675 	for( level = 0; level < ui_numSinglePlayerArenas; level++ ) {
    676 		UI_GetBestScore( level, &rank, &skill );
    677 		if ( !rank || rank > 1 ) {
    678 			return level;
    679 		}
    680 	}
    681 
    682 	info = UI_GetSpecialArenaInfo( "final" );
    683 	if( !info ) {
    684 		return -1;
    685 	}
    686 	return atoi( Info_ValueForKey( info, "num" ) );
    687 }
    688 
    689 
    690 /*
    691 ===============
    692 UI_NewGame
    693 
    694 Clears the scores and sets the difficutly level
    695 ===============
    696 */
    697 void UI_NewGame( void ) {
    698 	trap_Cvar_Set( "g_spScores1", "" );
    699 	trap_Cvar_Set( "g_spScores2", "" );
    700 	trap_Cvar_Set( "g_spScores3", "" );
    701 	trap_Cvar_Set( "g_spScores4", "" );
    702 	trap_Cvar_Set( "g_spScores5", "" );
    703 	trap_Cvar_Set( "g_spAwards", "" );
    704 	trap_Cvar_Set( "g_spVideos", "" );
    705 }
    706 
    707 
    708 /*
    709 ===============
    710 UI_GetNumArenas
    711 ===============
    712 */
    713 int UI_GetNumArenas( void ) {
    714 	return ui_numArenas;
    715 }
    716 
    717 
    718 /*
    719 ===============
    720 UI_GetNumSPArenas
    721 ===============
    722 */
    723 int UI_GetNumSPArenas( void ) {
    724 	return ui_numSinglePlayerArenas;
    725 }
    726 
    727 
    728 /*
    729 ===============
    730 UI_GetNumSPTiers
    731 ===============
    732 */
    733 int UI_GetNumSPTiers( void ) {
    734 	return ui_numSinglePlayerArenas / ARENAS_PER_TIER;
    735 }
    736 
    737 
    738 /*
    739 ===============
    740 UI_GetNumBots
    741 ===============
    742 */
    743 int UI_GetNumBots( void ) {
    744 	return ui_numBots;
    745 }
    746 
    747 
    748 /*
    749 ===============
    750 UI_SPUnlock_f
    751 ===============
    752 */
    753 void UI_SPUnlock_f( void ) {
    754 	char	arenaKey[16];
    755 	char	scores[MAX_INFO_VALUE];
    756 	int		level;
    757 	int		tier;
    758 
    759 	// get scores for skill 1
    760 	trap_Cvar_VariableStringBuffer( "g_spScores1", scores, MAX_INFO_VALUE );
    761 
    762 	// update scores
    763 	for( level = 0; level < ui_numSinglePlayerArenas + ui_numSpecialSinglePlayerArenas; level++ ) {
    764 		Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
    765 		Info_SetValueForKey( scores, arenaKey, "1" );
    766 	}
    767 	trap_Cvar_Set( "g_spScores1", scores );
    768 
    769 	// unlock cinematics
    770 	for( tier = 1; tier <= 8; tier++ ) {
    771 		UI_ShowTierVideo( tier );
    772 	}
    773 
    774 	trap_Print( "All levels unlocked at skill level 1\n" );
    775 
    776 	UI_SPLevelMenu_ReInit();
    777 }
    778 
    779 
    780 /*
    781 ===============
    782 UI_SPUnlockMedals_f
    783 ===============
    784 */
    785 void UI_SPUnlockMedals_f( void ) {
    786 	int		n;
    787 	char	key[16];
    788 	char	awardData[MAX_INFO_VALUE];
    789 
    790 	trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, MAX_INFO_VALUE );
    791 
    792 	for( n = 0; n < 6; n++ ) {
    793 		Com_sprintf( key, sizeof(key), "a%i", n );
    794 		Info_SetValueForKey( awardData, key, "100" );
    795 	}
    796 
    797 	trap_Cvar_Set( "g_spAwards", awardData );
    798 
    799 	trap_Print( "All levels unlocked at 100\n" );
    800 }
    801 
    802 
    803 /*
    804 ===============
    805 UI_InitGameinfo
    806 ===============
    807 */
    808 void UI_InitGameinfo( void ) {
    809 
    810 	UI_InitMemory();
    811 	UI_LoadArenas();
    812 	UI_LoadBots();
    813 
    814 	if( (trap_Cvar_VariableValue( "fs_restrict" )) || (ui_numSpecialSinglePlayerArenas == 0 && ui_numSinglePlayerArenas == 4) ) {
    815 		uis.demoversion = qtrue;
    816 	}
    817 	else {
    818 		uis.demoversion = qfalse;
    819 	}
    820 }