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 }