ui_main.c (181054B)
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 USER INTERFACE MAIN 27 28 ======================================================================= 29 */ 30 31 // use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build 32 //#define PRE_RELEASE_TADEMO 33 34 #include "ui_local.h" 35 36 uiInfo_t uiInfo; 37 38 static const char *MonthAbbrev[] = { 39 "Jan","Feb","Mar", 40 "Apr","May","Jun", 41 "Jul","Aug","Sep", 42 "Oct","Nov","Dec" 43 }; 44 45 46 static const char *skillLevels[] = { 47 "I Can Win", 48 "Bring It On", 49 "Hurt Me Plenty", 50 "Hardcore", 51 "Nightmare" 52 }; 53 54 static const int numSkillLevels = sizeof(skillLevels) / sizeof(const char*); 55 56 57 static const char *netSources[] = { 58 "Local", 59 "Mplayer", 60 "Internet", 61 "Favorites" 62 }; 63 static const int numNetSources = sizeof(netSources) / sizeof(const char*); 64 65 static const serverFilter_t serverFilters[] = { 66 {"All", "" }, 67 {"Quake 3 Arena", "" }, 68 {"Team Arena", "missionpack" }, 69 {"Rocket Arena", "arena" }, 70 {"Alliance", "alliance20" }, 71 {"Weapons Factory Arena", "wfa" }, 72 {"OSP", "osp" }, 73 }; 74 75 static const char *teamArenaGameTypes[] = { 76 "FFA", 77 "TOURNAMENT", 78 "SP", 79 "TEAM DM", 80 "CTF", 81 "1FCTF", 82 "OVERLOAD", 83 "HARVESTER", 84 "TEAMTOURNAMENT" 85 }; 86 87 static int const numTeamArenaGameTypes = sizeof(teamArenaGameTypes) / sizeof(const char*); 88 89 90 static const char *teamArenaGameNames[] = { 91 "Free For All", 92 "Tournament", 93 "Single Player", 94 "Team Deathmatch", 95 "Capture the Flag", 96 "One Flag CTF", 97 "Overload", 98 "Harvester", 99 "Team Tournament", 100 }; 101 102 static int const numTeamArenaGameNames = sizeof(teamArenaGameNames) / sizeof(const char*); 103 104 105 static const int numServerFilters = sizeof(serverFilters) / sizeof(serverFilter_t); 106 107 static const char *sortKeys[] = { 108 "Server Name", 109 "Map Name", 110 "Open Player Spots", 111 "Game Type", 112 "Ping Time" 113 }; 114 static const int numSortKeys = sizeof(sortKeys) / sizeof(const char*); 115 116 static char* netnames[] = { 117 "???", 118 "UDP", 119 "IPX", 120 NULL 121 }; 122 123 #ifndef MISSIONPACK // bk001206 124 static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files"; 125 #endif 126 127 static int gamecodetoui[] = {4,2,3,0,5,1,6}; 128 static int uitogamecode[] = {4,6,2,3,1,5,7}; 129 130 131 static void UI_StartServerRefresh(qboolean full); 132 static void UI_StopServerRefresh( void ); 133 static void UI_DoServerRefresh( void ); 134 static void UI_FeederSelection(float feederID, int index); 135 static void UI_BuildServerDisplayList(qboolean force); 136 static void UI_BuildServerStatus(qboolean force); 137 static void UI_BuildFindPlayerList(qboolean force); 138 static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 ); 139 static int UI_MapCountByGameType(qboolean singlePlayer); 140 static int UI_HeadCountByTeam( void ); 141 static void UI_ParseGameInfo(const char *teamFile); 142 static void UI_ParseTeamInfo(const char *teamFile); 143 static const char *UI_SelectedMap(int index, int *actual); 144 static const char *UI_SelectedHead(int index, int *actual); 145 static int UI_GetIndexFromSelection(int actual); 146 147 int ProcessNewUI( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ); 148 149 /* 150 ================ 151 vmMain 152 153 This is the only way control passes into the module. 154 This must be the very first function compiled into the .qvm file 155 ================ 156 */ 157 vmCvar_t ui_new; 158 vmCvar_t ui_debug; 159 vmCvar_t ui_initialized; 160 vmCvar_t ui_teamArenaFirstRun; 161 162 void _UI_Init( qboolean ); 163 void _UI_Shutdown( void ); 164 void _UI_KeyEvent( int key, qboolean down ); 165 void _UI_MouseEvent( int dx, int dy ); 166 void _UI_Refresh( int realtime ); 167 qboolean _UI_IsFullscreen( void ); 168 int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { 169 switch ( command ) { 170 case UI_GETAPIVERSION: 171 return UI_API_VERSION; 172 173 case UI_INIT: 174 _UI_Init(arg0); 175 return 0; 176 177 case UI_SHUTDOWN: 178 _UI_Shutdown(); 179 return 0; 180 181 case UI_KEY_EVENT: 182 _UI_KeyEvent( arg0, arg1 ); 183 return 0; 184 185 case UI_MOUSE_EVENT: 186 _UI_MouseEvent( arg0, arg1 ); 187 return 0; 188 189 case UI_REFRESH: 190 _UI_Refresh( arg0 ); 191 return 0; 192 193 case UI_IS_FULLSCREEN: 194 return _UI_IsFullscreen(); 195 196 case UI_SET_ACTIVE_MENU: 197 _UI_SetActiveMenu( arg0 ); 198 return 0; 199 200 case UI_CONSOLE_COMMAND: 201 return UI_ConsoleCommand(arg0); 202 203 case UI_DRAW_CONNECT_SCREEN: 204 UI_DrawConnectScreen( arg0 ); 205 return 0; 206 case UI_HASUNIQUECDKEY: // mod authors need to observe this 207 return qtrue; // bk010117 - change this to qfalse for mods! 208 209 } 210 211 return -1; 212 } 213 214 215 216 void AssetCache() { 217 int n; 218 //if (Assets.textFont == NULL) { 219 //} 220 //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND ); 221 //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus)); 222 uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); 223 uiInfo.uiDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); 224 uiInfo.uiDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); 225 uiInfo.uiDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); 226 uiInfo.uiDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); 227 uiInfo.uiDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); 228 uiInfo.uiDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); 229 uiInfo.uiDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); 230 uiInfo.uiDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); 231 uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR ); 232 uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN ); 233 uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP ); 234 uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT ); 235 uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT ); 236 uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB ); 237 uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR ); 238 uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB ); 239 240 for( n = 0; n < NUM_CROSSHAIRS; n++ ) { 241 uiInfo.uiDC.Assets.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) ); 242 } 243 244 uiInfo.newHighScoreSound = trap_S_RegisterSound("sound/feedback/voc_newhighscore.wav", qfalse); 245 } 246 247 void _UI_DrawSides(float x, float y, float w, float h, float size) { 248 UI_AdjustFrom640( &x, &y, &w, &h ); 249 size *= uiInfo.uiDC.xscale; 250 trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); 251 trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); 252 } 253 254 void _UI_DrawTopBottom(float x, float y, float w, float h, float size) { 255 UI_AdjustFrom640( &x, &y, &w, &h ); 256 size *= uiInfo.uiDC.yscale; 257 trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); 258 trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader ); 259 } 260 /* 261 ================ 262 UI_DrawRect 263 264 Coordinates are 640*480 virtual values 265 ================= 266 */ 267 void _UI_DrawRect( float x, float y, float width, float height, float size, const float *color ) { 268 trap_R_SetColor( color ); 269 270 _UI_DrawTopBottom(x, y, width, height, size); 271 _UI_DrawSides(x, y, width, height, size); 272 273 trap_R_SetColor( NULL ); 274 } 275 276 int Text_Width(const char *text, float scale, int limit) { 277 int count,len; 278 float out; 279 glyphInfo_t *glyph; 280 float useScale; 281 const char *s = text; 282 fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; 283 if (scale <= ui_smallFont.value) { 284 font = &uiInfo.uiDC.Assets.smallFont; 285 } else if (scale >= ui_bigFont.value) { 286 font = &uiInfo.uiDC.Assets.bigFont; 287 } 288 useScale = scale * font->glyphScale; 289 out = 0; 290 if (text) { 291 len = strlen(text); 292 if (limit > 0 && len > limit) { 293 len = limit; 294 } 295 count = 0; 296 while (s && *s && count < len) { 297 if ( Q_IsColorString(s) ) { 298 s += 2; 299 continue; 300 } else { 301 glyph = &font->glyphs[(int)*s]; 302 out += glyph->xSkip; 303 s++; 304 count++; 305 } 306 } 307 } 308 return out * useScale; 309 } 310 311 int Text_Height(const char *text, float scale, int limit) { 312 int len, count; 313 float max; 314 glyphInfo_t *glyph; 315 float useScale; 316 const char *s = text; // bk001206 - unsigned 317 fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; 318 if (scale <= ui_smallFont.value) { 319 font = &uiInfo.uiDC.Assets.smallFont; 320 } else if (scale >= ui_bigFont.value) { 321 font = &uiInfo.uiDC.Assets.bigFont; 322 } 323 useScale = scale * font->glyphScale; 324 max = 0; 325 if (text) { 326 len = strlen(text); 327 if (limit > 0 && len > limit) { 328 len = limit; 329 } 330 count = 0; 331 while (s && *s && count < len) { 332 if ( Q_IsColorString(s) ) { 333 s += 2; 334 continue; 335 } else { 336 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build 337 if (max < glyph->height) { 338 max = glyph->height; 339 } 340 s++; 341 count++; 342 } 343 } 344 } 345 return max * useScale; 346 } 347 348 void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) { 349 float w, h; 350 w = width * scale; 351 h = height * scale; 352 UI_AdjustFrom640( &x, &y, &w, &h ); 353 trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader ); 354 } 355 356 void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) { 357 int len, count; 358 vec4_t newColor; 359 glyphInfo_t *glyph; 360 float useScale; 361 fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; 362 if (scale <= ui_smallFont.value) { 363 font = &uiInfo.uiDC.Assets.smallFont; 364 } else if (scale >= ui_bigFont.value) { 365 font = &uiInfo.uiDC.Assets.bigFont; 366 } 367 useScale = scale * font->glyphScale; 368 if (text) { 369 const char *s = text; // bk001206 - unsigned 370 trap_R_SetColor( color ); 371 memcpy(&newColor[0], &color[0], sizeof(vec4_t)); 372 len = strlen(text); 373 if (limit > 0 && len > limit) { 374 len = limit; 375 } 376 count = 0; 377 while (s && *s && count < len) { 378 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build 379 //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; 380 //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); 381 if ( Q_IsColorString( s ) ) { 382 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); 383 newColor[3] = color[3]; 384 trap_R_SetColor( newColor ); 385 s += 2; 386 continue; 387 } else { 388 float yadj = useScale * glyph->top; 389 if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { 390 int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; 391 colorBlack[3] = newColor[3]; 392 trap_R_SetColor( colorBlack ); 393 Text_PaintChar(x + ofs, y - yadj + ofs, 394 glyph->imageWidth, 395 glyph->imageHeight, 396 useScale, 397 glyph->s, 398 glyph->t, 399 glyph->s2, 400 glyph->t2, 401 glyph->glyph); 402 trap_R_SetColor( newColor ); 403 colorBlack[3] = 1.0; 404 } 405 Text_PaintChar(x, y - yadj, 406 glyph->imageWidth, 407 glyph->imageHeight, 408 useScale, 409 glyph->s, 410 glyph->t, 411 glyph->s2, 412 glyph->t2, 413 glyph->glyph); 414 415 x += (glyph->xSkip * useScale) + adjust; 416 s++; 417 count++; 418 } 419 } 420 trap_R_SetColor( NULL ); 421 } 422 } 423 424 void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { 425 int len, count; 426 vec4_t newColor; 427 glyphInfo_t *glyph, *glyph2; 428 float yadj; 429 float useScale; 430 fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; 431 if (scale <= ui_smallFont.value) { 432 font = &uiInfo.uiDC.Assets.smallFont; 433 } else if (scale >= ui_bigFont.value) { 434 font = &uiInfo.uiDC.Assets.bigFont; 435 } 436 useScale = scale * font->glyphScale; 437 if (text) { 438 const char *s = text; // bk001206 - unsigned 439 trap_R_SetColor( color ); 440 memcpy(&newColor[0], &color[0], sizeof(vec4_t)); 441 len = strlen(text); 442 if (limit > 0 && len > limit) { 443 len = limit; 444 } 445 count = 0; 446 glyph2 = &font->glyphs[ (int) cursor]; // bk001206 - possible signed char 447 while (s && *s && count < len) { 448 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build 449 //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; 450 //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); 451 if ( Q_IsColorString( s ) ) { 452 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); 453 newColor[3] = color[3]; 454 trap_R_SetColor( newColor ); 455 s += 2; 456 continue; 457 } else { 458 yadj = useScale * glyph->top; 459 if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { 460 int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; 461 colorBlack[3] = newColor[3]; 462 trap_R_SetColor( colorBlack ); 463 Text_PaintChar(x + ofs, y - yadj + ofs, 464 glyph->imageWidth, 465 glyph->imageHeight, 466 useScale, 467 glyph->s, 468 glyph->t, 469 glyph->s2, 470 glyph->t2, 471 glyph->glyph); 472 colorBlack[3] = 1.0; 473 trap_R_SetColor( newColor ); 474 } 475 Text_PaintChar(x, y - yadj, 476 glyph->imageWidth, 477 glyph->imageHeight, 478 useScale, 479 glyph->s, 480 glyph->t, 481 glyph->s2, 482 glyph->t2, 483 glyph->glyph); 484 485 yadj = useScale * glyph2->top; 486 if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { 487 Text_PaintChar(x, y - yadj, 488 glyph2->imageWidth, 489 glyph2->imageHeight, 490 useScale, 491 glyph2->s, 492 glyph2->t, 493 glyph2->s2, 494 glyph2->t2, 495 glyph2->glyph); 496 } 497 498 x += (glyph->xSkip * useScale); 499 s++; 500 count++; 501 } 502 } 503 // need to paint cursor at end of text 504 if (cursorPos == len && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { 505 yadj = useScale * glyph2->top; 506 Text_PaintChar(x, y - yadj, 507 glyph2->imageWidth, 508 glyph2->imageHeight, 509 useScale, 510 glyph2->s, 511 glyph2->t, 512 glyph2->s2, 513 glyph2->t2, 514 glyph2->glyph); 515 516 } 517 518 trap_R_SetColor( NULL ); 519 } 520 } 521 522 523 static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) { 524 int len, count; 525 vec4_t newColor; 526 glyphInfo_t *glyph; 527 if (text) { 528 const char *s = text; // bk001206 - unsigned 529 float max = *maxX; 530 float useScale; 531 fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; 532 if (scale <= ui_smallFont.value) { 533 font = &uiInfo.uiDC.Assets.smallFont; 534 } else if (scale > ui_bigFont.value) { 535 font = &uiInfo.uiDC.Assets.bigFont; 536 } 537 useScale = scale * font->glyphScale; 538 trap_R_SetColor( color ); 539 len = strlen(text); 540 if (limit > 0 && len > limit) { 541 len = limit; 542 } 543 count = 0; 544 while (s && *s && count < len) { 545 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build 546 if ( Q_IsColorString( s ) ) { 547 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); 548 newColor[3] = color[3]; 549 trap_R_SetColor( newColor ); 550 s += 2; 551 continue; 552 } else { 553 float yadj = useScale * glyph->top; 554 if (Text_Width(s, useScale, 1) + x > max) { 555 *maxX = 0; 556 break; 557 } 558 Text_PaintChar(x, y - yadj, 559 glyph->imageWidth, 560 glyph->imageHeight, 561 useScale, 562 glyph->s, 563 glyph->t, 564 glyph->s2, 565 glyph->t2, 566 glyph->glyph); 567 x += (glyph->xSkip * useScale) + adjust; 568 *maxX = x; 569 count++; 570 s++; 571 } 572 } 573 trap_R_SetColor( NULL ); 574 } 575 576 } 577 578 579 void UI_ShowPostGame(qboolean newHigh) { 580 trap_Cvar_Set ("cg_cameraOrbit", "0"); 581 trap_Cvar_Set("cg_thirdPerson", "0"); 582 trap_Cvar_Set( "sv_killserver", "1" ); 583 uiInfo.soundHighScore = newHigh; 584 _UI_SetActiveMenu(UIMENU_POSTGAME); 585 } 586 /* 587 ================= 588 _UI_Refresh 589 ================= 590 */ 591 592 void UI_DrawCenteredPic(qhandle_t image, int w, int h) { 593 int x, y; 594 x = (SCREEN_WIDTH - w) / 2; 595 y = (SCREEN_HEIGHT - h) / 2; 596 UI_DrawHandlePic(x, y, w, h, image); 597 } 598 599 int frameCount = 0; 600 int startTime; 601 602 #define UI_FPS_FRAMES 4 603 void _UI_Refresh( int realtime ) 604 { 605 static int index; 606 static int previousTimes[UI_FPS_FRAMES]; 607 608 //if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) { 609 // return; 610 //} 611 612 uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime; 613 uiInfo.uiDC.realTime = realtime; 614 615 previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime; 616 index++; 617 if ( index > UI_FPS_FRAMES ) { 618 int i, total; 619 // average multiple frames together to smooth changes out a bit 620 total = 0; 621 for ( i = 0 ; i < UI_FPS_FRAMES ; i++ ) { 622 total += previousTimes[i]; 623 } 624 if ( !total ) { 625 total = 1; 626 } 627 uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total; 628 } 629 630 631 632 UI_UpdateCvars(); 633 634 if (Menu_Count() > 0) { 635 // paint all the menus 636 Menu_PaintAll(); 637 // refresh server browser list 638 UI_DoServerRefresh(); 639 // refresh server status 640 UI_BuildServerStatus(qfalse); 641 // refresh find player list 642 UI_BuildFindPlayerList(qfalse); 643 } 644 645 // draw cursor 646 UI_SetColor( NULL ); 647 if (Menu_Count() > 0) { 648 UI_DrawHandlePic( uiInfo.uiDC.cursorx-16, uiInfo.uiDC.cursory-16, 32, 32, uiInfo.uiDC.Assets.cursor); 649 } 650 651 #ifndef NDEBUG 652 if (uiInfo.uiDC.debug) 653 { 654 // cursor coordinates 655 //FIXME 656 //UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed ); 657 } 658 #endif 659 660 } 661 662 /* 663 ================= 664 _UI_Shutdown 665 ================= 666 */ 667 void _UI_Shutdown( void ) { 668 trap_LAN_SaveCachedServers(); 669 } 670 671 char *defaultMenu = NULL; 672 673 char *GetMenuBuffer(const char *filename) { 674 int len; 675 fileHandle_t f; 676 static char buf[MAX_MENUFILE]; 677 678 len = trap_FS_FOpenFile( filename, &f, FS_READ ); 679 if ( !f ) { 680 trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) ); 681 return defaultMenu; 682 } 683 if ( len >= MAX_MENUFILE ) { 684 trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE ) ); 685 trap_FS_FCloseFile( f ); 686 return defaultMenu; 687 } 688 689 trap_FS_Read( buf, len, f ); 690 buf[len] = 0; 691 trap_FS_FCloseFile( f ); 692 //COM_Compress(buf); 693 return buf; 694 695 } 696 697 qboolean Asset_Parse(int handle) { 698 pc_token_t token; 699 const char *tempStr; 700 701 if (!trap_PC_ReadToken(handle, &token)) 702 return qfalse; 703 if (Q_stricmp(token.string, "{") != 0) { 704 return qfalse; 705 } 706 707 while ( 1 ) { 708 709 memset(&token, 0, sizeof(pc_token_t)); 710 711 if (!trap_PC_ReadToken(handle, &token)) 712 return qfalse; 713 714 if (Q_stricmp(token.string, "}") == 0) { 715 return qtrue; 716 } 717 718 // font 719 if (Q_stricmp(token.string, "font") == 0) { 720 int pointSize; 721 if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { 722 return qfalse; 723 } 724 trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.textFont); 725 uiInfo.uiDC.Assets.fontRegistered = qtrue; 726 continue; 727 } 728 729 if (Q_stricmp(token.string, "smallFont") == 0) { 730 int pointSize; 731 if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { 732 return qfalse; 733 } 734 trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont); 735 continue; 736 } 737 738 if (Q_stricmp(token.string, "bigFont") == 0) { 739 int pointSize; 740 if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) { 741 return qfalse; 742 } 743 trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont); 744 continue; 745 } 746 747 748 // gradientbar 749 if (Q_stricmp(token.string, "gradientbar") == 0) { 750 if (!PC_String_Parse(handle, &tempStr)) { 751 return qfalse; 752 } 753 uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr); 754 continue; 755 } 756 757 // enterMenuSound 758 if (Q_stricmp(token.string, "menuEnterSound") == 0) { 759 if (!PC_String_Parse(handle, &tempStr)) { 760 return qfalse; 761 } 762 uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse ); 763 continue; 764 } 765 766 // exitMenuSound 767 if (Q_stricmp(token.string, "menuExitSound") == 0) { 768 if (!PC_String_Parse(handle, &tempStr)) { 769 return qfalse; 770 } 771 uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse ); 772 continue; 773 } 774 775 // itemFocusSound 776 if (Q_stricmp(token.string, "itemFocusSound") == 0) { 777 if (!PC_String_Parse(handle, &tempStr)) { 778 return qfalse; 779 } 780 uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse ); 781 continue; 782 } 783 784 // menuBuzzSound 785 if (Q_stricmp(token.string, "menuBuzzSound") == 0) { 786 if (!PC_String_Parse(handle, &tempStr)) { 787 return qfalse; 788 } 789 uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse ); 790 continue; 791 } 792 793 if (Q_stricmp(token.string, "cursor") == 0) { 794 if (!PC_String_Parse(handle, &uiInfo.uiDC.Assets.cursorStr)) { 795 return qfalse; 796 } 797 uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( uiInfo.uiDC.Assets.cursorStr); 798 continue; 799 } 800 801 if (Q_stricmp(token.string, "fadeClamp") == 0) { 802 if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeClamp)) { 803 return qfalse; 804 } 805 continue; 806 } 807 808 if (Q_stricmp(token.string, "fadeCycle") == 0) { 809 if (!PC_Int_Parse(handle, &uiInfo.uiDC.Assets.fadeCycle)) { 810 return qfalse; 811 } 812 continue; 813 } 814 815 if (Q_stricmp(token.string, "fadeAmount") == 0) { 816 if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeAmount)) { 817 return qfalse; 818 } 819 continue; 820 } 821 822 if (Q_stricmp(token.string, "shadowX") == 0) { 823 if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowX)) { 824 return qfalse; 825 } 826 continue; 827 } 828 829 if (Q_stricmp(token.string, "shadowY") == 0) { 830 if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowY)) { 831 return qfalse; 832 } 833 continue; 834 } 835 836 if (Q_stricmp(token.string, "shadowColor") == 0) { 837 if (!PC_Color_Parse(handle, &uiInfo.uiDC.Assets.shadowColor)) { 838 return qfalse; 839 } 840 uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3]; 841 continue; 842 } 843 844 } 845 return qfalse; 846 } 847 848 void Font_Report() { 849 int i; 850 Com_Printf("Font Info\n"); 851 Com_Printf("=========\n"); 852 for ( i = 32; i < 96; i++) { 853 Com_Printf("Glyph handle %i: %i\n", i, uiInfo.uiDC.Assets.textFont.glyphs[i].glyph); 854 } 855 } 856 857 void UI_Report() { 858 String_Report(); 859 //Font_Report(); 860 861 } 862 863 void UI_ParseMenu(const char *menuFile) { 864 int handle; 865 pc_token_t token; 866 867 Com_Printf("Parsing menu file:%s\n", menuFile); 868 869 handle = trap_PC_LoadSource(menuFile); 870 if (!handle) { 871 return; 872 } 873 874 while ( 1 ) { 875 memset(&token, 0, sizeof(pc_token_t)); 876 if (!trap_PC_ReadToken( handle, &token )) { 877 break; 878 } 879 880 //if ( Q_stricmp( token, "{" ) ) { 881 // Com_Printf( "Missing { in menu file\n" ); 882 // break; 883 //} 884 885 //if ( menuCount == MAX_MENUS ) { 886 // Com_Printf( "Too many menus!\n" ); 887 // break; 888 //} 889 890 if ( token.string[0] == '}' ) { 891 break; 892 } 893 894 if (Q_stricmp(token.string, "assetGlobalDef") == 0) { 895 if (Asset_Parse(handle)) { 896 continue; 897 } else { 898 break; 899 } 900 } 901 902 if (Q_stricmp(token.string, "menudef") == 0) { 903 // start a new menu 904 Menu_New(handle); 905 } 906 } 907 trap_PC_FreeSource(handle); 908 } 909 910 qboolean Load_Menu(int handle) { 911 pc_token_t token; 912 913 if (!trap_PC_ReadToken(handle, &token)) 914 return qfalse; 915 if (token.string[0] != '{') { 916 return qfalse; 917 } 918 919 while ( 1 ) { 920 921 if (!trap_PC_ReadToken(handle, &token)) 922 return qfalse; 923 924 if ( token.string[0] == 0 ) { 925 return qfalse; 926 } 927 928 if ( token.string[0] == '}' ) { 929 return qtrue; 930 } 931 932 UI_ParseMenu(token.string); 933 } 934 return qfalse; 935 } 936 937 void UI_LoadMenus(const char *menuFile, qboolean reset) { 938 pc_token_t token; 939 int handle; 940 int start; 941 942 start = trap_Milliseconds(); 943 944 handle = trap_PC_LoadSource( menuFile ); 945 if (!handle) { 946 trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); 947 handle = trap_PC_LoadSource( "ui/menus.txt" ); 948 if (!handle) { 949 trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n", menuFile ) ); 950 } 951 } 952 953 ui_new.integer = 1; 954 955 if (reset) { 956 Menu_Reset(); 957 } 958 959 while ( 1 ) { 960 if (!trap_PC_ReadToken(handle, &token)) 961 break; 962 if( token.string[0] == 0 || token.string[0] == '}') { 963 break; 964 } 965 966 if ( token.string[0] == '}' ) { 967 break; 968 } 969 970 if (Q_stricmp(token.string, "loadmenu") == 0) { 971 if (Load_Menu(handle)) { 972 continue; 973 } else { 974 break; 975 } 976 } 977 } 978 979 Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start); 980 981 trap_PC_FreeSource( handle ); 982 } 983 984 void UI_Load() { 985 char lastName[1024]; 986 menuDef_t *menu = Menu_GetFocused(); 987 char *menuSet = UI_Cvar_VariableString("ui_menuFiles"); 988 if (menu && menu->window.name) { 989 strcpy(lastName, menu->window.name); 990 } 991 if (menuSet == NULL || menuSet[0] == '\0') { 992 menuSet = "ui/menus.txt"; 993 } 994 995 String_Init(); 996 997 #ifdef PRE_RELEASE_TADEMO 998 UI_ParseGameInfo("demogameinfo.txt"); 999 #else 1000 UI_ParseGameInfo("gameinfo.txt"); 1001 UI_LoadArenas(); 1002 #endif 1003 1004 UI_LoadMenus(menuSet, qtrue); 1005 Menus_CloseAll(); 1006 Menus_ActivateByName(lastName); 1007 1008 } 1009 1010 static const char *handicapValues[] = {"None","95","90","85","80","75","70","65","60","55","50","45","40","35","30","25","20","15","10","5",NULL}; 1011 #ifndef MISSIONPACK // bk001206 1012 static int numHandicaps = sizeof(handicapValues) / sizeof(const char*); 1013 #endif 1014 1015 static void UI_DrawHandicap(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1016 int i, h; 1017 1018 h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); 1019 i = 20 - h / 5; 1020 1021 Text_Paint(rect->x, rect->y, scale, color, handicapValues[i], 0, 0, textStyle); 1022 } 1023 1024 static void UI_DrawClanName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1025 Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_teamName"), 0, 0, textStyle); 1026 } 1027 1028 1029 static void UI_SetCapFragLimits(qboolean uiVars) { 1030 int cap = 5; 1031 int frag = 10; 1032 if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_OBELISK) { 1033 cap = 4; 1034 } else if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_HARVESTER) { 1035 cap = 15; 1036 } 1037 if (uiVars) { 1038 trap_Cvar_Set("ui_captureLimit", va("%d", cap)); 1039 trap_Cvar_Set("ui_fragLimit", va("%d", frag)); 1040 } else { 1041 trap_Cvar_Set("capturelimit", va("%d", cap)); 1042 trap_Cvar_Set("fraglimit", va("%d", frag)); 1043 } 1044 } 1045 // ui_gameType assumes gametype 0 is -1 ALL and will not show 1046 static void UI_DrawGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1047 Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_gameType.integer].gameType, 0, 0, textStyle); 1048 } 1049 1050 static void UI_DrawNetGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1051 if (ui_netGameType.integer < 0 || ui_netGameType.integer > uiInfo.numGameTypes) { 1052 trap_Cvar_Set("ui_netGameType", "0"); 1053 trap_Cvar_Set("ui_actualNetGameType", "0"); 1054 } 1055 Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_netGameType.integer].gameType , 0, 0, textStyle); 1056 } 1057 1058 static void UI_DrawJoinGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1059 if (ui_joinGameType.integer < 0 || ui_joinGameType.integer > uiInfo.numJoinGameTypes) { 1060 trap_Cvar_Set("ui_joinGameType", "0"); 1061 } 1062 Text_Paint(rect->x, rect->y, scale, color, uiInfo.joinGameTypes[ui_joinGameType.integer].gameType , 0, 0, textStyle); 1063 } 1064 1065 1066 1067 static int UI_TeamIndexFromName(const char *name) { 1068 int i; 1069 1070 if (name && *name) { 1071 for (i = 0; i < uiInfo.teamCount; i++) { 1072 if (Q_stricmp(name, uiInfo.teamList[i].teamName) == 0) { 1073 return i; 1074 } 1075 } 1076 } 1077 1078 return 0; 1079 1080 } 1081 1082 static void UI_DrawClanLogo(rectDef_t *rect, float scale, vec4_t color) { 1083 int i; 1084 i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1085 if (i >= 0 && i < uiInfo.teamCount) { 1086 trap_R_SetColor( color ); 1087 1088 if (uiInfo.teamList[i].teamIcon == -1) { 1089 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1090 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1091 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1092 } 1093 1094 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon); 1095 trap_R_SetColor(NULL); 1096 } 1097 } 1098 1099 static void UI_DrawClanCinematic(rectDef_t *rect, float scale, vec4_t color) { 1100 int i; 1101 i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1102 if (i >= 0 && i < uiInfo.teamCount) { 1103 1104 if (uiInfo.teamList[i].cinematic >= -2) { 1105 if (uiInfo.teamList[i].cinematic == -1) { 1106 uiInfo.teamList[i].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.teamList[i].imageName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 1107 } 1108 if (uiInfo.teamList[i].cinematic >= 0) { 1109 trap_CIN_RunCinematic(uiInfo.teamList[i].cinematic); 1110 trap_CIN_SetExtents(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h); 1111 trap_CIN_DrawCinematic(uiInfo.teamList[i].cinematic); 1112 } else { 1113 trap_R_SetColor( color ); 1114 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal); 1115 trap_R_SetColor(NULL); 1116 uiInfo.teamList[i].cinematic = -2; 1117 } 1118 } else { 1119 trap_R_SetColor( color ); 1120 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon); 1121 trap_R_SetColor(NULL); 1122 } 1123 } 1124 1125 } 1126 1127 static void UI_DrawPreviewCinematic(rectDef_t *rect, float scale, vec4_t color) { 1128 if (uiInfo.previewMovie > -2) { 1129 uiInfo.previewMovie = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.movieList[uiInfo.movieIndex]), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 1130 if (uiInfo.previewMovie >= 0) { 1131 trap_CIN_RunCinematic(uiInfo.previewMovie); 1132 trap_CIN_SetExtents(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h); 1133 trap_CIN_DrawCinematic(uiInfo.previewMovie); 1134 } else { 1135 uiInfo.previewMovie = -2; 1136 } 1137 } 1138 1139 } 1140 1141 1142 1143 static void UI_DrawSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1144 int i; 1145 i = trap_Cvar_VariableValue( "g_spSkill" ); 1146 if (i < 1 || i > numSkillLevels) { 1147 i = 1; 1148 } 1149 Text_Paint(rect->x, rect->y, scale, color, skillLevels[i-1],0, 0, textStyle); 1150 } 1151 1152 1153 static void UI_DrawTeamName(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int textStyle) { 1154 int i; 1155 i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam")); 1156 if (i >= 0 && i < uiInfo.teamCount) { 1157 Text_Paint(rect->x, rect->y, scale, color, va("%s: %s", (blue) ? "Blue" : "Red", uiInfo.teamList[i].teamName),0, 0, textStyle); 1158 } 1159 } 1160 1161 static void UI_DrawTeamMember(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int num, int textStyle) { 1162 // 0 - None 1163 // 1 - Human 1164 // 2..NumCharacters - Bot 1165 int value = trap_Cvar_VariableValue(va(blue ? "ui_blueteam%i" : "ui_redteam%i", num)); 1166 const char *text; 1167 if (value <= 0) { 1168 text = "Closed"; 1169 } else if (value == 1) { 1170 text = "Human"; 1171 } else { 1172 value -= 2; 1173 1174 if (ui_actualNetGameType.integer >= GT_TEAM) { 1175 if (value >= uiInfo.characterCount) { 1176 value = 0; 1177 } 1178 text = uiInfo.characterList[value].name; 1179 } else { 1180 if (value >= UI_GetNumBots()) { 1181 value = 0; 1182 } 1183 text = UI_GetBotNameByNumber(value); 1184 } 1185 } 1186 Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle); 1187 } 1188 1189 static void UI_DrawEffects(rectDef_t *rect, float scale, vec4_t color) { 1190 UI_DrawHandlePic( rect->x, rect->y - 14, 128, 8, uiInfo.uiDC.Assets.fxBasePic ); 1191 UI_DrawHandlePic( rect->x + uiInfo.effectsColor * 16 + 8, rect->y - 16, 16, 12, uiInfo.uiDC.Assets.fxPic[uiInfo.effectsColor] ); 1192 } 1193 1194 static void UI_DrawMapPreview(rectDef_t *rect, float scale, vec4_t color, qboolean net) { 1195 int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; 1196 if (map < 0 || map > uiInfo.mapCount) { 1197 if (net) { 1198 ui_currentNetMap.integer = 0; 1199 trap_Cvar_Set("ui_currentNetMap", "0"); 1200 } else { 1201 ui_currentMap.integer = 0; 1202 trap_Cvar_Set("ui_currentMap", "0"); 1203 } 1204 map = 0; 1205 } 1206 1207 if (uiInfo.mapList[map].levelShot == -1) { 1208 uiInfo.mapList[map].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[map].imageName); 1209 } 1210 1211 if (uiInfo.mapList[map].levelShot > 0) { 1212 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.mapList[map].levelShot); 1213 } else { 1214 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("menu/art/unknownmap")); 1215 } 1216 } 1217 1218 1219 static void UI_DrawMapTimeToBeat(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1220 int minutes, seconds, time; 1221 if (ui_currentMap.integer < 0 || ui_currentMap.integer > uiInfo.mapCount) { 1222 ui_currentMap.integer = 0; 1223 trap_Cvar_Set("ui_currentMap", "0"); 1224 } 1225 1226 time = uiInfo.mapList[ui_currentMap.integer].timeToBeat[uiInfo.gameTypes[ui_gameType.integer].gtEnum]; 1227 1228 minutes = time / 60; 1229 seconds = time % 60; 1230 1231 Text_Paint(rect->x, rect->y, scale, color, va("%02i:%02i", minutes, seconds), 0, 0, textStyle); 1232 } 1233 1234 1235 1236 static void UI_DrawMapCinematic(rectDef_t *rect, float scale, vec4_t color, qboolean net) { 1237 1238 int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; 1239 if (map < 0 || map > uiInfo.mapCount) { 1240 if (net) { 1241 ui_currentNetMap.integer = 0; 1242 trap_Cvar_Set("ui_currentNetMap", "0"); 1243 } else { 1244 ui_currentMap.integer = 0; 1245 trap_Cvar_Set("ui_currentMap", "0"); 1246 } 1247 map = 0; 1248 } 1249 1250 if (uiInfo.mapList[map].cinematic >= -1) { 1251 if (uiInfo.mapList[map].cinematic == -1) { 1252 uiInfo.mapList[map].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[map].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 1253 } 1254 if (uiInfo.mapList[map].cinematic >= 0) { 1255 trap_CIN_RunCinematic(uiInfo.mapList[map].cinematic); 1256 trap_CIN_SetExtents(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h); 1257 trap_CIN_DrawCinematic(uiInfo.mapList[map].cinematic); 1258 } else { 1259 uiInfo.mapList[map].cinematic = -2; 1260 } 1261 } else { 1262 UI_DrawMapPreview(rect, scale, color, net); 1263 } 1264 } 1265 1266 1267 1268 static qboolean updateModel = qtrue; 1269 static qboolean q3Model = qfalse; 1270 1271 static void UI_DrawPlayerModel(rectDef_t *rect) { 1272 static playerInfo_t info; 1273 char model[MAX_QPATH]; 1274 char team[256]; 1275 char head[256]; 1276 vec3_t viewangles; 1277 vec3_t moveangles; 1278 1279 if (trap_Cvar_VariableValue("ui_Q3Model")) { 1280 strcpy(model, UI_Cvar_VariableString("model")); 1281 strcpy(head, UI_Cvar_VariableString("headmodel")); 1282 if (!q3Model) { 1283 q3Model = qtrue; 1284 updateModel = qtrue; 1285 } 1286 team[0] = '\0'; 1287 } else { 1288 1289 strcpy(team, UI_Cvar_VariableString("ui_teamName")); 1290 strcpy(model, UI_Cvar_VariableString("team_model")); 1291 strcpy(head, UI_Cvar_VariableString("team_headmodel")); 1292 if (q3Model) { 1293 q3Model = qfalse; 1294 updateModel = qtrue; 1295 } 1296 } 1297 if (updateModel) { 1298 memset( &info, 0, sizeof(playerInfo_t) ); 1299 viewangles[YAW] = 180 - 10; 1300 viewangles[PITCH] = 0; 1301 viewangles[ROLL] = 0; 1302 VectorClear( moveangles ); 1303 UI_PlayerInfo_SetModel( &info, model, head, team); 1304 UI_PlayerInfo_SetInfo( &info, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); 1305 // UI_RegisterClientModelname( &info, model, head, team); 1306 updateModel = qfalse; 1307 } 1308 1309 UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info, uiInfo.uiDC.realTime / 2); 1310 1311 } 1312 1313 static void UI_DrawNetSource(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1314 if (ui_netSource.integer < 0 || ui_netSource.integer > numNetSources) { 1315 ui_netSource.integer = 0; 1316 } 1317 Text_Paint(rect->x, rect->y, scale, color, va("Source: %s", netSources[ui_netSource.integer]), 0, 0, textStyle); 1318 } 1319 1320 static void UI_DrawNetMapPreview(rectDef_t *rect, float scale, vec4_t color) { 1321 1322 if (uiInfo.serverStatus.currentServerPreview > 0) { 1323 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.serverStatus.currentServerPreview); 1324 } else { 1325 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("menu/art/unknownmap")); 1326 } 1327 } 1328 1329 static void UI_DrawNetMapCinematic(rectDef_t *rect, float scale, vec4_t color) { 1330 if (ui_currentNetMap.integer < 0 || ui_currentNetMap.integer > uiInfo.mapCount) { 1331 ui_currentNetMap.integer = 0; 1332 trap_Cvar_Set("ui_currentNetMap", "0"); 1333 } 1334 1335 if (uiInfo.serverStatus.currentServerCinematic >= 0) { 1336 trap_CIN_RunCinematic(uiInfo.serverStatus.currentServerCinematic); 1337 trap_CIN_SetExtents(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h); 1338 trap_CIN_DrawCinematic(uiInfo.serverStatus.currentServerCinematic); 1339 } else { 1340 UI_DrawNetMapPreview(rect, scale, color); 1341 } 1342 } 1343 1344 1345 1346 static void UI_DrawNetFilter(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1347 if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { 1348 ui_serverFilterType.integer = 0; 1349 } 1350 Text_Paint(rect->x, rect->y, scale, color, va("Filter: %s", serverFilters[ui_serverFilterType.integer].description), 0, 0, textStyle); 1351 } 1352 1353 1354 static void UI_DrawTier(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1355 int i; 1356 i = trap_Cvar_VariableValue( "ui_currentTier" ); 1357 if (i < 0 || i >= uiInfo.tierCount) { 1358 i = 0; 1359 } 1360 Text_Paint(rect->x, rect->y, scale, color, va("Tier: %s", uiInfo.tierList[i].tierName),0, 0, textStyle); 1361 } 1362 1363 static void UI_DrawTierMap(rectDef_t *rect, int index) { 1364 int i; 1365 i = trap_Cvar_VariableValue( "ui_currentTier" ); 1366 if (i < 0 || i >= uiInfo.tierCount) { 1367 i = 0; 1368 } 1369 1370 if (uiInfo.tierList[i].mapHandles[index] == -1) { 1371 uiInfo.tierList[i].mapHandles[index] = trap_R_RegisterShaderNoMip(va("levelshots/%s", uiInfo.tierList[i].maps[index])); 1372 } 1373 1374 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.tierList[i].mapHandles[index]); 1375 } 1376 1377 static const char *UI_EnglishMapName(const char *map) { 1378 int i; 1379 for (i = 0; i < uiInfo.mapCount; i++) { 1380 if (Q_stricmp(map, uiInfo.mapList[i].mapLoadName) == 0) { 1381 return uiInfo.mapList[i].mapName; 1382 } 1383 } 1384 return ""; 1385 } 1386 1387 static void UI_DrawTierMapName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1388 int i, j; 1389 i = trap_Cvar_VariableValue( "ui_currentTier" ); 1390 if (i < 0 || i >= uiInfo.tierCount) { 1391 i = 0; 1392 } 1393 j = trap_Cvar_VariableValue("ui_currentMap"); 1394 if (j < 0 || j > MAPS_PER_TIER) { 1395 j = 0; 1396 } 1397 1398 Text_Paint(rect->x, rect->y, scale, color, UI_EnglishMapName(uiInfo.tierList[i].maps[j]), 0, 0, textStyle); 1399 } 1400 1401 static void UI_DrawTierGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1402 int i, j; 1403 i = trap_Cvar_VariableValue( "ui_currentTier" ); 1404 if (i < 0 || i >= uiInfo.tierCount) { 1405 i = 0; 1406 } 1407 j = trap_Cvar_VariableValue("ui_currentMap"); 1408 if (j < 0 || j > MAPS_PER_TIER) { 1409 j = 0; 1410 } 1411 1412 Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[uiInfo.tierList[i].gameTypes[j]].gameType , 0, 0, textStyle); 1413 } 1414 1415 1416 #ifndef MISSIONPACK // bk001206 1417 static const char *UI_OpponentLeaderName() { 1418 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1419 return uiInfo.teamList[i].teamMembers[0]; 1420 } 1421 #endif 1422 1423 static const char *UI_AIFromName(const char *name) { 1424 int j; 1425 for (j = 0; j < uiInfo.aliasCount; j++) { 1426 if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) { 1427 return uiInfo.aliasList[j].ai; 1428 } 1429 } 1430 return "James"; 1431 } 1432 1433 #ifndef MISSIONPACK // bk001206 1434 static const int UI_AIIndex(const char *name) { 1435 int j; 1436 for (j = 0; j < uiInfo.characterCount; j++) { 1437 if (Q_stricmp(name, uiInfo.characterList[j].name) == 0) { 1438 return j; 1439 } 1440 } 1441 return 0; 1442 } 1443 #endif 1444 1445 #ifndef MISSIONPACK // bk001206 1446 static const int UI_AIIndexFromName(const char *name) { 1447 int j; 1448 for (j = 0; j < uiInfo.aliasCount; j++) { 1449 if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) { 1450 return UI_AIIndex(uiInfo.aliasList[j].ai); 1451 } 1452 } 1453 return 0; 1454 } 1455 #endif 1456 1457 1458 #ifndef MISSIONPACK // bk001206 1459 static const char *UI_OpponentLeaderHead() { 1460 const char *leader = UI_OpponentLeaderName(); 1461 return UI_AIFromName(leader); 1462 } 1463 #endif 1464 1465 #ifndef MISSIONPACK // bk001206 1466 static const char *UI_OpponentLeaderModel() { 1467 int i; 1468 const char *head = UI_OpponentLeaderHead(); 1469 for (i = 0; i < uiInfo.characterCount; i++) { 1470 if (Q_stricmp(head, uiInfo.characterList[i].name) == 0) { 1471 return uiInfo.characterList[i].base; 1472 } 1473 } 1474 return "James"; 1475 } 1476 #endif 1477 1478 1479 static qboolean updateOpponentModel = qtrue; 1480 static void UI_DrawOpponent(rectDef_t *rect) { 1481 static playerInfo_t info2; 1482 char model[MAX_QPATH]; 1483 char headmodel[MAX_QPATH]; 1484 char team[256]; 1485 vec3_t viewangles; 1486 vec3_t moveangles; 1487 1488 if (updateOpponentModel) { 1489 1490 strcpy(model, UI_Cvar_VariableString("ui_opponentModel")); 1491 strcpy(headmodel, UI_Cvar_VariableString("ui_opponentModel")); 1492 team[0] = '\0'; 1493 1494 memset( &info2, 0, sizeof(playerInfo_t) ); 1495 viewangles[YAW] = 180 - 10; 1496 viewangles[PITCH] = 0; 1497 viewangles[ROLL] = 0; 1498 VectorClear( moveangles ); 1499 UI_PlayerInfo_SetModel( &info2, model, headmodel, ""); 1500 UI_PlayerInfo_SetInfo( &info2, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse ); 1501 UI_RegisterClientModelname( &info2, model, headmodel, team); 1502 updateOpponentModel = qfalse; 1503 } 1504 1505 UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info2, uiInfo.uiDC.realTime / 2); 1506 1507 } 1508 1509 static void UI_NextOpponent() { 1510 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1511 int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1512 i++; 1513 if (i >= uiInfo.teamCount) { 1514 i = 0; 1515 } 1516 if (i == j) { 1517 i++; 1518 if ( i >= uiInfo.teamCount) { 1519 i = 0; 1520 } 1521 } 1522 trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName ); 1523 } 1524 1525 static void UI_PriorOpponent() { 1526 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1527 int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1528 i--; 1529 if (i < 0) { 1530 i = uiInfo.teamCount - 1; 1531 } 1532 if (i == j) { 1533 i--; 1534 if ( i < 0) { 1535 i = uiInfo.teamCount - 1; 1536 } 1537 } 1538 trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName ); 1539 } 1540 1541 static void UI_DrawPlayerLogo(rectDef_t *rect, vec3_t color) { 1542 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1543 1544 if (uiInfo.teamList[i].teamIcon == -1) { 1545 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1546 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1547 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1548 } 1549 1550 trap_R_SetColor( color ); 1551 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon ); 1552 trap_R_SetColor( NULL ); 1553 } 1554 1555 static void UI_DrawPlayerLogoMetal(rectDef_t *rect, vec3_t color) { 1556 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1557 if (uiInfo.teamList[i].teamIcon == -1) { 1558 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1559 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1560 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1561 } 1562 1563 trap_R_SetColor( color ); 1564 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal ); 1565 trap_R_SetColor( NULL ); 1566 } 1567 1568 static void UI_DrawPlayerLogoName(rectDef_t *rect, vec3_t color) { 1569 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 1570 if (uiInfo.teamList[i].teamIcon == -1) { 1571 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1572 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1573 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1574 } 1575 1576 trap_R_SetColor( color ); 1577 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name ); 1578 trap_R_SetColor( NULL ); 1579 } 1580 1581 static void UI_DrawOpponentLogo(rectDef_t *rect, vec3_t color) { 1582 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1583 if (uiInfo.teamList[i].teamIcon == -1) { 1584 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1585 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1586 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1587 } 1588 1589 trap_R_SetColor( color ); 1590 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon ); 1591 trap_R_SetColor( NULL ); 1592 } 1593 1594 static void UI_DrawOpponentLogoMetal(rectDef_t *rect, vec3_t color) { 1595 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1596 if (uiInfo.teamList[i].teamIcon == -1) { 1597 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1598 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1599 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1600 } 1601 1602 trap_R_SetColor( color ); 1603 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal ); 1604 trap_R_SetColor( NULL ); 1605 } 1606 1607 static void UI_DrawOpponentLogoName(rectDef_t *rect, vec3_t color) { 1608 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 1609 if (uiInfo.teamList[i].teamIcon == -1) { 1610 uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName); 1611 uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName)); 1612 uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName)); 1613 } 1614 1615 trap_R_SetColor( color ); 1616 UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name ); 1617 trap_R_SetColor( NULL ); 1618 } 1619 1620 static void UI_DrawAllMapsSelection(rectDef_t *rect, float scale, vec4_t color, int textStyle, qboolean net) { 1621 int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer; 1622 if (map >= 0 && map < uiInfo.mapCount) { 1623 Text_Paint(rect->x, rect->y, scale, color, uiInfo.mapList[map].mapName, 0, 0, textStyle); 1624 } 1625 } 1626 1627 static void UI_DrawOpponentName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1628 Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle); 1629 } 1630 1631 1632 static int UI_OwnerDrawWidth(int ownerDraw, float scale) { 1633 int i, h, value; 1634 const char *text; 1635 const char *s = NULL; 1636 1637 switch (ownerDraw) { 1638 case UI_HANDICAP: 1639 h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); 1640 i = 20 - h / 5; 1641 s = handicapValues[i]; 1642 break; 1643 case UI_CLANNAME: 1644 s = UI_Cvar_VariableString("ui_teamName"); 1645 break; 1646 case UI_GAMETYPE: 1647 s = uiInfo.gameTypes[ui_gameType.integer].gameType; 1648 break; 1649 case UI_SKILL: 1650 i = trap_Cvar_VariableValue( "g_spSkill" ); 1651 if (i < 1 || i > numSkillLevels) { 1652 i = 1; 1653 } 1654 s = skillLevels[i-1]; 1655 break; 1656 case UI_BLUETEAMNAME: 1657 i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_blueTeam")); 1658 if (i >= 0 && i < uiInfo.teamCount) { 1659 s = va("%s: %s", "Blue", uiInfo.teamList[i].teamName); 1660 } 1661 break; 1662 case UI_REDTEAMNAME: 1663 i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_redTeam")); 1664 if (i >= 0 && i < uiInfo.teamCount) { 1665 s = va("%s: %s", "Red", uiInfo.teamList[i].teamName); 1666 } 1667 break; 1668 case UI_BLUETEAM1: 1669 case UI_BLUETEAM2: 1670 case UI_BLUETEAM3: 1671 case UI_BLUETEAM4: 1672 case UI_BLUETEAM5: 1673 value = trap_Cvar_VariableValue(va("ui_blueteam%i", ownerDraw-UI_BLUETEAM1 + 1)); 1674 if (value <= 0) { 1675 text = "Closed"; 1676 } else if (value == 1) { 1677 text = "Human"; 1678 } else { 1679 value -= 2; 1680 if (value >= uiInfo.aliasCount) { 1681 value = 0; 1682 } 1683 text = uiInfo.aliasList[value].name; 1684 } 1685 s = va("%i. %s", ownerDraw-UI_BLUETEAM1 + 1, text); 1686 break; 1687 case UI_REDTEAM1: 1688 case UI_REDTEAM2: 1689 case UI_REDTEAM3: 1690 case UI_REDTEAM4: 1691 case UI_REDTEAM5: 1692 value = trap_Cvar_VariableValue(va("ui_redteam%i", ownerDraw-UI_REDTEAM1 + 1)); 1693 if (value <= 0) { 1694 text = "Closed"; 1695 } else if (value == 1) { 1696 text = "Human"; 1697 } else { 1698 value -= 2; 1699 if (value >= uiInfo.aliasCount) { 1700 value = 0; 1701 } 1702 text = uiInfo.aliasList[value].name; 1703 } 1704 s = va("%i. %s", ownerDraw-UI_REDTEAM1 + 1, text); 1705 break; 1706 case UI_NETSOURCE: 1707 if (ui_netSource.integer < 0 || ui_netSource.integer > uiInfo.numJoinGameTypes) { 1708 ui_netSource.integer = 0; 1709 } 1710 s = va("Source: %s", netSources[ui_netSource.integer]); 1711 break; 1712 case UI_NETFILTER: 1713 if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { 1714 ui_serverFilterType.integer = 0; 1715 } 1716 s = va("Filter: %s", serverFilters[ui_serverFilterType.integer].description ); 1717 break; 1718 case UI_TIER: 1719 break; 1720 case UI_TIER_MAPNAME: 1721 break; 1722 case UI_TIER_GAMETYPE: 1723 break; 1724 case UI_ALLMAPS_SELECTION: 1725 break; 1726 case UI_OPPONENT_NAME: 1727 break; 1728 case UI_KEYBINDSTATUS: 1729 if (Display_KeyBindPending()) { 1730 s = "Waiting for new key... Press ESCAPE to cancel"; 1731 } else { 1732 s = "Press ENTER or CLICK to change, Press BACKSPACE to clear"; 1733 } 1734 break; 1735 case UI_SERVERREFRESHDATE: 1736 s = UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)); 1737 break; 1738 default: 1739 break; 1740 } 1741 1742 if (s) { 1743 return Text_Width(s, scale, 0); 1744 } 1745 return 0; 1746 } 1747 1748 static void UI_DrawBotName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1749 int value = uiInfo.botIndex; 1750 int game = trap_Cvar_VariableValue("g_gametype"); 1751 const char *text = ""; 1752 if (game >= GT_TEAM) { 1753 if (value >= uiInfo.characterCount) { 1754 value = 0; 1755 } 1756 text = uiInfo.characterList[value].name; 1757 } else { 1758 if (value >= UI_GetNumBots()) { 1759 value = 0; 1760 } 1761 text = UI_GetBotNameByNumber(value); 1762 } 1763 Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle); 1764 } 1765 1766 static void UI_DrawBotSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1767 if (uiInfo.skillIndex >= 0 && uiInfo.skillIndex < numSkillLevels) { 1768 Text_Paint(rect->x, rect->y, scale, color, skillLevels[uiInfo.skillIndex], 0, 0, textStyle); 1769 } 1770 } 1771 1772 static void UI_DrawRedBlue(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1773 Text_Paint(rect->x, rect->y, scale, color, (uiInfo.redBlue == 0) ? "Red" : "Blue", 0, 0, textStyle); 1774 } 1775 1776 static void UI_DrawCrosshair(rectDef_t *rect, float scale, vec4_t color) { 1777 trap_R_SetColor( color ); 1778 if (uiInfo.currentCrosshair < 0 || uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { 1779 uiInfo.currentCrosshair = 0; 1780 } 1781 UI_DrawHandlePic( rect->x, rect->y - rect->h, rect->w, rect->h, uiInfo.uiDC.Assets.crosshairShader[uiInfo.currentCrosshair]); 1782 trap_R_SetColor( NULL ); 1783 } 1784 1785 /* 1786 =============== 1787 UI_BuildPlayerList 1788 =============== 1789 */ 1790 static void UI_BuildPlayerList() { 1791 uiClientState_t cs; 1792 int n, count, team, team2, playerTeamNumber; 1793 char info[MAX_INFO_STRING]; 1794 1795 trap_GetClientState( &cs ); 1796 trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING ); 1797 uiInfo.playerNumber = cs.clientNum; 1798 uiInfo.teamLeader = atoi(Info_ValueForKey(info, "tl")); 1799 team = atoi(Info_ValueForKey(info, "t")); 1800 trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); 1801 count = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); 1802 uiInfo.playerCount = 0; 1803 uiInfo.myTeamCount = 0; 1804 playerTeamNumber = 0; 1805 for( n = 0; n < count; n++ ) { 1806 trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); 1807 1808 if (info[0]) { 1809 Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); 1810 Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] ); 1811 uiInfo.playerCount++; 1812 team2 = atoi(Info_ValueForKey(info, "t")); 1813 if (team2 == team) { 1814 Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); 1815 Q_CleanStr( uiInfo.teamNames[uiInfo.myTeamCount] ); 1816 uiInfo.teamClientNums[uiInfo.myTeamCount] = n; 1817 if (uiInfo.playerNumber == n) { 1818 playerTeamNumber = uiInfo.myTeamCount; 1819 } 1820 uiInfo.myTeamCount++; 1821 } 1822 } 1823 } 1824 1825 if (!uiInfo.teamLeader) { 1826 trap_Cvar_Set("cg_selectedPlayer", va("%d", playerTeamNumber)); 1827 } 1828 1829 n = trap_Cvar_VariableValue("cg_selectedPlayer"); 1830 if (n < 0 || n > uiInfo.myTeamCount) { 1831 n = 0; 1832 } 1833 if (n < uiInfo.myTeamCount) { 1834 trap_Cvar_Set("cg_selectedPlayerName", uiInfo.teamNames[n]); 1835 } 1836 } 1837 1838 1839 static void UI_DrawSelectedPlayer(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1840 if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { 1841 uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; 1842 UI_BuildPlayerList(); 1843 } 1844 Text_Paint(rect->x, rect->y, scale, color, (uiInfo.teamLeader) ? UI_Cvar_VariableString("cg_selectedPlayerName") : UI_Cvar_VariableString("name") , 0, 0, textStyle); 1845 } 1846 1847 static void UI_DrawServerRefreshDate(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1848 if (uiInfo.serverStatus.refreshActive) { 1849 vec4_t lowLight, newColor; 1850 lowLight[0] = 0.8 * color[0]; 1851 lowLight[1] = 0.8 * color[1]; 1852 lowLight[2] = 0.8 * color[2]; 1853 lowLight[3] = 0.8 * color[3]; 1854 LerpColor(color,lowLight,newColor,0.5+0.5*sin(uiInfo.uiDC.realTime / PULSE_DIVISOR)); 1855 Text_Paint(rect->x, rect->y, scale, newColor, va("Getting info for %d servers (ESC to cancel)", trap_LAN_GetServerCount(ui_netSource.integer)), 0, 0, textStyle); 1856 } else { 1857 char buff[64]; 1858 Q_strncpyz(buff, UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)), 64); 1859 Text_Paint(rect->x, rect->y, scale, color, va("Refresh Time: %s", buff), 0, 0, textStyle); 1860 } 1861 } 1862 1863 static void UI_DrawServerMOTD(rectDef_t *rect, float scale, vec4_t color) { 1864 if (uiInfo.serverStatus.motdLen) { 1865 float maxX; 1866 1867 if (uiInfo.serverStatus.motdWidth == -1) { 1868 uiInfo.serverStatus.motdWidth = 0; 1869 uiInfo.serverStatus.motdPaintX = rect->x + 1; 1870 uiInfo.serverStatus.motdPaintX2 = -1; 1871 } 1872 1873 if (uiInfo.serverStatus.motdOffset > uiInfo.serverStatus.motdLen) { 1874 uiInfo.serverStatus.motdOffset = 0; 1875 uiInfo.serverStatus.motdPaintX = rect->x + 1; 1876 uiInfo.serverStatus.motdPaintX2 = -1; 1877 } 1878 1879 if (uiInfo.uiDC.realTime > uiInfo.serverStatus.motdTime) { 1880 uiInfo.serverStatus.motdTime = uiInfo.uiDC.realTime + 10; 1881 if (uiInfo.serverStatus.motdPaintX <= rect->x + 2) { 1882 if (uiInfo.serverStatus.motdOffset < uiInfo.serverStatus.motdLen) { 1883 uiInfo.serverStatus.motdPaintX += Text_Width(&uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], scale, 1) - 1; 1884 uiInfo.serverStatus.motdOffset++; 1885 } else { 1886 uiInfo.serverStatus.motdOffset = 0; 1887 if (uiInfo.serverStatus.motdPaintX2 >= 0) { 1888 uiInfo.serverStatus.motdPaintX = uiInfo.serverStatus.motdPaintX2; 1889 } else { 1890 uiInfo.serverStatus.motdPaintX = rect->x + rect->w - 2; 1891 } 1892 uiInfo.serverStatus.motdPaintX2 = -1; 1893 } 1894 } else { 1895 //serverStatus.motdPaintX--; 1896 uiInfo.serverStatus.motdPaintX -= 2; 1897 if (uiInfo.serverStatus.motdPaintX2 >= 0) { 1898 //serverStatus.motdPaintX2--; 1899 uiInfo.serverStatus.motdPaintX2 -= 2; 1900 } 1901 } 1902 } 1903 1904 maxX = rect->x + rect->w - 2; 1905 Text_Paint_Limit(&maxX, uiInfo.serverStatus.motdPaintX, rect->y + rect->h - 3, scale, color, &uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], 0, 0); 1906 if (uiInfo.serverStatus.motdPaintX2 >= 0) { 1907 float maxX2 = rect->x + rect->w - 2; 1908 Text_Paint_Limit(&maxX2, uiInfo.serverStatus.motdPaintX2, rect->y + rect->h - 3, scale, color, uiInfo.serverStatus.motd, 0, uiInfo.serverStatus.motdOffset); 1909 } 1910 if (uiInfo.serverStatus.motdOffset && maxX > 0) { 1911 // if we have an offset ( we are skipping the first part of the string ) and we fit the string 1912 if (uiInfo.serverStatus.motdPaintX2 == -1) { 1913 uiInfo.serverStatus.motdPaintX2 = rect->x + rect->w - 2; 1914 } 1915 } else { 1916 uiInfo.serverStatus.motdPaintX2 = -1; 1917 } 1918 1919 } 1920 } 1921 1922 static void UI_DrawKeyBindStatus(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1923 // int ofs = 0; TTimo: unused 1924 if (Display_KeyBindPending()) { 1925 Text_Paint(rect->x, rect->y, scale, color, "Waiting for new key... Press ESCAPE to cancel", 0, 0, textStyle); 1926 } else { 1927 Text_Paint(rect->x, rect->y, scale, color, "Press ENTER or CLICK to change, Press BACKSPACE to clear", 0, 0, textStyle); 1928 } 1929 } 1930 1931 static void UI_DrawGLInfo(rectDef_t *rect, float scale, vec4_t color, int textStyle) { 1932 char * eptr; 1933 char buff[1024]; 1934 const char *lines[64]; 1935 int y, numLines, i; 1936 1937 Text_Paint(rect->x + 2, rect->y, scale, color, va("VENDOR: %s", uiInfo.uiDC.glconfig.vendor_string), 0, 30, textStyle); 1938 Text_Paint(rect->x + 2, rect->y + 15, scale, color, va("VERSION: %s: %s", uiInfo.uiDC.glconfig.version_string,uiInfo.uiDC.glconfig.renderer_string), 0, 30, textStyle); 1939 Text_Paint(rect->x + 2, rect->y + 30, scale, color, va ("PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)", uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits, uiInfo.uiDC.glconfig.stencilBits), 0, 30, textStyle); 1940 1941 // build null terminated extension strings 1942 // TTimo: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399 1943 // in TA this was not directly crashing, but displaying a nasty broken shader right in the middle 1944 // brought down the string size to 1024, there's not much that can be shown on the screen anyway 1945 Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 1024); 1946 eptr = buff; 1947 y = rect->y + 45; 1948 numLines = 0; 1949 while ( y < rect->y + rect->h && *eptr ) 1950 { 1951 while ( *eptr && *eptr == ' ' ) 1952 *eptr++ = '\0'; 1953 1954 // track start of valid string 1955 if (*eptr && *eptr != ' ') { 1956 lines[numLines++] = eptr; 1957 } 1958 1959 while ( *eptr && *eptr != ' ' ) 1960 eptr++; 1961 } 1962 1963 i = 0; 1964 while (i < numLines) { 1965 Text_Paint(rect->x + 2, y, scale, color, lines[i++], 0, 20, textStyle); 1966 if (i < numLines) { 1967 Text_Paint(rect->x + rect->w / 2, y, scale, color, lines[i++], 0, 20, textStyle); 1968 } 1969 y += 10; 1970 if (y > rect->y + rect->h - 11) { 1971 break; 1972 } 1973 } 1974 1975 1976 } 1977 1978 // FIXME: table drive 1979 // 1980 static void UI_OwnerDraw(float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle) { 1981 rectDef_t rect; 1982 1983 rect.x = x + text_x; 1984 rect.y = y + text_y; 1985 rect.w = w; 1986 rect.h = h; 1987 1988 switch (ownerDraw) { 1989 case UI_HANDICAP: 1990 UI_DrawHandicap(&rect, scale, color, textStyle); 1991 break; 1992 case UI_EFFECTS: 1993 UI_DrawEffects(&rect, scale, color); 1994 break; 1995 case UI_PLAYERMODEL: 1996 UI_DrawPlayerModel(&rect); 1997 break; 1998 case UI_CLANNAME: 1999 UI_DrawClanName(&rect, scale, color, textStyle); 2000 break; 2001 case UI_CLANLOGO: 2002 UI_DrawClanLogo(&rect, scale, color); 2003 break; 2004 case UI_CLANCINEMATIC: 2005 UI_DrawClanCinematic(&rect, scale, color); 2006 break; 2007 case UI_PREVIEWCINEMATIC: 2008 UI_DrawPreviewCinematic(&rect, scale, color); 2009 break; 2010 case UI_GAMETYPE: 2011 UI_DrawGameType(&rect, scale, color, textStyle); 2012 break; 2013 case UI_NETGAMETYPE: 2014 UI_DrawNetGameType(&rect, scale, color, textStyle); 2015 break; 2016 case UI_JOINGAMETYPE: 2017 UI_DrawJoinGameType(&rect, scale, color, textStyle); 2018 break; 2019 case UI_MAPPREVIEW: 2020 UI_DrawMapPreview(&rect, scale, color, qtrue); 2021 break; 2022 case UI_MAP_TIMETOBEAT: 2023 UI_DrawMapTimeToBeat(&rect, scale, color, textStyle); 2024 break; 2025 case UI_MAPCINEMATIC: 2026 UI_DrawMapCinematic(&rect, scale, color, qfalse); 2027 break; 2028 case UI_STARTMAPCINEMATIC: 2029 UI_DrawMapCinematic(&rect, scale, color, qtrue); 2030 break; 2031 case UI_SKILL: 2032 UI_DrawSkill(&rect, scale, color, textStyle); 2033 break; 2034 case UI_BLUETEAMNAME: 2035 UI_DrawTeamName(&rect, scale, color, qtrue, textStyle); 2036 break; 2037 case UI_REDTEAMNAME: 2038 UI_DrawTeamName(&rect, scale, color, qfalse, textStyle); 2039 break; 2040 case UI_BLUETEAM1: 2041 case UI_BLUETEAM2: 2042 case UI_BLUETEAM3: 2043 case UI_BLUETEAM4: 2044 case UI_BLUETEAM5: 2045 UI_DrawTeamMember(&rect, scale, color, qtrue, ownerDraw - UI_BLUETEAM1 + 1, textStyle); 2046 break; 2047 case UI_REDTEAM1: 2048 case UI_REDTEAM2: 2049 case UI_REDTEAM3: 2050 case UI_REDTEAM4: 2051 case UI_REDTEAM5: 2052 UI_DrawTeamMember(&rect, scale, color, qfalse, ownerDraw - UI_REDTEAM1 + 1, textStyle); 2053 break; 2054 case UI_NETSOURCE: 2055 UI_DrawNetSource(&rect, scale, color, textStyle); 2056 break; 2057 case UI_NETMAPPREVIEW: 2058 UI_DrawNetMapPreview(&rect, scale, color); 2059 break; 2060 case UI_NETMAPCINEMATIC: 2061 UI_DrawNetMapCinematic(&rect, scale, color); 2062 break; 2063 case UI_NETFILTER: 2064 UI_DrawNetFilter(&rect, scale, color, textStyle); 2065 break; 2066 case UI_TIER: 2067 UI_DrawTier(&rect, scale, color, textStyle); 2068 break; 2069 case UI_OPPONENTMODEL: 2070 UI_DrawOpponent(&rect); 2071 break; 2072 case UI_TIERMAP1: 2073 UI_DrawTierMap(&rect, 0); 2074 break; 2075 case UI_TIERMAP2: 2076 UI_DrawTierMap(&rect, 1); 2077 break; 2078 case UI_TIERMAP3: 2079 UI_DrawTierMap(&rect, 2); 2080 break; 2081 case UI_PLAYERLOGO: 2082 UI_DrawPlayerLogo(&rect, color); 2083 break; 2084 case UI_PLAYERLOGO_METAL: 2085 UI_DrawPlayerLogoMetal(&rect, color); 2086 break; 2087 case UI_PLAYERLOGO_NAME: 2088 UI_DrawPlayerLogoName(&rect, color); 2089 break; 2090 case UI_OPPONENTLOGO: 2091 UI_DrawOpponentLogo(&rect, color); 2092 break; 2093 case UI_OPPONENTLOGO_METAL: 2094 UI_DrawOpponentLogoMetal(&rect, color); 2095 break; 2096 case UI_OPPONENTLOGO_NAME: 2097 UI_DrawOpponentLogoName(&rect, color); 2098 break; 2099 case UI_TIER_MAPNAME: 2100 UI_DrawTierMapName(&rect, scale, color, textStyle); 2101 break; 2102 case UI_TIER_GAMETYPE: 2103 UI_DrawTierGameType(&rect, scale, color, textStyle); 2104 break; 2105 case UI_ALLMAPS_SELECTION: 2106 UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qtrue); 2107 break; 2108 case UI_MAPS_SELECTION: 2109 UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qfalse); 2110 break; 2111 case UI_OPPONENT_NAME: 2112 UI_DrawOpponentName(&rect, scale, color, textStyle); 2113 break; 2114 case UI_BOTNAME: 2115 UI_DrawBotName(&rect, scale, color, textStyle); 2116 break; 2117 case UI_BOTSKILL: 2118 UI_DrawBotSkill(&rect, scale, color, textStyle); 2119 break; 2120 case UI_REDBLUE: 2121 UI_DrawRedBlue(&rect, scale, color, textStyle); 2122 break; 2123 case UI_CROSSHAIR: 2124 UI_DrawCrosshair(&rect, scale, color); 2125 break; 2126 case UI_SELECTEDPLAYER: 2127 UI_DrawSelectedPlayer(&rect, scale, color, textStyle); 2128 break; 2129 case UI_SERVERREFRESHDATE: 2130 UI_DrawServerRefreshDate(&rect, scale, color, textStyle); 2131 break; 2132 case UI_SERVERMOTD: 2133 UI_DrawServerMOTD(&rect, scale, color); 2134 break; 2135 case UI_GLINFO: 2136 UI_DrawGLInfo(&rect,scale, color, textStyle); 2137 break; 2138 case UI_KEYBINDSTATUS: 2139 UI_DrawKeyBindStatus(&rect,scale, color, textStyle); 2140 break; 2141 default: 2142 break; 2143 } 2144 2145 } 2146 2147 static qboolean UI_OwnerDrawVisible(int flags) { 2148 qboolean vis = qtrue; 2149 2150 while (flags) { 2151 2152 if (flags & UI_SHOW_FFA) { 2153 if (trap_Cvar_VariableValue("g_gametype") != GT_FFA) { 2154 vis = qfalse; 2155 } 2156 flags &= ~UI_SHOW_FFA; 2157 } 2158 2159 if (flags & UI_SHOW_NOTFFA) { 2160 if (trap_Cvar_VariableValue("g_gametype") == GT_FFA) { 2161 vis = qfalse; 2162 } 2163 flags &= ~UI_SHOW_NOTFFA; 2164 } 2165 2166 if (flags & UI_SHOW_LEADER) { 2167 // these need to show when this client can give orders to a player or a group 2168 if (!uiInfo.teamLeader) { 2169 vis = qfalse; 2170 } else { 2171 // if showing yourself 2172 if (ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber) { 2173 vis = qfalse; 2174 } 2175 } 2176 flags &= ~UI_SHOW_LEADER; 2177 } 2178 if (flags & UI_SHOW_NOTLEADER) { 2179 // these need to show when this client is assigning their own status or they are NOT the leader 2180 if (uiInfo.teamLeader) { 2181 // if not showing yourself 2182 if (!(ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber)) { 2183 vis = qfalse; 2184 } 2185 // these need to show when this client can give orders to a player or a group 2186 } 2187 flags &= ~UI_SHOW_NOTLEADER; 2188 } 2189 if (flags & UI_SHOW_FAVORITESERVERS) { 2190 // this assumes you only put this type of display flag on something showing in the proper context 2191 if (ui_netSource.integer != AS_FAVORITES) { 2192 vis = qfalse; 2193 } 2194 flags &= ~UI_SHOW_FAVORITESERVERS; 2195 } 2196 if (flags & UI_SHOW_NOTFAVORITESERVERS) { 2197 // this assumes you only put this type of display flag on something showing in the proper context 2198 if (ui_netSource.integer == AS_FAVORITES) { 2199 vis = qfalse; 2200 } 2201 flags &= ~UI_SHOW_NOTFAVORITESERVERS; 2202 } 2203 if (flags & UI_SHOW_ANYTEAMGAME) { 2204 if (uiInfo.gameTypes[ui_gameType.integer].gtEnum <= GT_TEAM ) { 2205 vis = qfalse; 2206 } 2207 flags &= ~UI_SHOW_ANYTEAMGAME; 2208 } 2209 if (flags & UI_SHOW_ANYNONTEAMGAME) { 2210 if (uiInfo.gameTypes[ui_gameType.integer].gtEnum > GT_TEAM ) { 2211 vis = qfalse; 2212 } 2213 flags &= ~UI_SHOW_ANYNONTEAMGAME; 2214 } 2215 if (flags & UI_SHOW_NETANYTEAMGAME) { 2216 if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum <= GT_TEAM ) { 2217 vis = qfalse; 2218 } 2219 flags &= ~UI_SHOW_NETANYTEAMGAME; 2220 } 2221 if (flags & UI_SHOW_NETANYNONTEAMGAME) { 2222 if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum > GT_TEAM ) { 2223 vis = qfalse; 2224 } 2225 flags &= ~UI_SHOW_NETANYNONTEAMGAME; 2226 } 2227 if (flags & UI_SHOW_NEWHIGHSCORE) { 2228 if (uiInfo.newHighScoreTime < uiInfo.uiDC.realTime) { 2229 vis = qfalse; 2230 } else { 2231 if (uiInfo.soundHighScore) { 2232 if (trap_Cvar_VariableValue("sv_killserver") == 0) { 2233 // wait on server to go down before playing sound 2234 trap_S_StartLocalSound(uiInfo.newHighScoreSound, CHAN_ANNOUNCER); 2235 uiInfo.soundHighScore = qfalse; 2236 } 2237 } 2238 } 2239 flags &= ~UI_SHOW_NEWHIGHSCORE; 2240 } 2241 if (flags & UI_SHOW_NEWBESTTIME) { 2242 if (uiInfo.newBestTime < uiInfo.uiDC.realTime) { 2243 vis = qfalse; 2244 } 2245 flags &= ~UI_SHOW_NEWBESTTIME; 2246 } 2247 if (flags & UI_SHOW_DEMOAVAILABLE) { 2248 if (!uiInfo.demoAvailable) { 2249 vis = qfalse; 2250 } 2251 flags &= ~UI_SHOW_DEMOAVAILABLE; 2252 } else { 2253 flags = 0; 2254 } 2255 } 2256 return vis; 2257 } 2258 2259 static qboolean UI_Handicap_HandleKey(int flags, float *special, int key) { 2260 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2261 int h; 2262 h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") ); 2263 if (key == K_MOUSE2) { 2264 h -= 5; 2265 } else { 2266 h += 5; 2267 } 2268 if (h > 100) { 2269 h = 5; 2270 } else if (h < 0) { 2271 h = 100; 2272 } 2273 trap_Cvar_Set( "handicap", va( "%i", h) ); 2274 return qtrue; 2275 } 2276 return qfalse; 2277 } 2278 2279 static qboolean UI_Effects_HandleKey(int flags, float *special, int key) { 2280 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2281 2282 if (key == K_MOUSE2) { 2283 uiInfo.effectsColor--; 2284 } else { 2285 uiInfo.effectsColor++; 2286 } 2287 2288 if( uiInfo.effectsColor > 6 ) { 2289 uiInfo.effectsColor = 0; 2290 } else if (uiInfo.effectsColor < 0) { 2291 uiInfo.effectsColor = 6; 2292 } 2293 2294 trap_Cvar_SetValue( "color1", uitogamecode[uiInfo.effectsColor] ); 2295 return qtrue; 2296 } 2297 return qfalse; 2298 } 2299 2300 static qboolean UI_ClanName_HandleKey(int flags, float *special, int key) { 2301 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2302 int i; 2303 i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 2304 if (uiInfo.teamList[i].cinematic >= 0) { 2305 trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic); 2306 uiInfo.teamList[i].cinematic = -1; 2307 } 2308 if (key == K_MOUSE2) { 2309 i--; 2310 } else { 2311 i++; 2312 } 2313 if (i >= uiInfo.teamCount) { 2314 i = 0; 2315 } else if (i < 0) { 2316 i = uiInfo.teamCount - 1; 2317 } 2318 trap_Cvar_Set( "ui_teamName", uiInfo.teamList[i].teamName); 2319 UI_HeadCountByTeam(); 2320 UI_FeederSelection(FEEDER_HEADS, 0); 2321 updateModel = qtrue; 2322 return qtrue; 2323 } 2324 return qfalse; 2325 } 2326 2327 static qboolean UI_GameType_HandleKey(int flags, float *special, int key, qboolean resetMap) { 2328 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2329 int oldCount = UI_MapCountByGameType(qtrue); 2330 2331 // hard coded mess here 2332 if (key == K_MOUSE2) { 2333 ui_gameType.integer--; 2334 if (ui_gameType.integer == 2) { 2335 ui_gameType.integer = 1; 2336 } else if (ui_gameType.integer < 2) { 2337 ui_gameType.integer = uiInfo.numGameTypes - 1; 2338 } 2339 } else { 2340 ui_gameType.integer++; 2341 if (ui_gameType.integer >= uiInfo.numGameTypes) { 2342 ui_gameType.integer = 1; 2343 } else if (ui_gameType.integer == 2) { 2344 ui_gameType.integer = 3; 2345 } 2346 } 2347 2348 if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_TOURNAMENT) { 2349 trap_Cvar_Set("ui_Q3Model", "1"); 2350 } else { 2351 trap_Cvar_Set("ui_Q3Model", "0"); 2352 } 2353 2354 trap_Cvar_Set("ui_gameType", va("%d", ui_gameType.integer)); 2355 UI_SetCapFragLimits(qtrue); 2356 UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); 2357 if (resetMap && oldCount != UI_MapCountByGameType(qtrue)) { 2358 trap_Cvar_Set( "ui_currentMap", "0"); 2359 Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, NULL); 2360 } 2361 return qtrue; 2362 } 2363 return qfalse; 2364 } 2365 2366 static qboolean UI_NetGameType_HandleKey(int flags, float *special, int key) { 2367 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2368 2369 if (key == K_MOUSE2) { 2370 ui_netGameType.integer--; 2371 } else { 2372 ui_netGameType.integer++; 2373 } 2374 2375 if (ui_netGameType.integer < 0) { 2376 ui_netGameType.integer = uiInfo.numGameTypes - 1; 2377 } else if (ui_netGameType.integer >= uiInfo.numGameTypes) { 2378 ui_netGameType.integer = 0; 2379 } 2380 2381 trap_Cvar_Set( "ui_netGameType", va("%d", ui_netGameType.integer)); 2382 trap_Cvar_Set( "ui_actualnetGameType", va("%d", uiInfo.gameTypes[ui_netGameType.integer].gtEnum)); 2383 trap_Cvar_Set( "ui_currentNetMap", "0"); 2384 UI_MapCountByGameType(qfalse); 2385 Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, NULL); 2386 return qtrue; 2387 } 2388 return qfalse; 2389 } 2390 2391 static qboolean UI_JoinGameType_HandleKey(int flags, float *special, int key) { 2392 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2393 2394 if (key == K_MOUSE2) { 2395 ui_joinGameType.integer--; 2396 } else { 2397 ui_joinGameType.integer++; 2398 } 2399 2400 if (ui_joinGameType.integer < 0) { 2401 ui_joinGameType.integer = uiInfo.numJoinGameTypes - 1; 2402 } else if (ui_joinGameType.integer >= uiInfo.numJoinGameTypes) { 2403 ui_joinGameType.integer = 0; 2404 } 2405 2406 trap_Cvar_Set( "ui_joinGameType", va("%d", ui_joinGameType.integer)); 2407 UI_BuildServerDisplayList(qtrue); 2408 return qtrue; 2409 } 2410 return qfalse; 2411 } 2412 2413 2414 2415 static qboolean UI_Skill_HandleKey(int flags, float *special, int key) { 2416 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2417 int i = trap_Cvar_VariableValue( "g_spSkill" ); 2418 2419 if (key == K_MOUSE2) { 2420 i--; 2421 } else { 2422 i++; 2423 } 2424 2425 if (i < 1) { 2426 i = numSkillLevels; 2427 } else if (i > numSkillLevels) { 2428 i = 1; 2429 } 2430 2431 trap_Cvar_Set("g_spSkill", va("%i", i)); 2432 return qtrue; 2433 } 2434 return qfalse; 2435 } 2436 2437 static qboolean UI_TeamName_HandleKey(int flags, float *special, int key, qboolean blue) { 2438 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2439 int i; 2440 i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam")); 2441 2442 if (key == K_MOUSE2) { 2443 i--; 2444 } else { 2445 i++; 2446 } 2447 2448 if (i >= uiInfo.teamCount) { 2449 i = 0; 2450 } else if (i < 0) { 2451 i = uiInfo.teamCount - 1; 2452 } 2453 2454 trap_Cvar_Set( (blue) ? "ui_blueTeam" : "ui_redTeam", uiInfo.teamList[i].teamName); 2455 2456 return qtrue; 2457 } 2458 return qfalse; 2459 } 2460 2461 static qboolean UI_TeamMember_HandleKey(int flags, float *special, int key, qboolean blue, int num) { 2462 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2463 // 0 - None 2464 // 1 - Human 2465 // 2..NumCharacters - Bot 2466 char *cvar = va(blue ? "ui_blueteam%i" : "ui_redteam%i", num); 2467 int value = trap_Cvar_VariableValue(cvar); 2468 2469 if (key == K_MOUSE2) { 2470 value--; 2471 } else { 2472 value++; 2473 } 2474 2475 if (ui_actualNetGameType.integer >= GT_TEAM) { 2476 if (value >= uiInfo.characterCount + 2) { 2477 value = 0; 2478 } else if (value < 0) { 2479 value = uiInfo.characterCount + 2 - 1; 2480 } 2481 } else { 2482 if (value >= UI_GetNumBots() + 2) { 2483 value = 0; 2484 } else if (value < 0) { 2485 value = UI_GetNumBots() + 2 - 1; 2486 } 2487 } 2488 2489 trap_Cvar_Set(cvar, va("%i", value)); 2490 return qtrue; 2491 } 2492 return qfalse; 2493 } 2494 2495 static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) { 2496 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2497 2498 if (key == K_MOUSE2) { 2499 ui_netSource.integer--; 2500 if (ui_netSource.integer == AS_MPLAYER) 2501 ui_netSource.integer--; 2502 } else { 2503 ui_netSource.integer++; 2504 if (ui_netSource.integer == AS_MPLAYER) 2505 ui_netSource.integer++; 2506 } 2507 2508 if (ui_netSource.integer >= numNetSources) { 2509 ui_netSource.integer = 0; 2510 } else if (ui_netSource.integer < 0) { 2511 ui_netSource.integer = numNetSources - 1; 2512 } 2513 2514 UI_BuildServerDisplayList(qtrue); 2515 if (ui_netSource.integer != AS_GLOBAL) { 2516 UI_StartServerRefresh(qtrue); 2517 } 2518 trap_Cvar_Set( "ui_netSource", va("%d", ui_netSource.integer)); 2519 return qtrue; 2520 } 2521 return qfalse; 2522 } 2523 2524 static qboolean UI_NetFilter_HandleKey(int flags, float *special, int key) { 2525 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2526 2527 if (key == K_MOUSE2) { 2528 ui_serverFilterType.integer--; 2529 } else { 2530 ui_serverFilterType.integer++; 2531 } 2532 2533 if (ui_serverFilterType.integer >= numServerFilters) { 2534 ui_serverFilterType.integer = 0; 2535 } else if (ui_serverFilterType.integer < 0) { 2536 ui_serverFilterType.integer = numServerFilters - 1; 2537 } 2538 UI_BuildServerDisplayList(qtrue); 2539 return qtrue; 2540 } 2541 return qfalse; 2542 } 2543 2544 static qboolean UI_OpponentName_HandleKey(int flags, float *special, int key) { 2545 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2546 if (key == K_MOUSE2) { 2547 UI_PriorOpponent(); 2548 } else { 2549 UI_NextOpponent(); 2550 } 2551 return qtrue; 2552 } 2553 return qfalse; 2554 } 2555 2556 static qboolean UI_BotName_HandleKey(int flags, float *special, int key) { 2557 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2558 int game = trap_Cvar_VariableValue("g_gametype"); 2559 int value = uiInfo.botIndex; 2560 2561 if (key == K_MOUSE2) { 2562 value--; 2563 } else { 2564 value++; 2565 } 2566 2567 if (game >= GT_TEAM) { 2568 if (value >= uiInfo.characterCount + 2) { 2569 value = 0; 2570 } else if (value < 0) { 2571 value = uiInfo.characterCount + 2 - 1; 2572 } 2573 } else { 2574 if (value >= UI_GetNumBots() + 2) { 2575 value = 0; 2576 } else if (value < 0) { 2577 value = UI_GetNumBots() + 2 - 1; 2578 } 2579 } 2580 uiInfo.botIndex = value; 2581 return qtrue; 2582 } 2583 return qfalse; 2584 } 2585 2586 static qboolean UI_BotSkill_HandleKey(int flags, float *special, int key) { 2587 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2588 if (key == K_MOUSE2) { 2589 uiInfo.skillIndex--; 2590 } else { 2591 uiInfo.skillIndex++; 2592 } 2593 if (uiInfo.skillIndex >= numSkillLevels) { 2594 uiInfo.skillIndex = 0; 2595 } else if (uiInfo.skillIndex < 0) { 2596 uiInfo.skillIndex = numSkillLevels-1; 2597 } 2598 return qtrue; 2599 } 2600 return qfalse; 2601 } 2602 2603 static qboolean UI_RedBlue_HandleKey(int flags, float *special, int key) { 2604 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2605 uiInfo.redBlue ^= 1; 2606 return qtrue; 2607 } 2608 return qfalse; 2609 } 2610 2611 static qboolean UI_Crosshair_HandleKey(int flags, float *special, int key) { 2612 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2613 if (key == K_MOUSE2) { 2614 uiInfo.currentCrosshair--; 2615 } else { 2616 uiInfo.currentCrosshair++; 2617 } 2618 2619 if (uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { 2620 uiInfo.currentCrosshair = 0; 2621 } else if (uiInfo.currentCrosshair < 0) { 2622 uiInfo.currentCrosshair = NUM_CROSSHAIRS - 1; 2623 } 2624 trap_Cvar_Set("cg_drawCrosshair", va("%d", uiInfo.currentCrosshair)); 2625 return qtrue; 2626 } 2627 return qfalse; 2628 } 2629 2630 2631 2632 static qboolean UI_SelectedPlayer_HandleKey(int flags, float *special, int key) { 2633 if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) { 2634 int selected; 2635 2636 UI_BuildPlayerList(); 2637 if (!uiInfo.teamLeader) { 2638 return qfalse; 2639 } 2640 selected = trap_Cvar_VariableValue("cg_selectedPlayer"); 2641 2642 if (key == K_MOUSE2) { 2643 selected--; 2644 } else { 2645 selected++; 2646 } 2647 2648 if (selected > uiInfo.myTeamCount) { 2649 selected = 0; 2650 } else if (selected < 0) { 2651 selected = uiInfo.myTeamCount; 2652 } 2653 2654 if (selected == uiInfo.myTeamCount) { 2655 trap_Cvar_Set( "cg_selectedPlayerName", "Everyone"); 2656 } else { 2657 trap_Cvar_Set( "cg_selectedPlayerName", uiInfo.teamNames[selected]); 2658 } 2659 trap_Cvar_Set( "cg_selectedPlayer", va("%d", selected)); 2660 } 2661 return qfalse; 2662 } 2663 2664 2665 static qboolean UI_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key) { 2666 switch (ownerDraw) { 2667 case UI_HANDICAP: 2668 return UI_Handicap_HandleKey(flags, special, key); 2669 break; 2670 case UI_EFFECTS: 2671 return UI_Effects_HandleKey(flags, special, key); 2672 break; 2673 case UI_CLANNAME: 2674 return UI_ClanName_HandleKey(flags, special, key); 2675 break; 2676 case UI_GAMETYPE: 2677 return UI_GameType_HandleKey(flags, special, key, qtrue); 2678 break; 2679 case UI_NETGAMETYPE: 2680 return UI_NetGameType_HandleKey(flags, special, key); 2681 break; 2682 case UI_JOINGAMETYPE: 2683 return UI_JoinGameType_HandleKey(flags, special, key); 2684 break; 2685 case UI_SKILL: 2686 return UI_Skill_HandleKey(flags, special, key); 2687 break; 2688 case UI_BLUETEAMNAME: 2689 return UI_TeamName_HandleKey(flags, special, key, qtrue); 2690 break; 2691 case UI_REDTEAMNAME: 2692 return UI_TeamName_HandleKey(flags, special, key, qfalse); 2693 break; 2694 case UI_BLUETEAM1: 2695 case UI_BLUETEAM2: 2696 case UI_BLUETEAM3: 2697 case UI_BLUETEAM4: 2698 case UI_BLUETEAM5: 2699 UI_TeamMember_HandleKey(flags, special, key, qtrue, ownerDraw - UI_BLUETEAM1 + 1); 2700 break; 2701 case UI_REDTEAM1: 2702 case UI_REDTEAM2: 2703 case UI_REDTEAM3: 2704 case UI_REDTEAM4: 2705 case UI_REDTEAM5: 2706 UI_TeamMember_HandleKey(flags, special, key, qfalse, ownerDraw - UI_REDTEAM1 + 1); 2707 break; 2708 case UI_NETSOURCE: 2709 UI_NetSource_HandleKey(flags, special, key); 2710 break; 2711 case UI_NETFILTER: 2712 UI_NetFilter_HandleKey(flags, special, key); 2713 break; 2714 case UI_OPPONENT_NAME: 2715 UI_OpponentName_HandleKey(flags, special, key); 2716 break; 2717 case UI_BOTNAME: 2718 return UI_BotName_HandleKey(flags, special, key); 2719 break; 2720 case UI_BOTSKILL: 2721 return UI_BotSkill_HandleKey(flags, special, key); 2722 break; 2723 case UI_REDBLUE: 2724 UI_RedBlue_HandleKey(flags, special, key); 2725 break; 2726 case UI_CROSSHAIR: 2727 UI_Crosshair_HandleKey(flags, special, key); 2728 break; 2729 case UI_SELECTEDPLAYER: 2730 UI_SelectedPlayer_HandleKey(flags, special, key); 2731 break; 2732 default: 2733 break; 2734 } 2735 2736 return qfalse; 2737 } 2738 2739 2740 static float UI_GetValue(int ownerDraw) { 2741 return 0; 2742 } 2743 2744 /* 2745 ================= 2746 UI_ServersQsortCompare 2747 ================= 2748 */ 2749 static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 ) { 2750 return trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey, uiInfo.serverStatus.sortDir, *(int*)arg1, *(int*)arg2); 2751 } 2752 2753 2754 /* 2755 ================= 2756 UI_ServersSort 2757 ================= 2758 */ 2759 void UI_ServersSort(int column, qboolean force) { 2760 2761 if ( !force ) { 2762 if ( uiInfo.serverStatus.sortKey == column ) { 2763 return; 2764 } 2765 } 2766 2767 uiInfo.serverStatus.sortKey = column; 2768 qsort( &uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers, sizeof(int), UI_ServersQsortCompare); 2769 } 2770 2771 /* 2772 static void UI_StartSinglePlayer() { 2773 int i,j, k, skill; 2774 char buff[1024]; 2775 i = trap_Cvar_VariableValue( "ui_currentTier" ); 2776 if (i < 0 || i >= tierCount) { 2777 i = 0; 2778 } 2779 j = trap_Cvar_VariableValue("ui_currentMap"); 2780 if (j < 0 || j > MAPS_PER_TIER) { 2781 j = 0; 2782 } 2783 2784 trap_Cvar_SetValue( "singleplayer", 1 ); 2785 trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 7, tierList[i].gameTypes[j] ) ); 2786 trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", tierList[i].maps[j] ) ); 2787 skill = trap_Cvar_VariableValue( "g_spSkill" ); 2788 2789 if (j == MAPS_PER_TIER-1) { 2790 k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 2791 Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[0]), skill, "", teamList[k].teamMembers[0]); 2792 } else { 2793 k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 2794 for (i = 0; i < PLAYERS_PER_TEAM; i++) { 2795 Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Blue", teamList[k].teamMembers[i]); 2796 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 2797 } 2798 2799 k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 2800 for (i = 1; i < PLAYERS_PER_TEAM; i++) { 2801 Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Red", teamList[k].teamMembers[i]); 2802 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 2803 } 2804 trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); 2805 } 2806 2807 2808 } 2809 */ 2810 2811 /* 2812 =============== 2813 UI_LoadMods 2814 =============== 2815 */ 2816 static void UI_LoadMods() { 2817 int numdirs; 2818 char dirlist[2048]; 2819 char *dirptr; 2820 char *descptr; 2821 int i; 2822 int dirlen; 2823 2824 uiInfo.modCount = 0; 2825 numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) ); 2826 dirptr = dirlist; 2827 for( i = 0; i < numdirs; i++ ) { 2828 dirlen = strlen( dirptr ) + 1; 2829 descptr = dirptr + dirlen; 2830 uiInfo.modList[uiInfo.modCount].modName = String_Alloc(dirptr); 2831 uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc(descptr); 2832 dirptr += dirlen + strlen(descptr) + 1; 2833 uiInfo.modCount++; 2834 if (uiInfo.modCount >= MAX_MODS) { 2835 break; 2836 } 2837 } 2838 2839 } 2840 2841 2842 /* 2843 =============== 2844 UI_LoadTeams 2845 =============== 2846 */ 2847 static void UI_LoadTeams() { 2848 char teamList[4096]; 2849 char *teamName; 2850 int i, len, count; 2851 2852 count = trap_FS_GetFileList( "", "team", teamList, 4096 ); 2853 2854 if (count) { 2855 teamName = teamList; 2856 for ( i = 0; i < count; i++ ) { 2857 len = strlen( teamName ); 2858 UI_ParseTeamInfo(teamName); 2859 teamName += len + 1; 2860 } 2861 } 2862 2863 } 2864 2865 2866 /* 2867 =============== 2868 UI_LoadMovies 2869 =============== 2870 */ 2871 static void UI_LoadMovies() { 2872 char movielist[4096]; 2873 char *moviename; 2874 int i, len; 2875 2876 uiInfo.movieCount = trap_FS_GetFileList( "video", "roq", movielist, 4096 ); 2877 2878 if (uiInfo.movieCount) { 2879 if (uiInfo.movieCount > MAX_MOVIES) { 2880 uiInfo.movieCount = MAX_MOVIES; 2881 } 2882 moviename = movielist; 2883 for ( i = 0; i < uiInfo.movieCount; i++ ) { 2884 len = strlen( moviename ); 2885 if (!Q_stricmp(moviename + len - 4,".roq")) { 2886 moviename[len-4] = '\0'; 2887 } 2888 Q_strupr(moviename); 2889 uiInfo.movieList[i] = String_Alloc(moviename); 2890 moviename += len + 1; 2891 } 2892 } 2893 2894 } 2895 2896 2897 2898 /* 2899 =============== 2900 UI_LoadDemos 2901 =============== 2902 */ 2903 static void UI_LoadDemos() { 2904 char demolist[4096]; 2905 char demoExt[32]; 2906 char *demoname; 2907 int i, len; 2908 2909 Com_sprintf(demoExt, sizeof(demoExt), "dm_%d", (int)trap_Cvar_VariableValue("protocol")); 2910 2911 uiInfo.demoCount = trap_FS_GetFileList( "demos", demoExt, demolist, 4096 ); 2912 2913 Com_sprintf(demoExt, sizeof(demoExt), ".dm_%d", (int)trap_Cvar_VariableValue("protocol")); 2914 2915 if (uiInfo.demoCount) { 2916 if (uiInfo.demoCount > MAX_DEMOS) { 2917 uiInfo.demoCount = MAX_DEMOS; 2918 } 2919 demoname = demolist; 2920 for ( i = 0; i < uiInfo.demoCount; i++ ) { 2921 len = strlen( demoname ); 2922 if (!Q_stricmp(demoname + len - strlen(demoExt), demoExt)) { 2923 demoname[len-strlen(demoExt)] = '\0'; 2924 } 2925 Q_strupr(demoname); 2926 uiInfo.demoList[i] = String_Alloc(demoname); 2927 demoname += len + 1; 2928 } 2929 } 2930 2931 } 2932 2933 2934 static qboolean UI_SetNextMap(int actual, int index) { 2935 int i; 2936 for (i = actual + 1; i < uiInfo.mapCount; i++) { 2937 if (uiInfo.mapList[i].active) { 2938 Menu_SetFeederSelection(NULL, FEEDER_MAPS, index + 1, "skirmish"); 2939 return qtrue; 2940 } 2941 } 2942 return qfalse; 2943 } 2944 2945 2946 static void UI_StartSkirmish(qboolean next) { 2947 int i, k, g, delay, temp; 2948 float skill; 2949 char buff[MAX_STRING_CHARS]; 2950 2951 if (next) { 2952 int actual; 2953 int index = trap_Cvar_VariableValue("ui_mapIndex"); 2954 UI_MapCountByGameType(qtrue); 2955 UI_SelectedMap(index, &actual); 2956 if (UI_SetNextMap(actual, index)) { 2957 } else { 2958 UI_GameType_HandleKey(0, 0, K_MOUSE1, qfalse); 2959 UI_MapCountByGameType(qtrue); 2960 Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, "skirmish"); 2961 } 2962 } 2963 2964 g = uiInfo.gameTypes[ui_gameType.integer].gtEnum; 2965 trap_Cvar_SetValue( "g_gametype", g ); 2966 trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName) ); 2967 skill = trap_Cvar_VariableValue( "g_spSkill" ); 2968 trap_Cvar_Set("ui_scoreMap", uiInfo.mapList[ui_currentMap.integer].mapName); 2969 2970 k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); 2971 2972 trap_Cvar_Set("ui_singlePlayerActive", "1"); 2973 2974 // set up sp overrides, will be replaced on postgame 2975 temp = trap_Cvar_VariableValue( "capturelimit" ); 2976 trap_Cvar_Set("ui_saveCaptureLimit", va("%i", temp)); 2977 temp = trap_Cvar_VariableValue( "fraglimit" ); 2978 trap_Cvar_Set("ui_saveFragLimit", va("%i", temp)); 2979 2980 UI_SetCapFragLimits(qfalse); 2981 2982 temp = trap_Cvar_VariableValue( "cg_drawTimer" ); 2983 trap_Cvar_Set("ui_drawTimer", va("%i", temp)); 2984 temp = trap_Cvar_VariableValue( "g_doWarmup" ); 2985 trap_Cvar_Set("ui_doWarmup", va("%i", temp)); 2986 temp = trap_Cvar_VariableValue( "g_friendlyFire" ); 2987 trap_Cvar_Set("ui_friendlyFire", va("%i", temp)); 2988 temp = trap_Cvar_VariableValue( "sv_maxClients" ); 2989 trap_Cvar_Set("ui_maxClients", va("%i", temp)); 2990 temp = trap_Cvar_VariableValue( "g_warmup" ); 2991 trap_Cvar_Set("ui_Warmup", va("%i", temp)); 2992 temp = trap_Cvar_VariableValue( "sv_pure" ); 2993 trap_Cvar_Set("ui_pure", va("%i", temp)); 2994 2995 trap_Cvar_Set("cg_cameraOrbit", "0"); 2996 trap_Cvar_Set("cg_thirdPerson", "0"); 2997 trap_Cvar_Set("cg_drawTimer", "1"); 2998 trap_Cvar_Set("g_doWarmup", "1"); 2999 trap_Cvar_Set("g_warmup", "15"); 3000 trap_Cvar_Set("sv_pure", "0"); 3001 trap_Cvar_Set("g_friendlyFire", "0"); 3002 trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName")); 3003 trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName")); 3004 3005 if (trap_Cvar_VariableValue("ui_recordSPDemo")) { 3006 Com_sprintf(buff, MAX_STRING_CHARS, "%s_%i", uiInfo.mapList[ui_currentMap.integer].mapLoadName, g); 3007 trap_Cvar_Set("ui_recordSPDemoName", buff); 3008 } 3009 3010 delay = 500; 3011 3012 if (g == GT_TOURNAMENT) { 3013 trap_Cvar_Set("sv_maxClients", "2"); 3014 Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %f "", %i \n", uiInfo.mapList[ui_currentMap.integer].opponentName, skill, delay); 3015 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 3016 } else { 3017 temp = uiInfo.mapList[ui_currentMap.integer].teamMembers * 2; 3018 trap_Cvar_Set("sv_maxClients", va("%d", temp)); 3019 for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers; i++) { 3020 Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, (g == GT_FFA) ? "" : "Blue", delay, uiInfo.teamList[k].teamMembers[i]); 3021 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 3022 delay += 500; 3023 } 3024 k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 3025 for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers-1; i++) { 3026 Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, (g == GT_FFA) ? "" : "Red", delay, uiInfo.teamList[k].teamMembers[i]); 3027 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 3028 delay += 500; 3029 } 3030 } 3031 if (g >= GT_TEAM ) { 3032 trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); 3033 } 3034 } 3035 3036 static void UI_Update(const char *name) { 3037 int val = trap_Cvar_VariableValue(name); 3038 3039 if (Q_stricmp(name, "ui_SetName") == 0) { 3040 trap_Cvar_Set( "name", UI_Cvar_VariableString("ui_Name")); 3041 } else if (Q_stricmp(name, "ui_setRate") == 0) { 3042 float rate = trap_Cvar_VariableValue("rate"); 3043 if (rate >= 5000) { 3044 trap_Cvar_Set("cl_maxpackets", "30"); 3045 trap_Cvar_Set("cl_packetdup", "1"); 3046 } else if (rate >= 4000) { 3047 trap_Cvar_Set("cl_maxpackets", "15"); 3048 trap_Cvar_Set("cl_packetdup", "2"); // favor less prediction errors when there's packet loss 3049 } else { 3050 trap_Cvar_Set("cl_maxpackets", "15"); 3051 trap_Cvar_Set("cl_packetdup", "1"); // favor lower bandwidth 3052 } 3053 } else if (Q_stricmp(name, "ui_GetName") == 0) { 3054 trap_Cvar_Set( "ui_Name", UI_Cvar_VariableString("name")); 3055 } else if (Q_stricmp(name, "r_colorbits") == 0) { 3056 switch (val) { 3057 case 0: 3058 trap_Cvar_SetValue( "r_depthbits", 0 ); 3059 trap_Cvar_SetValue( "r_stencilbits", 0 ); 3060 break; 3061 case 16: 3062 trap_Cvar_SetValue( "r_depthbits", 16 ); 3063 trap_Cvar_SetValue( "r_stencilbits", 0 ); 3064 break; 3065 case 32: 3066 trap_Cvar_SetValue( "r_depthbits", 24 ); 3067 break; 3068 } 3069 } else if (Q_stricmp(name, "r_lodbias") == 0) { 3070 switch (val) { 3071 case 0: 3072 trap_Cvar_SetValue( "r_subdivisions", 4 ); 3073 break; 3074 case 1: 3075 trap_Cvar_SetValue( "r_subdivisions", 12 ); 3076 break; 3077 case 2: 3078 trap_Cvar_SetValue( "r_subdivisions", 20 ); 3079 break; 3080 } 3081 } else if (Q_stricmp(name, "ui_glCustom") == 0) { 3082 switch (val) { 3083 case 0: // high quality 3084 trap_Cvar_SetValue( "r_fullScreen", 1 ); 3085 trap_Cvar_SetValue( "r_subdivisions", 4 ); 3086 trap_Cvar_SetValue( "r_vertexlight", 0 ); 3087 trap_Cvar_SetValue( "r_lodbias", 0 ); 3088 trap_Cvar_SetValue( "r_colorbits", 32 ); 3089 trap_Cvar_SetValue( "r_depthbits", 24 ); 3090 trap_Cvar_SetValue( "r_picmip", 0 ); 3091 trap_Cvar_SetValue( "r_mode", 4 ); 3092 trap_Cvar_SetValue( "r_texturebits", 32 ); 3093 trap_Cvar_SetValue( "r_fastSky", 0 ); 3094 trap_Cvar_SetValue( "r_inGameVideo", 1 ); 3095 trap_Cvar_SetValue( "cg_shadows", 1 ); 3096 trap_Cvar_SetValue( "cg_brassTime", 2500 ); 3097 trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); 3098 break; 3099 case 1: // normal 3100 trap_Cvar_SetValue( "r_fullScreen", 1 ); 3101 trap_Cvar_SetValue( "r_subdivisions", 12 ); 3102 trap_Cvar_SetValue( "r_vertexlight", 0 ); 3103 trap_Cvar_SetValue( "r_lodbias", 0 ); 3104 trap_Cvar_SetValue( "r_colorbits", 0 ); 3105 trap_Cvar_SetValue( "r_depthbits", 24 ); 3106 trap_Cvar_SetValue( "r_picmip", 1 ); 3107 trap_Cvar_SetValue( "r_mode", 3 ); 3108 trap_Cvar_SetValue( "r_texturebits", 0 ); 3109 trap_Cvar_SetValue( "r_fastSky", 0 ); 3110 trap_Cvar_SetValue( "r_inGameVideo", 1 ); 3111 trap_Cvar_SetValue( "cg_brassTime", 2500 ); 3112 trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); 3113 trap_Cvar_SetValue( "cg_shadows", 0 ); 3114 break; 3115 case 2: // fast 3116 trap_Cvar_SetValue( "r_fullScreen", 1 ); 3117 trap_Cvar_SetValue( "r_subdivisions", 8 ); 3118 trap_Cvar_SetValue( "r_vertexlight", 0 ); 3119 trap_Cvar_SetValue( "r_lodbias", 1 ); 3120 trap_Cvar_SetValue( "r_colorbits", 0 ); 3121 trap_Cvar_SetValue( "r_depthbits", 0 ); 3122 trap_Cvar_SetValue( "r_picmip", 1 ); 3123 trap_Cvar_SetValue( "r_mode", 3 ); 3124 trap_Cvar_SetValue( "r_texturebits", 0 ); 3125 trap_Cvar_SetValue( "cg_shadows", 0 ); 3126 trap_Cvar_SetValue( "r_fastSky", 1 ); 3127 trap_Cvar_SetValue( "r_inGameVideo", 0 ); 3128 trap_Cvar_SetValue( "cg_brassTime", 0 ); 3129 trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); 3130 break; 3131 case 3: // fastest 3132 trap_Cvar_SetValue( "r_fullScreen", 1 ); 3133 trap_Cvar_SetValue( "r_subdivisions", 20 ); 3134 trap_Cvar_SetValue( "r_vertexlight", 1 ); 3135 trap_Cvar_SetValue( "r_lodbias", 2 ); 3136 trap_Cvar_SetValue( "r_colorbits", 16 ); 3137 trap_Cvar_SetValue( "r_depthbits", 16 ); 3138 trap_Cvar_SetValue( "r_mode", 3 ); 3139 trap_Cvar_SetValue( "r_picmip", 2 ); 3140 trap_Cvar_SetValue( "r_texturebits", 16 ); 3141 trap_Cvar_SetValue( "cg_shadows", 0 ); 3142 trap_Cvar_SetValue( "cg_brassTime", 0 ); 3143 trap_Cvar_SetValue( "r_fastSky", 1 ); 3144 trap_Cvar_SetValue( "r_inGameVideo", 0 ); 3145 trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" ); 3146 break; 3147 } 3148 } else if (Q_stricmp(name, "ui_mousePitch") == 0) { 3149 if (val == 0) { 3150 trap_Cvar_SetValue( "m_pitch", 0.022f ); 3151 } else { 3152 trap_Cvar_SetValue( "m_pitch", -0.022f ); 3153 } 3154 } 3155 } 3156 3157 static void UI_RunMenuScript(char **args) { 3158 const char *name, *name2; 3159 char buff[1024]; 3160 3161 if (String_Parse(args, &name)) { 3162 if (Q_stricmp(name, "StartServer") == 0) { 3163 int i, clients, oldclients; 3164 float skill; 3165 trap_Cvar_Set("cg_thirdPerson", "0"); 3166 trap_Cvar_Set("cg_cameraOrbit", "0"); 3167 trap_Cvar_Set("ui_singlePlayerActive", "0"); 3168 trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, ui_dedicated.integer ) ); 3169 trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 8, uiInfo.gameTypes[ui_netGameType.integer].gtEnum ) ); 3170 trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName")); 3171 trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName")); 3172 trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName ) ); 3173 skill = trap_Cvar_VariableValue( "g_spSkill" ); 3174 // set max clients based on spots 3175 oldclients = trap_Cvar_VariableValue( "sv_maxClients" ); 3176 clients = 0; 3177 for (i = 0; i < PLAYERS_PER_TEAM; i++) { 3178 int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1)); 3179 if (bot >= 0) { 3180 clients++; 3181 } 3182 bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1)); 3183 if (bot >= 0) { 3184 clients++; 3185 } 3186 } 3187 if (clients == 0) { 3188 clients = 8; 3189 } 3190 3191 if (oldclients > clients) { 3192 clients = oldclients; 3193 } 3194 3195 trap_Cvar_Set("sv_maxClients", va("%d",clients)); 3196 3197 for (i = 0; i < PLAYERS_PER_TEAM; i++) { 3198 int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1)); 3199 if (bot > 1) { 3200 if (ui_actualNetGameType.integer >= GT_TEAM) { 3201 Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Blue"); 3202 } else { 3203 Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill); 3204 } 3205 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 3206 } 3207 bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1)); 3208 if (bot > 1) { 3209 if (ui_actualNetGameType.integer >= GT_TEAM) { 3210 Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Red"); 3211 } else { 3212 Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill); 3213 } 3214 trap_Cmd_ExecuteText( EXEC_APPEND, buff ); 3215 } 3216 } 3217 } else if (Q_stricmp(name, "updateSPMenu") == 0) { 3218 UI_SetCapFragLimits(qtrue); 3219 UI_MapCountByGameType(qtrue); 3220 ui_mapIndex.integer = UI_GetIndexFromSelection(ui_currentMap.integer); 3221 trap_Cvar_Set("ui_mapIndex", va("%d", ui_mapIndex.integer)); 3222 Menu_SetFeederSelection(NULL, FEEDER_MAPS, ui_mapIndex.integer, "skirmish"); 3223 UI_GameType_HandleKey(0, 0, K_MOUSE1, qfalse); 3224 UI_GameType_HandleKey(0, 0, K_MOUSE2, qfalse); 3225 } else if (Q_stricmp(name, "resetDefaults") == 0) { 3226 trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n"); 3227 trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n"); 3228 Controls_SetDefaults(); 3229 trap_Cvar_Set("com_introPlayed", "1" ); 3230 trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" ); 3231 } else if (Q_stricmp(name, "getCDKey") == 0) { 3232 char out[17]; 3233 trap_GetCDKey(buff, 17); 3234 trap_Cvar_Set("cdkey1", ""); 3235 trap_Cvar_Set("cdkey2", ""); 3236 trap_Cvar_Set("cdkey3", ""); 3237 trap_Cvar_Set("cdkey4", ""); 3238 if (strlen(buff) == CDKEY_LEN) { 3239 Q_strncpyz(out, buff, 5); 3240 trap_Cvar_Set("cdkey1", out); 3241 Q_strncpyz(out, buff + 4, 5); 3242 trap_Cvar_Set("cdkey2", out); 3243 Q_strncpyz(out, buff + 8, 5); 3244 trap_Cvar_Set("cdkey3", out); 3245 Q_strncpyz(out, buff + 12, 5); 3246 trap_Cvar_Set("cdkey4", out); 3247 } 3248 3249 } else if (Q_stricmp(name, "verifyCDKey") == 0) { 3250 buff[0] = '\0'; 3251 Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey1")); 3252 Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey2")); 3253 Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey3")); 3254 Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey4")); 3255 trap_Cvar_Set("cdkey", buff); 3256 if (trap_VerifyCDKey(buff, UI_Cvar_VariableString("cdkeychecksum"))) { 3257 trap_Cvar_Set("ui_cdkeyvalid", "CD Key Appears to be valid."); 3258 trap_SetCDKey(buff); 3259 } else { 3260 trap_Cvar_Set("ui_cdkeyvalid", "CD Key does not appear to be valid."); 3261 } 3262 } else if (Q_stricmp(name, "loadArenas") == 0) { 3263 UI_LoadArenas(); 3264 UI_MapCountByGameType(qfalse); 3265 Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver"); 3266 } else if (Q_stricmp(name, "saveControls") == 0) { 3267 Controls_SetConfig(qtrue); 3268 } else if (Q_stricmp(name, "loadControls") == 0) { 3269 Controls_GetConfig(); 3270 } else if (Q_stricmp(name, "clearError") == 0) { 3271 trap_Cvar_Set("com_errorMessage", ""); 3272 } else if (Q_stricmp(name, "loadGameInfo") == 0) { 3273 #ifdef PRE_RELEASE_TADEMO 3274 UI_ParseGameInfo("demogameinfo.txt"); 3275 #else 3276 UI_ParseGameInfo("gameinfo.txt"); 3277 #endif 3278 UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); 3279 } else if (Q_stricmp(name, "resetScores") == 0) { 3280 UI_ClearScores(); 3281 } else if (Q_stricmp(name, "RefreshServers") == 0) { 3282 UI_StartServerRefresh(qtrue); 3283 UI_BuildServerDisplayList(qtrue); 3284 } else if (Q_stricmp(name, "RefreshFilter") == 0) { 3285 UI_StartServerRefresh(qfalse); 3286 UI_BuildServerDisplayList(qtrue); 3287 } else if (Q_stricmp(name, "RunSPDemo") == 0) { 3288 if (uiInfo.demoAvailable) { 3289 trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s_%i\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum)); 3290 } 3291 } else if (Q_stricmp(name, "LoadDemos") == 0) { 3292 UI_LoadDemos(); 3293 } else if (Q_stricmp(name, "LoadMovies") == 0) { 3294 UI_LoadMovies(); 3295 } else if (Q_stricmp(name, "LoadMods") == 0) { 3296 UI_LoadMods(); 3297 } else if (Q_stricmp(name, "playMovie") == 0) { 3298 if (uiInfo.previewMovie >= 0) { 3299 trap_CIN_StopCinematic(uiInfo.previewMovie); 3300 } 3301 trap_Cmd_ExecuteText( EXEC_APPEND, va("cinematic %s.roq 2\n", uiInfo.movieList[uiInfo.movieIndex])); 3302 } else if (Q_stricmp(name, "RunMod") == 0) { 3303 trap_Cvar_Set( "fs_game", uiInfo.modList[uiInfo.modIndex].modName); 3304 trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); 3305 } else if (Q_stricmp(name, "RunDemo") == 0) { 3306 trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s\n", uiInfo.demoList[uiInfo.demoIndex])); 3307 } else if (Q_stricmp(name, "Quake3") == 0) { 3308 trap_Cvar_Set( "fs_game", ""); 3309 trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" ); 3310 } else if (Q_stricmp(name, "closeJoin") == 0) { 3311 if (uiInfo.serverStatus.refreshActive) { 3312 UI_StopServerRefresh(); 3313 uiInfo.serverStatus.nextDisplayRefresh = 0; 3314 uiInfo.nextServerStatusRefresh = 0; 3315 uiInfo.nextFindPlayerRefresh = 0; 3316 UI_BuildServerDisplayList(qtrue); 3317 } else { 3318 Menus_CloseByName("joinserver"); 3319 Menus_OpenByName("main"); 3320 } 3321 } else if (Q_stricmp(name, "StopRefresh") == 0) { 3322 UI_StopServerRefresh(); 3323 uiInfo.serverStatus.nextDisplayRefresh = 0; 3324 uiInfo.nextServerStatusRefresh = 0; 3325 uiInfo.nextFindPlayerRefresh = 0; 3326 } else if (Q_stricmp(name, "UpdateFilter") == 0) { 3327 if (ui_netSource.integer == AS_LOCAL) { 3328 UI_StartServerRefresh(qtrue); 3329 } 3330 UI_BuildServerDisplayList(qtrue); 3331 UI_FeederSelection(FEEDER_SERVERS, 0); 3332 } else if (Q_stricmp(name, "ServerStatus") == 0) { 3333 trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], uiInfo.serverStatusAddress, sizeof(uiInfo.serverStatusAddress)); 3334 UI_BuildServerStatus(qtrue); 3335 } else if (Q_stricmp(name, "FoundPlayerServerStatus") == 0) { 3336 Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress)); 3337 UI_BuildServerStatus(qtrue); 3338 Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL); 3339 } else if (Q_stricmp(name, "FindPlayer") == 0) { 3340 UI_BuildFindPlayerList(qtrue); 3341 // clear the displayed server status info 3342 uiInfo.serverStatusInfo.numLines = 0; 3343 Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL); 3344 } else if (Q_stricmp(name, "JoinServer") == 0) { 3345 trap_Cvar_Set("cg_thirdPerson", "0"); 3346 trap_Cvar_Set("cg_cameraOrbit", "0"); 3347 trap_Cvar_Set("ui_singlePlayerActive", "0"); 3348 if (uiInfo.serverStatus.currentServer >= 0 && uiInfo.serverStatus.currentServer < uiInfo.serverStatus.numDisplayServers) { 3349 trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, 1024); 3350 trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) ); 3351 } 3352 } else if (Q_stricmp(name, "FoundPlayerJoinServer") == 0) { 3353 trap_Cvar_Set("ui_singlePlayerActive", "0"); 3354 if (uiInfo.currentFoundPlayerServer >= 0 && uiInfo.currentFoundPlayerServer < uiInfo.numFoundPlayerServers) { 3355 trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer] ) ); 3356 } 3357 } else if (Q_stricmp(name, "Quit") == 0) { 3358 trap_Cvar_Set("ui_singlePlayerActive", "0"); 3359 trap_Cmd_ExecuteText( EXEC_NOW, "quit"); 3360 } else if (Q_stricmp(name, "Controls") == 0) { 3361 trap_Cvar_Set( "cl_paused", "1" ); 3362 trap_Key_SetCatcher( KEYCATCH_UI ); 3363 Menus_CloseAll(); 3364 Menus_ActivateByName("setup_menu2"); 3365 } else if (Q_stricmp(name, "Leave") == 0) { 3366 trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" ); 3367 trap_Key_SetCatcher( KEYCATCH_UI ); 3368 Menus_CloseAll(); 3369 Menus_ActivateByName("main"); 3370 } else if (Q_stricmp(name, "ServerSort") == 0) { 3371 int sortColumn; 3372 if (Int_Parse(args, &sortColumn)) { 3373 // if same column we're already sorting on then flip the direction 3374 if (sortColumn == uiInfo.serverStatus.sortKey) { 3375 uiInfo.serverStatus.sortDir = !uiInfo.serverStatus.sortDir; 3376 } 3377 // make sure we sort again 3378 UI_ServersSort(sortColumn, qtrue); 3379 } 3380 } else if (Q_stricmp(name, "nextSkirmish") == 0) { 3381 UI_StartSkirmish(qtrue); 3382 } else if (Q_stricmp(name, "SkirmishStart") == 0) { 3383 UI_StartSkirmish(qfalse); 3384 } else if (Q_stricmp(name, "closeingame") == 0) { 3385 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 3386 trap_Key_ClearStates(); 3387 trap_Cvar_Set( "cl_paused", "0" ); 3388 Menus_CloseAll(); 3389 } else if (Q_stricmp(name, "voteMap") == 0) { 3390 if (ui_currentNetMap.integer >=0 && ui_currentNetMap.integer < uiInfo.mapCount) { 3391 trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote map %s\n",uiInfo.mapList[ui_currentNetMap.integer].mapLoadName) ); 3392 } 3393 } else if (Q_stricmp(name, "voteKick") == 0) { 3394 if (uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount) { 3395 trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote kick %s\n",uiInfo.playerNames[uiInfo.playerIndex]) ); 3396 } 3397 } else if (Q_stricmp(name, "voteGame") == 0) { 3398 if (ui_netGameType.integer >= 0 && ui_netGameType.integer < uiInfo.numGameTypes) { 3399 trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote g_gametype %i\n",uiInfo.gameTypes[ui_netGameType.integer].gtEnum) ); 3400 } 3401 } else if (Q_stricmp(name, "voteLeader") == 0) { 3402 if (uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount) { 3403 trap_Cmd_ExecuteText( EXEC_APPEND, va("callteamvote leader %s\n",uiInfo.teamNames[uiInfo.teamIndex]) ); 3404 } 3405 } else if (Q_stricmp(name, "addBot") == 0) { 3406 if (trap_Cvar_VariableValue("g_gametype") >= GT_TEAM) { 3407 trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", uiInfo.characterList[uiInfo.botIndex].name, uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); 3408 } else { 3409 trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", UI_GetBotNameByNumber(uiInfo.botIndex), uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); 3410 } 3411 } else if (Q_stricmp(name, "addFavorite") == 0) { 3412 if (ui_netSource.integer != AS_FAVORITES) { 3413 char name[MAX_NAME_LENGTH]; 3414 char addr[MAX_NAME_LENGTH]; 3415 int res; 3416 3417 trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS); 3418 name[0] = addr[0] = '\0'; 3419 Q_strncpyz(name, Info_ValueForKey(buff, "hostname"), MAX_NAME_LENGTH); 3420 Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH); 3421 if (strlen(name) > 0 && strlen(addr) > 0) { 3422 res = trap_LAN_AddServer(AS_FAVORITES, name, addr); 3423 if (res == 0) { 3424 // server already in the list 3425 Com_Printf("Favorite already in list\n"); 3426 } 3427 else if (res == -1) { 3428 // list full 3429 Com_Printf("Favorite list full\n"); 3430 } 3431 else { 3432 // successfully added 3433 Com_Printf("Added favorite server %s\n", addr); 3434 } 3435 } 3436 } 3437 } else if (Q_stricmp(name, "deleteFavorite") == 0) { 3438 if (ui_netSource.integer == AS_FAVORITES) { 3439 char addr[MAX_NAME_LENGTH]; 3440 trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS); 3441 addr[0] = '\0'; 3442 Q_strncpyz(addr, Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH); 3443 if (strlen(addr) > 0) { 3444 trap_LAN_RemoveServer(AS_FAVORITES, addr); 3445 } 3446 } 3447 } else if (Q_stricmp(name, "createFavorite") == 0) { 3448 if (ui_netSource.integer == AS_FAVORITES) { 3449 char name[MAX_NAME_LENGTH]; 3450 char addr[MAX_NAME_LENGTH]; 3451 int res; 3452 3453 name[0] = addr[0] = '\0'; 3454 Q_strncpyz(name, UI_Cvar_VariableString("ui_favoriteName"), MAX_NAME_LENGTH); 3455 Q_strncpyz(addr, UI_Cvar_VariableString("ui_favoriteAddress"), MAX_NAME_LENGTH); 3456 if (strlen(name) > 0 && strlen(addr) > 0) { 3457 res = trap_LAN_AddServer(AS_FAVORITES, name, addr); 3458 if (res == 0) { 3459 // server already in the list 3460 Com_Printf("Favorite already in list\n"); 3461 } 3462 else if (res == -1) { 3463 // list full 3464 Com_Printf("Favorite list full\n"); 3465 } 3466 else { 3467 // successfully added 3468 Com_Printf("Added favorite server %s\n", addr); 3469 } 3470 } 3471 } 3472 } else if (Q_stricmp(name, "orders") == 0) { 3473 const char *orders; 3474 if (String_Parse(args, &orders)) { 3475 int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); 3476 if (selectedPlayer < uiInfo.myTeamCount) { 3477 strcpy(buff, orders); 3478 trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) ); 3479 trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); 3480 } else { 3481 int i; 3482 for (i = 0; i < uiInfo.myTeamCount; i++) { 3483 if (Q_stricmp(UI_Cvar_VariableString("name"), uiInfo.teamNames[i]) == 0) { 3484 continue; 3485 } 3486 strcpy(buff, orders); 3487 trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamNames[i]) ); 3488 trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); 3489 } 3490 } 3491 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 3492 trap_Key_ClearStates(); 3493 trap_Cvar_Set( "cl_paused", "0" ); 3494 Menus_CloseAll(); 3495 } 3496 } else if (Q_stricmp(name, "voiceOrdersTeam") == 0) { 3497 const char *orders; 3498 if (String_Parse(args, &orders)) { 3499 int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); 3500 if (selectedPlayer == uiInfo.myTeamCount) { 3501 trap_Cmd_ExecuteText( EXEC_APPEND, orders ); 3502 trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); 3503 } 3504 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 3505 trap_Key_ClearStates(); 3506 trap_Cvar_Set( "cl_paused", "0" ); 3507 Menus_CloseAll(); 3508 } 3509 } else if (Q_stricmp(name, "voiceOrders") == 0) { 3510 const char *orders; 3511 if (String_Parse(args, &orders)) { 3512 int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer"); 3513 if (selectedPlayer < uiInfo.myTeamCount) { 3514 strcpy(buff, orders); 3515 trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) ); 3516 trap_Cmd_ExecuteText( EXEC_APPEND, "\n" ); 3517 } 3518 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 3519 trap_Key_ClearStates(); 3520 trap_Cvar_Set( "cl_paused", "0" ); 3521 Menus_CloseAll(); 3522 } 3523 } else if (Q_stricmp(name, "glCustom") == 0) { 3524 trap_Cvar_Set("ui_glCustom", "4"); 3525 } else if (Q_stricmp(name, "update") == 0) { 3526 if (String_Parse(args, &name2)) { 3527 UI_Update(name2); 3528 } 3529 } else if (Q_stricmp(name, "setPbClStatus") == 0) { 3530 int stat; 3531 if ( Int_Parse( args, &stat ) ) 3532 trap_SetPbClStatus( stat ); 3533 } 3534 else { 3535 Com_Printf("unknown UI script %s\n", name); 3536 } 3537 } 3538 } 3539 3540 static void UI_GetTeamColor(vec4_t *color) { 3541 } 3542 3543 /* 3544 ================== 3545 UI_MapCountByGameType 3546 ================== 3547 */ 3548 static int UI_MapCountByGameType(qboolean singlePlayer) { 3549 int i, c, game; 3550 c = 0; 3551 game = singlePlayer ? uiInfo.gameTypes[ui_gameType.integer].gtEnum : uiInfo.gameTypes[ui_netGameType.integer].gtEnum; 3552 if (game == GT_SINGLE_PLAYER) { 3553 game++; 3554 } 3555 if (game == GT_TEAM) { 3556 game = GT_FFA; 3557 } 3558 3559 for (i = 0; i < uiInfo.mapCount; i++) { 3560 uiInfo.mapList[i].active = qfalse; 3561 if ( uiInfo.mapList[i].typeBits & (1 << game)) { 3562 if (singlePlayer) { 3563 if (!(uiInfo.mapList[i].typeBits & (1 << GT_SINGLE_PLAYER))) { 3564 continue; 3565 } 3566 } 3567 c++; 3568 uiInfo.mapList[i].active = qtrue; 3569 } 3570 } 3571 return c; 3572 } 3573 3574 qboolean UI_hasSkinForBase(const char *base, const char *team) { 3575 char test[1024]; 3576 3577 Com_sprintf( test, sizeof( test ), "models/players/%s/%s/lower_default.skin", base, team ); 3578 3579 if (trap_FS_FOpenFile(test, 0, FS_READ)) { 3580 return qtrue; 3581 } 3582 Com_sprintf( test, sizeof( test ), "models/players/characters/%s/%s/lower_default.skin", base, team ); 3583 3584 if (trap_FS_FOpenFile(test, 0, FS_READ)) { 3585 return qtrue; 3586 } 3587 return qfalse; 3588 } 3589 3590 /* 3591 ================== 3592 UI_MapCountByTeam 3593 ================== 3594 */ 3595 static int UI_HeadCountByTeam() { 3596 static int init = 0; 3597 int i, j, k, c, tIndex; 3598 3599 c = 0; 3600 if (!init) { 3601 for (i = 0; i < uiInfo.characterCount; i++) { 3602 uiInfo.characterList[i].reference = 0; 3603 for (j = 0; j < uiInfo.teamCount; j++) { 3604 if (UI_hasSkinForBase(uiInfo.characterList[i].base, uiInfo.teamList[j].teamName)) { 3605 uiInfo.characterList[i].reference |= (1<<j); 3606 } 3607 } 3608 } 3609 init = 1; 3610 } 3611 3612 tIndex = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 3613 3614 // do names 3615 for (i = 0; i < uiInfo.characterCount; i++) { 3616 uiInfo.characterList[i].active = qfalse; 3617 for(j = 0; j < TEAM_MEMBERS; j++) { 3618 if (uiInfo.teamList[tIndex].teamMembers[j] != NULL) { 3619 if (uiInfo.characterList[i].reference&(1<<tIndex)) {// && Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.characterList[i].name)==0) { 3620 uiInfo.characterList[i].active = qtrue; 3621 c++; 3622 break; 3623 } 3624 } 3625 } 3626 } 3627 3628 // and then aliases 3629 for(j = 0; j < TEAM_MEMBERS; j++) { 3630 for(k = 0; k < uiInfo.aliasCount; k++) { 3631 if (uiInfo.aliasList[k].name != NULL) { 3632 if (Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.aliasList[k].name)==0) { 3633 for (i = 0; i < uiInfo.characterCount; i++) { 3634 if (uiInfo.characterList[i].headImage != -1 && uiInfo.characterList[i].reference&(1<<tIndex) && Q_stricmp(uiInfo.aliasList[k].ai, uiInfo.characterList[i].name)==0) { 3635 if (uiInfo.characterList[i].active == qfalse) { 3636 uiInfo.characterList[i].active = qtrue; 3637 c++; 3638 } 3639 break; 3640 } 3641 } 3642 } 3643 } 3644 } 3645 } 3646 return c; 3647 } 3648 3649 /* 3650 ================== 3651 UI_InsertServerIntoDisplayList 3652 ================== 3653 */ 3654 static void UI_InsertServerIntoDisplayList(int num, int position) { 3655 int i; 3656 3657 if (position < 0 || position > uiInfo.serverStatus.numDisplayServers ) { 3658 return; 3659 } 3660 // 3661 uiInfo.serverStatus.numDisplayServers++; 3662 for (i = uiInfo.serverStatus.numDisplayServers; i > position; i--) { 3663 uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i-1]; 3664 } 3665 uiInfo.serverStatus.displayServers[position] = num; 3666 } 3667 3668 /* 3669 ================== 3670 UI_RemoveServerFromDisplayList 3671 ================== 3672 */ 3673 static void UI_RemoveServerFromDisplayList(int num) { 3674 int i, j; 3675 3676 for (i = 0; i < uiInfo.serverStatus.numDisplayServers; i++) { 3677 if (uiInfo.serverStatus.displayServers[i] == num) { 3678 uiInfo.serverStatus.numDisplayServers--; 3679 for (j = i; j < uiInfo.serverStatus.numDisplayServers; j++) { 3680 uiInfo.serverStatus.displayServers[j] = uiInfo.serverStatus.displayServers[j+1]; 3681 } 3682 return; 3683 } 3684 } 3685 } 3686 3687 /* 3688 ================== 3689 UI_BinaryServerInsertion 3690 ================== 3691 */ 3692 static void UI_BinaryServerInsertion(int num) { 3693 int mid, offset, res, len; 3694 3695 // use binary search to insert server 3696 len = uiInfo.serverStatus.numDisplayServers; 3697 mid = len; 3698 offset = 0; 3699 res = 0; 3700 while(mid > 0) { 3701 mid = len >> 1; 3702 // 3703 res = trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey, 3704 uiInfo.serverStatus.sortDir, num, uiInfo.serverStatus.displayServers[offset+mid]); 3705 // if equal 3706 if (res == 0) { 3707 UI_InsertServerIntoDisplayList(num, offset+mid); 3708 return; 3709 } 3710 // if larger 3711 else if (res == 1) { 3712 offset += mid; 3713 len -= mid; 3714 } 3715 // if smaller 3716 else { 3717 len -= mid; 3718 } 3719 } 3720 if (res == 1) { 3721 offset++; 3722 } 3723 UI_InsertServerIntoDisplayList(num, offset); 3724 } 3725 3726 /* 3727 ================== 3728 UI_BuildServerDisplayList 3729 ================== 3730 */ 3731 static void UI_BuildServerDisplayList(qboolean force) { 3732 int i, count, clients, maxClients, ping, game, len, visible; 3733 char info[MAX_STRING_CHARS]; 3734 // qboolean startRefresh = qtrue; TTimo: unused 3735 static int numinvisible; 3736 3737 if (!(force || uiInfo.uiDC.realTime > uiInfo.serverStatus.nextDisplayRefresh)) { 3738 return; 3739 } 3740 // if we shouldn't reset 3741 if ( force == 2 ) { 3742 force = 0; 3743 } 3744 3745 // do motd updates here too 3746 trap_Cvar_VariableStringBuffer( "cl_motdString", uiInfo.serverStatus.motd, sizeof(uiInfo.serverStatus.motd) ); 3747 len = strlen(uiInfo.serverStatus.motd); 3748 if (len == 0) { 3749 strcpy(uiInfo.serverStatus.motd, "Welcome to Team Arena!"); 3750 len = strlen(uiInfo.serverStatus.motd); 3751 } 3752 if (len != uiInfo.serverStatus.motdLen) { 3753 uiInfo.serverStatus.motdLen = len; 3754 uiInfo.serverStatus.motdWidth = -1; 3755 } 3756 3757 if (force) { 3758 numinvisible = 0; 3759 // clear number of displayed servers 3760 uiInfo.serverStatus.numDisplayServers = 0; 3761 uiInfo.serverStatus.numPlayersOnServers = 0; 3762 // set list box index to zero 3763 Menu_SetFeederSelection(NULL, FEEDER_SERVERS, 0, NULL); 3764 // mark all servers as visible so we store ping updates for them 3765 trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue); 3766 } 3767 3768 // get the server count (comes from the master) 3769 count = trap_LAN_GetServerCount(ui_netSource.integer); 3770 if (count == -1 || (ui_netSource.integer == AS_LOCAL && count == 0) ) { 3771 // still waiting on a response from the master 3772 uiInfo.serverStatus.numDisplayServers = 0; 3773 uiInfo.serverStatus.numPlayersOnServers = 0; 3774 uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 500; 3775 return; 3776 } 3777 3778 visible = qfalse; 3779 for (i = 0; i < count; i++) { 3780 // if we already got info for this server 3781 if (!trap_LAN_ServerIsVisible(ui_netSource.integer, i)) { 3782 continue; 3783 } 3784 visible = qtrue; 3785 // get the ping for this server 3786 ping = trap_LAN_GetServerPing(ui_netSource.integer, i); 3787 if (ping > 0 || ui_netSource.integer == AS_FAVORITES) { 3788 3789 trap_LAN_GetServerInfo(ui_netSource.integer, i, info, MAX_STRING_CHARS); 3790 3791 clients = atoi(Info_ValueForKey(info, "clients")); 3792 uiInfo.serverStatus.numPlayersOnServers += clients; 3793 3794 if (ui_browserShowEmpty.integer == 0) { 3795 if (clients == 0) { 3796 trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); 3797 continue; 3798 } 3799 } 3800 3801 if (ui_browserShowFull.integer == 0) { 3802 maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); 3803 if (clients == maxClients) { 3804 trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); 3805 continue; 3806 } 3807 } 3808 3809 if (uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum != -1) { 3810 game = atoi(Info_ValueForKey(info, "gametype")); 3811 if (game != uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum) { 3812 trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); 3813 continue; 3814 } 3815 } 3816 3817 if (ui_serverFilterType.integer > 0) { 3818 if (Q_stricmp(Info_ValueForKey(info, "game"), serverFilters[ui_serverFilterType.integer].basedir) != 0) { 3819 trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); 3820 continue; 3821 } 3822 } 3823 // make sure we never add a favorite server twice 3824 if (ui_netSource.integer == AS_FAVORITES) { 3825 UI_RemoveServerFromDisplayList(i); 3826 } 3827 // insert the server into the list 3828 UI_BinaryServerInsertion(i); 3829 // done with this server 3830 if (ping > 0) { 3831 trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse); 3832 numinvisible++; 3833 } 3834 } 3835 } 3836 3837 uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime; 3838 3839 // if there were no servers visible for ping updates 3840 if (!visible) { 3841 // UI_StopServerRefresh(); 3842 // uiInfo.serverStatus.nextDisplayRefresh = 0; 3843 } 3844 } 3845 3846 typedef struct 3847 { 3848 char *name, *altName; 3849 } serverStatusCvar_t; 3850 3851 serverStatusCvar_t serverStatusCvars[] = { 3852 {"sv_hostname", "Name"}, 3853 {"Address", ""}, 3854 {"gamename", "Game name"}, 3855 {"g_gametype", "Game type"}, 3856 {"mapname", "Map"}, 3857 {"version", ""}, 3858 {"protocol", ""}, 3859 {"timelimit", ""}, 3860 {"fraglimit", ""}, 3861 {NULL, NULL} 3862 }; 3863 3864 /* 3865 ================== 3866 UI_SortServerStatusInfo 3867 ================== 3868 */ 3869 static void UI_SortServerStatusInfo( serverStatusInfo_t *info ) { 3870 int i, j, index; 3871 char *tmp1, *tmp2; 3872 3873 // FIXME: if "gamename" == "baseq3" or "missionpack" then 3874 // replace the gametype number by FFA, CTF etc. 3875 // 3876 index = 0; 3877 for (i = 0; serverStatusCvars[i].name; i++) { 3878 for (j = 0; j < info->numLines; j++) { 3879 if ( !info->lines[j][1] || info->lines[j][1][0] ) { 3880 continue; 3881 } 3882 if ( !Q_stricmp(serverStatusCvars[i].name, info->lines[j][0]) ) { 3883 // swap lines 3884 tmp1 = info->lines[index][0]; 3885 tmp2 = info->lines[index][3]; 3886 info->lines[index][0] = info->lines[j][0]; 3887 info->lines[index][3] = info->lines[j][3]; 3888 info->lines[j][0] = tmp1; 3889 info->lines[j][3] = tmp2; 3890 // 3891 if ( strlen(serverStatusCvars[i].altName) ) { 3892 info->lines[index][0] = serverStatusCvars[i].altName; 3893 } 3894 index++; 3895 } 3896 } 3897 } 3898 } 3899 3900 /* 3901 ================== 3902 UI_GetServerStatusInfo 3903 ================== 3904 */ 3905 static int UI_GetServerStatusInfo( const char *serverAddress, serverStatusInfo_t *info ) { 3906 char *p, *score, *ping, *name; 3907 int i, len; 3908 3909 if (!info) { 3910 trap_LAN_ServerStatus( serverAddress, NULL, 0); 3911 return qfalse; 3912 } 3913 memset(info, 0, sizeof(*info)); 3914 if ( trap_LAN_ServerStatus( serverAddress, info->text, sizeof(info->text)) ) { 3915 Q_strncpyz(info->address, serverAddress, sizeof(info->address)); 3916 p = info->text; 3917 info->numLines = 0; 3918 info->lines[info->numLines][0] = "Address"; 3919 info->lines[info->numLines][1] = ""; 3920 info->lines[info->numLines][2] = ""; 3921 info->lines[info->numLines][3] = info->address; 3922 info->numLines++; 3923 // get the cvars 3924 while (p && *p) { 3925 p = strchr(p, '\\'); 3926 if (!p) break; 3927 *p++ = '\0'; 3928 if (*p == '\\') 3929 break; 3930 info->lines[info->numLines][0] = p; 3931 info->lines[info->numLines][1] = ""; 3932 info->lines[info->numLines][2] = ""; 3933 p = strchr(p, '\\'); 3934 if (!p) break; 3935 *p++ = '\0'; 3936 info->lines[info->numLines][3] = p; 3937 3938 info->numLines++; 3939 if (info->numLines >= MAX_SERVERSTATUS_LINES) 3940 break; 3941 } 3942 // get the player list 3943 if (info->numLines < MAX_SERVERSTATUS_LINES-3) { 3944 // empty line 3945 info->lines[info->numLines][0] = ""; 3946 info->lines[info->numLines][1] = ""; 3947 info->lines[info->numLines][2] = ""; 3948 info->lines[info->numLines][3] = ""; 3949 info->numLines++; 3950 // header 3951 info->lines[info->numLines][0] = "num"; 3952 info->lines[info->numLines][1] = "score"; 3953 info->lines[info->numLines][2] = "ping"; 3954 info->lines[info->numLines][3] = "name"; 3955 info->numLines++; 3956 // parse players 3957 i = 0; 3958 len = 0; 3959 while (p && *p) { 3960 if (*p == '\\') 3961 *p++ = '\0'; 3962 if (!p) 3963 break; 3964 score = p; 3965 p = strchr(p, ' '); 3966 if (!p) 3967 break; 3968 *p++ = '\0'; 3969 ping = p; 3970 p = strchr(p, ' '); 3971 if (!p) 3972 break; 3973 *p++ = '\0'; 3974 name = p; 3975 Com_sprintf(&info->pings[len], sizeof(info->pings)-len, "%d", i); 3976 info->lines[info->numLines][0] = &info->pings[len]; 3977 len += strlen(&info->pings[len]) + 1; 3978 info->lines[info->numLines][1] = score; 3979 info->lines[info->numLines][2] = ping; 3980 info->lines[info->numLines][3] = name; 3981 info->numLines++; 3982 if (info->numLines >= MAX_SERVERSTATUS_LINES) 3983 break; 3984 p = strchr(p, '\\'); 3985 if (!p) 3986 break; 3987 *p++ = '\0'; 3988 // 3989 i++; 3990 } 3991 } 3992 UI_SortServerStatusInfo( info ); 3993 return qtrue; 3994 } 3995 return qfalse; 3996 } 3997 3998 /* 3999 ================== 4000 stristr 4001 ================== 4002 */ 4003 static char *stristr(char *str, char *charset) { 4004 int i; 4005 4006 while(*str) { 4007 for (i = 0; charset[i] && str[i]; i++) { 4008 if (toupper(charset[i]) != toupper(str[i])) break; 4009 } 4010 if (!charset[i]) return str; 4011 str++; 4012 } 4013 return NULL; 4014 } 4015 4016 /* 4017 ================== 4018 UI_BuildFindPlayerList 4019 ================== 4020 */ 4021 static void UI_BuildFindPlayerList(qboolean force) { 4022 static int numFound, numTimeOuts; 4023 int i, j, resend; 4024 serverStatusInfo_t info; 4025 char name[MAX_NAME_LENGTH+2]; 4026 char infoString[MAX_STRING_CHARS]; 4027 4028 if (!force) { 4029 if (!uiInfo.nextFindPlayerRefresh || uiInfo.nextFindPlayerRefresh > uiInfo.uiDC.realTime) { 4030 return; 4031 } 4032 } 4033 else { 4034 memset(&uiInfo.pendingServerStatus, 0, sizeof(uiInfo.pendingServerStatus)); 4035 uiInfo.numFoundPlayerServers = 0; 4036 uiInfo.currentFoundPlayerServer = 0; 4037 trap_Cvar_VariableStringBuffer( "ui_findPlayer", uiInfo.findPlayerName, sizeof(uiInfo.findPlayerName)); 4038 Q_CleanStr(uiInfo.findPlayerName); 4039 // should have a string of some length 4040 if (!strlen(uiInfo.findPlayerName)) { 4041 uiInfo.nextFindPlayerRefresh = 0; 4042 return; 4043 } 4044 // set resend time 4045 resend = ui_serverStatusTimeOut.integer / 2 - 10; 4046 if (resend < 50) { 4047 resend = 50; 4048 } 4049 trap_Cvar_Set("cl_serverStatusResendTime", va("%d", resend)); 4050 // reset all server status requests 4051 trap_LAN_ServerStatus( NULL, NULL, 0); 4052 // 4053 uiInfo.numFoundPlayerServers = 1; 4054 Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], 4055 sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), 4056 "searching %d...", uiInfo.pendingServerStatus.num); 4057 numFound = 0; 4058 numTimeOuts++; 4059 } 4060 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 4061 // if this pending server is valid 4062 if (uiInfo.pendingServerStatus.server[i].valid) { 4063 // try to get the server status for this server 4064 if (UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, &info ) ) { 4065 // 4066 numFound++; 4067 // parse through the server status lines 4068 for (j = 0; j < info.numLines; j++) { 4069 // should have ping info 4070 if ( !info.lines[j][2] || !info.lines[j][2][0] ) { 4071 continue; 4072 } 4073 // clean string first 4074 Q_strncpyz(name, info.lines[j][3], sizeof(name)); 4075 Q_CleanStr(name); 4076 // if the player name is a substring 4077 if (stristr(name, uiInfo.findPlayerName)) { 4078 // add to found server list if we have space (always leave space for a line with the number found) 4079 if (uiInfo.numFoundPlayerServers < MAX_FOUNDPLAYER_SERVERS-1) { 4080 // 4081 Q_strncpyz(uiInfo.foundPlayerServerAddresses[uiInfo.numFoundPlayerServers-1], 4082 uiInfo.pendingServerStatus.server[i].adrstr, 4083 sizeof(uiInfo.foundPlayerServerAddresses[0])); 4084 Q_strncpyz(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], 4085 uiInfo.pendingServerStatus.server[i].name, 4086 sizeof(uiInfo.foundPlayerServerNames[0])); 4087 uiInfo.numFoundPlayerServers++; 4088 } 4089 else { 4090 // can't add any more so we're done 4091 uiInfo.pendingServerStatus.num = uiInfo.serverStatus.numDisplayServers; 4092 } 4093 } 4094 } 4095 Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], 4096 sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), 4097 "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound); 4098 // retrieved the server status so reuse this spot 4099 uiInfo.pendingServerStatus.server[i].valid = qfalse; 4100 } 4101 } 4102 // if empty pending slot or timed out 4103 if (!uiInfo.pendingServerStatus.server[i].valid || 4104 uiInfo.pendingServerStatus.server[i].startTime < uiInfo.uiDC.realTime - ui_serverStatusTimeOut.integer) { 4105 if (uiInfo.pendingServerStatus.server[i].valid) { 4106 numTimeOuts++; 4107 } 4108 // reset server status request for this address 4109 UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, NULL ); 4110 // reuse pending slot 4111 uiInfo.pendingServerStatus.server[i].valid = qfalse; 4112 // if we didn't try to get the status of all servers in the main browser yet 4113 if (uiInfo.pendingServerStatus.num < uiInfo.serverStatus.numDisplayServers) { 4114 uiInfo.pendingServerStatus.server[i].startTime = uiInfo.uiDC.realTime; 4115 trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num], 4116 uiInfo.pendingServerStatus.server[i].adrstr, sizeof(uiInfo.pendingServerStatus.server[i].adrstr)); 4117 trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num], infoString, sizeof(infoString)); 4118 Q_strncpyz(uiInfo.pendingServerStatus.server[i].name, Info_ValueForKey(infoString, "hostname"), sizeof(uiInfo.pendingServerStatus.server[0].name)); 4119 uiInfo.pendingServerStatus.server[i].valid = qtrue; 4120 uiInfo.pendingServerStatus.num++; 4121 Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], 4122 sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]), 4123 "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound); 4124 } 4125 } 4126 } 4127 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 4128 if (uiInfo.pendingServerStatus.server[i].valid) { 4129 break; 4130 } 4131 } 4132 // if still trying to retrieve server status info 4133 if (i < MAX_SERVERSTATUSREQUESTS) { 4134 uiInfo.nextFindPlayerRefresh = uiInfo.uiDC.realTime + 25; 4135 } 4136 else { 4137 // add a line that shows the number of servers found 4138 if (!uiInfo.numFoundPlayerServers) { 4139 Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]), "no servers found"); 4140 } 4141 else { 4142 Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]), 4143 "%d server%s found with player %s", uiInfo.numFoundPlayerServers-1, 4144 uiInfo.numFoundPlayerServers == 2 ? "":"s", uiInfo.findPlayerName); 4145 } 4146 uiInfo.nextFindPlayerRefresh = 0; 4147 // show the server status info for the selected server 4148 UI_FeederSelection(FEEDER_FINDPLAYER, uiInfo.currentFoundPlayerServer); 4149 } 4150 } 4151 4152 /* 4153 ================== 4154 UI_BuildServerStatus 4155 ================== 4156 */ 4157 static void UI_BuildServerStatus(qboolean force) { 4158 4159 if (uiInfo.nextFindPlayerRefresh) { 4160 return; 4161 } 4162 if (!force) { 4163 if (!uiInfo.nextServerStatusRefresh || uiInfo.nextServerStatusRefresh > uiInfo.uiDC.realTime) { 4164 return; 4165 } 4166 } 4167 else { 4168 Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL); 4169 uiInfo.serverStatusInfo.numLines = 0; 4170 // reset all server status requests 4171 trap_LAN_ServerStatus( NULL, NULL, 0); 4172 } 4173 if (uiInfo.serverStatus.currentServer < 0 || uiInfo.serverStatus.currentServer > uiInfo.serverStatus.numDisplayServers || uiInfo.serverStatus.numDisplayServers == 0) { 4174 return; 4175 } 4176 if (UI_GetServerStatusInfo( uiInfo.serverStatusAddress, &uiInfo.serverStatusInfo ) ) { 4177 uiInfo.nextServerStatusRefresh = 0; 4178 UI_GetServerStatusInfo( uiInfo.serverStatusAddress, NULL ); 4179 } 4180 else { 4181 uiInfo.nextServerStatusRefresh = uiInfo.uiDC.realTime + 500; 4182 } 4183 } 4184 4185 /* 4186 ================== 4187 UI_FeederCount 4188 ================== 4189 */ 4190 static int UI_FeederCount(float feederID) { 4191 if (feederID == FEEDER_HEADS) { 4192 return UI_HeadCountByTeam(); 4193 } else if (feederID == FEEDER_Q3HEADS) { 4194 return uiInfo.q3HeadCount; 4195 } else if (feederID == FEEDER_CINEMATICS) { 4196 return uiInfo.movieCount; 4197 } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { 4198 return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse); 4199 } else if (feederID == FEEDER_SERVERS) { 4200 return uiInfo.serverStatus.numDisplayServers; 4201 } else if (feederID == FEEDER_SERVERSTATUS) { 4202 return uiInfo.serverStatusInfo.numLines; 4203 } else if (feederID == FEEDER_FINDPLAYER) { 4204 return uiInfo.numFoundPlayerServers; 4205 } else if (feederID == FEEDER_PLAYER_LIST) { 4206 if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { 4207 uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; 4208 UI_BuildPlayerList(); 4209 } 4210 return uiInfo.playerCount; 4211 } else if (feederID == FEEDER_TEAM_LIST) { 4212 if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { 4213 uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; 4214 UI_BuildPlayerList(); 4215 } 4216 return uiInfo.myTeamCount; 4217 } else if (feederID == FEEDER_MODS) { 4218 return uiInfo.modCount; 4219 } else if (feederID == FEEDER_DEMOS) { 4220 return uiInfo.demoCount; 4221 } 4222 return 0; 4223 } 4224 4225 static const char *UI_SelectedMap(int index, int *actual) { 4226 int i, c; 4227 c = 0; 4228 *actual = 0; 4229 for (i = 0; i < uiInfo.mapCount; i++) { 4230 if (uiInfo.mapList[i].active) { 4231 if (c == index) { 4232 *actual = i; 4233 return uiInfo.mapList[i].mapName; 4234 } else { 4235 c++; 4236 } 4237 } 4238 } 4239 return ""; 4240 } 4241 4242 static const char *UI_SelectedHead(int index, int *actual) { 4243 int i, c; 4244 c = 0; 4245 *actual = 0; 4246 for (i = 0; i < uiInfo.characterCount; i++) { 4247 if (uiInfo.characterList[i].active) { 4248 if (c == index) { 4249 *actual = i; 4250 return uiInfo.characterList[i].name; 4251 } else { 4252 c++; 4253 } 4254 } 4255 } 4256 return ""; 4257 } 4258 4259 static int UI_GetIndexFromSelection(int actual) { 4260 int i, c; 4261 c = 0; 4262 for (i = 0; i < uiInfo.mapCount; i++) { 4263 if (uiInfo.mapList[i].active) { 4264 if (i == actual) { 4265 return c; 4266 } 4267 c++; 4268 } 4269 } 4270 return 0; 4271 } 4272 4273 static void UI_UpdatePendingPings() { 4274 trap_LAN_ResetPings(ui_netSource.integer); 4275 uiInfo.serverStatus.refreshActive = qtrue; 4276 uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; 4277 4278 } 4279 4280 static const char *UI_FeederItemText(float feederID, int index, int column, qhandle_t *handle) { 4281 static char info[MAX_STRING_CHARS]; 4282 static char hostname[1024]; 4283 static char clientBuff[32]; 4284 static int lastColumn = -1; 4285 static int lastTime = 0; 4286 *handle = -1; 4287 if (feederID == FEEDER_HEADS) { 4288 int actual; 4289 return UI_SelectedHead(index, &actual); 4290 } else if (feederID == FEEDER_Q3HEADS) { 4291 if (index >= 0 && index < uiInfo.q3HeadCount) { 4292 return uiInfo.q3HeadNames[index]; 4293 } 4294 } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { 4295 int actual; 4296 return UI_SelectedMap(index, &actual); 4297 } else if (feederID == FEEDER_SERVERS) { 4298 if (index >= 0 && index < uiInfo.serverStatus.numDisplayServers) { 4299 int ping, game, punkbuster; 4300 if (lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000) { 4301 trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS); 4302 lastColumn = column; 4303 lastTime = uiInfo.uiDC.realTime; 4304 } 4305 ping = atoi(Info_ValueForKey(info, "ping")); 4306 if (ping == -1) { 4307 // if we ever see a ping that is out of date, do a server refresh 4308 // UI_UpdatePendingPings(); 4309 } 4310 switch (column) { 4311 case SORT_HOST : 4312 if (ping <= 0) { 4313 return Info_ValueForKey(info, "addr"); 4314 } else { 4315 if ( ui_netSource.integer == AS_LOCAL ) { 4316 Com_sprintf( hostname, sizeof(hostname), "%s [%s]", 4317 Info_ValueForKey(info, "hostname"), 4318 netnames[atoi(Info_ValueForKey(info, "nettype"))] ); 4319 return hostname; 4320 } 4321 else { 4322 Com_sprintf( hostname, sizeof(hostname), "%s", Info_ValueForKey(info, "hostname")); 4323 return hostname; 4324 } 4325 } 4326 case SORT_MAP : return Info_ValueForKey(info, "mapname"); 4327 case SORT_CLIENTS : 4328 Com_sprintf( clientBuff, sizeof(clientBuff), "%s (%s)", Info_ValueForKey(info, "clients"), Info_ValueForKey(info, "sv_maxclients")); 4329 return clientBuff; 4330 case SORT_GAME : 4331 game = atoi(Info_ValueForKey(info, "gametype")); 4332 if (game >= 0 && game < numTeamArenaGameTypes) { 4333 return teamArenaGameTypes[game]; 4334 } else { 4335 return "Unknown"; 4336 } 4337 case SORT_PING : 4338 if (ping <= 0) { 4339 return "..."; 4340 } else { 4341 return Info_ValueForKey(info, "ping"); 4342 } 4343 case SORT_PUNKBUSTER: 4344 punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); 4345 if ( punkbuster ) { 4346 return "Yes"; 4347 } else { 4348 return "No"; 4349 } 4350 } 4351 } 4352 } else if (feederID == FEEDER_SERVERSTATUS) { 4353 if ( index >= 0 && index < uiInfo.serverStatusInfo.numLines ) { 4354 if ( column >= 0 && column < 4 ) { 4355 return uiInfo.serverStatusInfo.lines[index][column]; 4356 } 4357 } 4358 } else if (feederID == FEEDER_FINDPLAYER) { 4359 if ( index >= 0 && index < uiInfo.numFoundPlayerServers ) { 4360 //return uiInfo.foundPlayerServerAddresses[index]; 4361 return uiInfo.foundPlayerServerNames[index]; 4362 } 4363 } else if (feederID == FEEDER_PLAYER_LIST) { 4364 if (index >= 0 && index < uiInfo.playerCount) { 4365 return uiInfo.playerNames[index]; 4366 } 4367 } else if (feederID == FEEDER_TEAM_LIST) { 4368 if (index >= 0 && index < uiInfo.myTeamCount) { 4369 return uiInfo.teamNames[index]; 4370 } 4371 } else if (feederID == FEEDER_MODS) { 4372 if (index >= 0 && index < uiInfo.modCount) { 4373 if (uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr) { 4374 return uiInfo.modList[index].modDescr; 4375 } else { 4376 return uiInfo.modList[index].modName; 4377 } 4378 } 4379 } else if (feederID == FEEDER_CINEMATICS) { 4380 if (index >= 0 && index < uiInfo.movieCount) { 4381 return uiInfo.movieList[index]; 4382 } 4383 } else if (feederID == FEEDER_DEMOS) { 4384 if (index >= 0 && index < uiInfo.demoCount) { 4385 return uiInfo.demoList[index]; 4386 } 4387 } 4388 return ""; 4389 } 4390 4391 4392 static qhandle_t UI_FeederItemImage(float feederID, int index) { 4393 if (feederID == FEEDER_HEADS) { 4394 int actual; 4395 UI_SelectedHead(index, &actual); 4396 index = actual; 4397 if (index >= 0 && index < uiInfo.characterCount) { 4398 if (uiInfo.characterList[index].headImage == -1) { 4399 uiInfo.characterList[index].headImage = trap_R_RegisterShaderNoMip(uiInfo.characterList[index].imageName); 4400 } 4401 return uiInfo.characterList[index].headImage; 4402 } 4403 } else if (feederID == FEEDER_Q3HEADS) { 4404 if (index >= 0 && index < uiInfo.q3HeadCount) { 4405 return uiInfo.q3HeadIcons[index]; 4406 } 4407 } else if (feederID == FEEDER_ALLMAPS || feederID == FEEDER_MAPS) { 4408 int actual; 4409 UI_SelectedMap(index, &actual); 4410 index = actual; 4411 if (index >= 0 && index < uiInfo.mapCount) { 4412 if (uiInfo.mapList[index].levelShot == -1) { 4413 uiInfo.mapList[index].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[index].imageName); 4414 } 4415 return uiInfo.mapList[index].levelShot; 4416 } 4417 } 4418 return 0; 4419 } 4420 4421 static void UI_FeederSelection(float feederID, int index) { 4422 static char info[MAX_STRING_CHARS]; 4423 if (feederID == FEEDER_HEADS) { 4424 int actual; 4425 UI_SelectedHead(index, &actual); 4426 index = actual; 4427 if (index >= 0 && index < uiInfo.characterCount) { 4428 trap_Cvar_Set( "team_model", va("%s", uiInfo.characterList[index].base)); 4429 trap_Cvar_Set( "team_headmodel", va("*%s", uiInfo.characterList[index].name)); 4430 updateModel = qtrue; 4431 } 4432 } else if (feederID == FEEDER_Q3HEADS) { 4433 if (index >= 0 && index < uiInfo.q3HeadCount) { 4434 trap_Cvar_Set( "model", uiInfo.q3HeadNames[index]); 4435 trap_Cvar_Set( "headmodel", uiInfo.q3HeadNames[index]); 4436 updateModel = qtrue; 4437 } 4438 } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) { 4439 int actual, map; 4440 map = (feederID == FEEDER_ALLMAPS) ? ui_currentNetMap.integer : ui_currentMap.integer; 4441 if (uiInfo.mapList[map].cinematic >= 0) { 4442 trap_CIN_StopCinematic(uiInfo.mapList[map].cinematic); 4443 uiInfo.mapList[map].cinematic = -1; 4444 } 4445 UI_SelectedMap(index, &actual); 4446 trap_Cvar_Set("ui_mapIndex", va("%d", index)); 4447 ui_mapIndex.integer = index; 4448 4449 if (feederID == FEEDER_MAPS) { 4450 ui_currentMap.integer = actual; 4451 trap_Cvar_Set("ui_currentMap", va("%d", actual)); 4452 uiInfo.mapList[ui_currentMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 4453 UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); 4454 trap_Cvar_Set("ui_opponentModel", uiInfo.mapList[ui_currentMap.integer].opponentName); 4455 updateOpponentModel = qtrue; 4456 } else { 4457 ui_currentNetMap.integer = actual; 4458 trap_Cvar_Set("ui_currentNetMap", va("%d", actual)); 4459 uiInfo.mapList[ui_currentNetMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 4460 } 4461 4462 } else if (feederID == FEEDER_SERVERS) { 4463 const char *mapName = NULL; 4464 uiInfo.serverStatus.currentServer = index; 4465 trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS); 4466 uiInfo.serverStatus.currentServerPreview = trap_R_RegisterShaderNoMip(va("levelshots/%s", Info_ValueForKey(info, "mapname"))); 4467 if (uiInfo.serverStatus.currentServerCinematic >= 0) { 4468 trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic); 4469 uiInfo.serverStatus.currentServerCinematic = -1; 4470 } 4471 mapName = Info_ValueForKey(info, "mapname"); 4472 if (mapName && *mapName) { 4473 uiInfo.serverStatus.currentServerCinematic = trap_CIN_PlayCinematic(va("%s.roq", mapName), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); 4474 } 4475 } else if (feederID == FEEDER_SERVERSTATUS) { 4476 // 4477 } else if (feederID == FEEDER_FINDPLAYER) { 4478 uiInfo.currentFoundPlayerServer = index; 4479 // 4480 if ( index < uiInfo.numFoundPlayerServers-1) { 4481 // build a new server status for this server 4482 Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress)); 4483 Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL); 4484 UI_BuildServerStatus(qtrue); 4485 } 4486 } else if (feederID == FEEDER_PLAYER_LIST) { 4487 uiInfo.playerIndex = index; 4488 } else if (feederID == FEEDER_TEAM_LIST) { 4489 uiInfo.teamIndex = index; 4490 } else if (feederID == FEEDER_MODS) { 4491 uiInfo.modIndex = index; 4492 } else if (feederID == FEEDER_CINEMATICS) { 4493 uiInfo.movieIndex = index; 4494 if (uiInfo.previewMovie >= 0) { 4495 trap_CIN_StopCinematic(uiInfo.previewMovie); 4496 } 4497 uiInfo.previewMovie = -1; 4498 } else if (feederID == FEEDER_DEMOS) { 4499 uiInfo.demoIndex = index; 4500 } 4501 } 4502 4503 static qboolean Team_Parse(char **p) { 4504 char *token; 4505 const char *tempStr; 4506 int i; 4507 4508 token = COM_ParseExt(p, qtrue); 4509 4510 if (token[0] != '{') { 4511 return qfalse; 4512 } 4513 4514 while ( 1 ) { 4515 4516 token = COM_ParseExt(p, qtrue); 4517 4518 if (Q_stricmp(token, "}") == 0) { 4519 return qtrue; 4520 } 4521 4522 if ( !token || token[0] == 0 ) { 4523 return qfalse; 4524 } 4525 4526 if (token[0] == '{') { 4527 // seven tokens per line, team name and icon, and 5 team member names 4528 if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamName) || !String_Parse(p, &tempStr)) { 4529 return qfalse; 4530 } 4531 4532 4533 uiInfo.teamList[uiInfo.teamCount].imageName = tempStr; 4534 uiInfo.teamList[uiInfo.teamCount].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[uiInfo.teamCount].imageName); 4535 uiInfo.teamList[uiInfo.teamCount].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[uiInfo.teamCount].imageName)); 4536 uiInfo.teamList[uiInfo.teamCount].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[uiInfo.teamCount].imageName)); 4537 4538 uiInfo.teamList[uiInfo.teamCount].cinematic = -1; 4539 4540 for (i = 0; i < TEAM_MEMBERS; i++) { 4541 uiInfo.teamList[uiInfo.teamCount].teamMembers[i] = NULL; 4542 if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamMembers[i])) { 4543 return qfalse; 4544 } 4545 } 4546 4547 Com_Printf("Loaded team %s with team icon %s.\n", uiInfo.teamList[uiInfo.teamCount].teamName, tempStr); 4548 if (uiInfo.teamCount < MAX_TEAMS) { 4549 uiInfo.teamCount++; 4550 } else { 4551 Com_Printf("Too many teams, last team replaced!\n"); 4552 } 4553 token = COM_ParseExt(p, qtrue); 4554 if (token[0] != '}') { 4555 return qfalse; 4556 } 4557 } 4558 } 4559 4560 return qfalse; 4561 } 4562 4563 static qboolean Character_Parse(char **p) { 4564 char *token; 4565 const char *tempStr; 4566 4567 token = COM_ParseExt(p, qtrue); 4568 4569 if (token[0] != '{') { 4570 return qfalse; 4571 } 4572 4573 4574 while ( 1 ) { 4575 token = COM_ParseExt(p, qtrue); 4576 4577 if (Q_stricmp(token, "}") == 0) { 4578 return qtrue; 4579 } 4580 4581 if ( !token || token[0] == 0 ) { 4582 return qfalse; 4583 } 4584 4585 if (token[0] == '{') { 4586 // two tokens per line, character name and sex 4587 if (!String_Parse(p, &uiInfo.characterList[uiInfo.characterCount].name) || !String_Parse(p, &tempStr)) { 4588 return qfalse; 4589 } 4590 4591 uiInfo.characterList[uiInfo.characterCount].headImage = -1; 4592 uiInfo.characterList[uiInfo.characterCount].imageName = String_Alloc(va("models/players/heads/%s/icon_default.tga", uiInfo.characterList[uiInfo.characterCount].name)); 4593 4594 if (tempStr && (!Q_stricmp(tempStr, "female"))) { 4595 uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("Janet")); 4596 } else if (tempStr && (!Q_stricmp(tempStr, "male"))) { 4597 uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("James")); 4598 } else { 4599 uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("%s",tempStr)); 4600 } 4601 4602 Com_Printf("Loaded %s character %s.\n", uiInfo.characterList[uiInfo.characterCount].base, uiInfo.characterList[uiInfo.characterCount].name); 4603 if (uiInfo.characterCount < MAX_HEADS) { 4604 uiInfo.characterCount++; 4605 } else { 4606 Com_Printf("Too many characters, last character replaced!\n"); 4607 } 4608 4609 token = COM_ParseExt(p, qtrue); 4610 if (token[0] != '}') { 4611 return qfalse; 4612 } 4613 } 4614 } 4615 4616 return qfalse; 4617 } 4618 4619 4620 static qboolean Alias_Parse(char **p) { 4621 char *token; 4622 4623 token = COM_ParseExt(p, qtrue); 4624 4625 if (token[0] != '{') { 4626 return qfalse; 4627 } 4628 4629 while ( 1 ) { 4630 token = COM_ParseExt(p, qtrue); 4631 4632 if (Q_stricmp(token, "}") == 0) { 4633 return qtrue; 4634 } 4635 4636 if ( !token || token[0] == 0 ) { 4637 return qfalse; 4638 } 4639 4640 if (token[0] == '{') { 4641 // three tokens per line, character name, bot alias, and preferred action a - all purpose, d - defense, o - offense 4642 if (!String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].name) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].ai) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].action)) { 4643 return qfalse; 4644 } 4645 4646 Com_Printf("Loaded character alias %s using character ai %s.\n", uiInfo.aliasList[uiInfo.aliasCount].name, uiInfo.aliasList[uiInfo.aliasCount].ai); 4647 if (uiInfo.aliasCount < MAX_ALIASES) { 4648 uiInfo.aliasCount++; 4649 } else { 4650 Com_Printf("Too many aliases, last alias replaced!\n"); 4651 } 4652 4653 token = COM_ParseExt(p, qtrue); 4654 if (token[0] != '}') { 4655 return qfalse; 4656 } 4657 } 4658 } 4659 4660 return qfalse; 4661 } 4662 4663 4664 4665 // mode 4666 // 0 - high level parsing 4667 // 1 - team parsing 4668 // 2 - character parsing 4669 static void UI_ParseTeamInfo(const char *teamFile) { 4670 char *token; 4671 char *p; 4672 char *buff = NULL; 4673 //static int mode = 0; TTimo: unused 4674 4675 buff = GetMenuBuffer(teamFile); 4676 if (!buff) { 4677 return; 4678 } 4679 4680 p = buff; 4681 4682 while ( 1 ) { 4683 token = COM_ParseExt( &p, qtrue ); 4684 if( !token || token[0] == 0 || token[0] == '}') { 4685 break; 4686 } 4687 4688 if ( Q_stricmp( token, "}" ) == 0 ) { 4689 break; 4690 } 4691 4692 if (Q_stricmp(token, "teams") == 0) { 4693 4694 if (Team_Parse(&p)) { 4695 continue; 4696 } else { 4697 break; 4698 } 4699 } 4700 4701 if (Q_stricmp(token, "characters") == 0) { 4702 Character_Parse(&p); 4703 } 4704 4705 if (Q_stricmp(token, "aliases") == 0) { 4706 Alias_Parse(&p); 4707 } 4708 4709 } 4710 4711 } 4712 4713 4714 static qboolean GameType_Parse(char **p, qboolean join) { 4715 char *token; 4716 4717 token = COM_ParseExt(p, qtrue); 4718 4719 if (token[0] != '{') { 4720 return qfalse; 4721 } 4722 4723 if (join) { 4724 uiInfo.numJoinGameTypes = 0; 4725 } else { 4726 uiInfo.numGameTypes = 0; 4727 } 4728 4729 while ( 1 ) { 4730 token = COM_ParseExt(p, qtrue); 4731 4732 if (Q_stricmp(token, "}") == 0) { 4733 return qtrue; 4734 } 4735 4736 if ( !token || token[0] == 0 ) { 4737 return qfalse; 4738 } 4739 4740 if (token[0] == '{') { 4741 // two tokens per line, character name and sex 4742 if (join) { 4743 if (!String_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gameType) || !Int_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gtEnum)) { 4744 return qfalse; 4745 } 4746 } else { 4747 if (!String_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gameType) || !Int_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gtEnum)) { 4748 return qfalse; 4749 } 4750 } 4751 4752 if (join) { 4753 if (uiInfo.numJoinGameTypes < MAX_GAMETYPES) { 4754 uiInfo.numJoinGameTypes++; 4755 } else { 4756 Com_Printf("Too many net game types, last one replace!\n"); 4757 } 4758 } else { 4759 if (uiInfo.numGameTypes < MAX_GAMETYPES) { 4760 uiInfo.numGameTypes++; 4761 } else { 4762 Com_Printf("Too many game types, last one replace!\n"); 4763 } 4764 } 4765 4766 token = COM_ParseExt(p, qtrue); 4767 if (token[0] != '}') { 4768 return qfalse; 4769 } 4770 } 4771 } 4772 return qfalse; 4773 } 4774 4775 static qboolean MapList_Parse(char **p) { 4776 char *token; 4777 4778 token = COM_ParseExt(p, qtrue); 4779 4780 if (token[0] != '{') { 4781 return qfalse; 4782 } 4783 4784 uiInfo.mapCount = 0; 4785 4786 while ( 1 ) { 4787 token = COM_ParseExt(p, qtrue); 4788 4789 if (Q_stricmp(token, "}") == 0) { 4790 return qtrue; 4791 } 4792 4793 if ( !token || token[0] == 0 ) { 4794 return qfalse; 4795 } 4796 4797 if (token[0] == '{') { 4798 if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapName) || !String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapLoadName) 4799 ||!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].teamMembers) ) { 4800 return qfalse; 4801 } 4802 4803 if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].opponentName)) { 4804 return qfalse; 4805 } 4806 4807 uiInfo.mapList[uiInfo.mapCount].typeBits = 0; 4808 4809 while (1) { 4810 token = COM_ParseExt(p, qtrue); 4811 if (token[0] >= '0' && token[0] <= '9') { 4812 uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << (token[0] - 0x030)); 4813 if (!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].timeToBeat[token[0] - 0x30])) { 4814 return qfalse; 4815 } 4816 } else { 4817 break; 4818 } 4819 } 4820 4821 //mapList[mapCount].imageName = String_Alloc(va("levelshots/%s", mapList[mapCount].mapLoadName)); 4822 //if (uiInfo.mapCount == 0) { 4823 // only load the first cinematic, selection loads the others 4824 // uiInfo.mapList[uiInfo.mapCount].cinematic = trap_CIN_PlayCinematic(va("%s.roq",uiInfo.mapList[uiInfo.mapCount].mapLoadName), qfalse, qfalse, qtrue, 0, 0, 0, 0); 4825 //} 4826 uiInfo.mapList[uiInfo.mapCount].cinematic = -1; 4827 uiInfo.mapList[uiInfo.mapCount].levelShot = trap_R_RegisterShaderNoMip(va("levelshots/%s_small", uiInfo.mapList[uiInfo.mapCount].mapLoadName)); 4828 4829 if (uiInfo.mapCount < MAX_MAPS) { 4830 uiInfo.mapCount++; 4831 } else { 4832 Com_Printf("Too many maps, last one replaced!\n"); 4833 } 4834 } 4835 } 4836 return qfalse; 4837 } 4838 4839 static void UI_ParseGameInfo(const char *teamFile) { 4840 char *token; 4841 char *p; 4842 char *buff = NULL; 4843 //int mode = 0; TTimo: unused 4844 4845 buff = GetMenuBuffer(teamFile); 4846 if (!buff) { 4847 return; 4848 } 4849 4850 p = buff; 4851 4852 while ( 1 ) { 4853 token = COM_ParseExt( &p, qtrue ); 4854 if( !token || token[0] == 0 || token[0] == '}') { 4855 break; 4856 } 4857 4858 if ( Q_stricmp( token, "}" ) == 0 ) { 4859 break; 4860 } 4861 4862 if (Q_stricmp(token, "gametypes") == 0) { 4863 4864 if (GameType_Parse(&p, qfalse)) { 4865 continue; 4866 } else { 4867 break; 4868 } 4869 } 4870 4871 if (Q_stricmp(token, "joingametypes") == 0) { 4872 4873 if (GameType_Parse(&p, qtrue)) { 4874 continue; 4875 } else { 4876 break; 4877 } 4878 } 4879 4880 if (Q_stricmp(token, "maps") == 0) { 4881 // start a new menu 4882 MapList_Parse(&p); 4883 } 4884 4885 } 4886 } 4887 4888 static void UI_Pause(qboolean b) { 4889 if (b) { 4890 // pause the game and set the ui keycatcher 4891 trap_Cvar_Set( "cl_paused", "1" ); 4892 trap_Key_SetCatcher( KEYCATCH_UI ); 4893 } else { 4894 // unpause the game and clear the ui keycatcher 4895 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 4896 trap_Key_ClearStates(); 4897 trap_Cvar_Set( "cl_paused", "0" ); 4898 } 4899 } 4900 4901 #ifndef MISSIONPACK // bk001206 4902 static int UI_OwnerDraw_Width(int ownerDraw) { 4903 // bk001205 - LCC missing return value 4904 return 0; 4905 } 4906 #endif 4907 4908 static int UI_PlayCinematic(const char *name, float x, float y, float w, float h) { 4909 return trap_CIN_PlayCinematic(name, x, y, w, h, (CIN_loop | CIN_silent)); 4910 } 4911 4912 static void UI_StopCinematic(int handle) { 4913 if (handle >= 0) { 4914 trap_CIN_StopCinematic(handle); 4915 } else { 4916 handle = abs(handle); 4917 if (handle == UI_MAPCINEMATIC) { 4918 if (uiInfo.mapList[ui_currentMap.integer].cinematic >= 0) { 4919 trap_CIN_StopCinematic(uiInfo.mapList[ui_currentMap.integer].cinematic); 4920 uiInfo.mapList[ui_currentMap.integer].cinematic = -1; 4921 } 4922 } else if (handle == UI_NETMAPCINEMATIC) { 4923 if (uiInfo.serverStatus.currentServerCinematic >= 0) { 4924 trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic); 4925 uiInfo.serverStatus.currentServerCinematic = -1; 4926 } 4927 } else if (handle == UI_CLANCINEMATIC) { 4928 int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); 4929 if (i >= 0 && i < uiInfo.teamCount) { 4930 if (uiInfo.teamList[i].cinematic >= 0) { 4931 trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic); 4932 uiInfo.teamList[i].cinematic = -1; 4933 } 4934 } 4935 } 4936 } 4937 } 4938 4939 static void UI_DrawCinematic(int handle, float x, float y, float w, float h) { 4940 trap_CIN_SetExtents(handle, x, y, w, h); 4941 trap_CIN_DrawCinematic(handle); 4942 } 4943 4944 static void UI_RunCinematicFrame(int handle) { 4945 trap_CIN_RunCinematic(handle); 4946 } 4947 4948 4949 4950 /* 4951 ================= 4952 PlayerModel_BuildList 4953 ================= 4954 */ 4955 static void UI_BuildQ3Model_List( void ) 4956 { 4957 int numdirs; 4958 int numfiles; 4959 char dirlist[2048]; 4960 char filelist[2048]; 4961 char skinname[64]; 4962 char scratch[256]; 4963 char* dirptr; 4964 char* fileptr; 4965 int i; 4966 int j, k, dirty; 4967 int dirlen; 4968 int filelen; 4969 4970 uiInfo.q3HeadCount = 0; 4971 4972 // iterate directory of all player models 4973 numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 2048 ); 4974 dirptr = dirlist; 4975 for (i=0; i<numdirs && uiInfo.q3HeadCount < MAX_PLAYERMODELS; i++,dirptr+=dirlen+1) 4976 { 4977 dirlen = strlen(dirptr); 4978 4979 if (dirlen && dirptr[dirlen-1]=='/') dirptr[dirlen-1]='\0'; 4980 4981 if (!strcmp(dirptr,".") || !strcmp(dirptr,"..")) 4982 continue; 4983 4984 // iterate all skin files in directory 4985 numfiles = trap_FS_GetFileList( va("models/players/%s",dirptr), "tga", filelist, 2048 ); 4986 fileptr = filelist; 4987 for (j=0; j<numfiles && uiInfo.q3HeadCount < MAX_PLAYERMODELS;j++,fileptr+=filelen+1) 4988 { 4989 filelen = strlen(fileptr); 4990 4991 COM_StripExtension(fileptr,skinname); 4992 4993 // look for icon_???? 4994 if (Q_stricmpn(skinname, "icon_", 5) == 0 && !(Q_stricmp(skinname,"icon_blue") == 0 || Q_stricmp(skinname,"icon_red") == 0)) 4995 { 4996 if (Q_stricmp(skinname, "icon_default") == 0) { 4997 Com_sprintf( scratch, sizeof(scratch), dirptr); 4998 } else { 4999 Com_sprintf( scratch, sizeof(scratch), "%s/%s",dirptr, skinname + 5); 5000 } 5001 dirty = 0; 5002 for(k=0;k<uiInfo.q3HeadCount;k++) { 5003 if (!Q_stricmp(scratch, uiInfo.q3HeadNames[uiInfo.q3HeadCount])) { 5004 dirty = 1; 5005 break; 5006 } 5007 } 5008 if (!dirty) { 5009 Com_sprintf( uiInfo.q3HeadNames[uiInfo.q3HeadCount], sizeof(uiInfo.q3HeadNames[uiInfo.q3HeadCount]), scratch); 5010 uiInfo.q3HeadIcons[uiInfo.q3HeadCount++] = trap_R_RegisterShaderNoMip(va("models/players/%s/%s",dirptr,skinname)); 5011 } 5012 } 5013 5014 } 5015 } 5016 5017 } 5018 5019 5020 5021 /* 5022 ================= 5023 UI_Init 5024 ================= 5025 */ 5026 void _UI_Init( qboolean inGameLoad ) { 5027 const char *menuSet; 5028 int start; 5029 5030 //uiInfo.inGameLoad = inGameLoad; 5031 5032 UI_RegisterCvars(); 5033 UI_InitMemory(); 5034 5035 // cache redundant calulations 5036 trap_GetGlconfig( &uiInfo.uiDC.glconfig ); 5037 5038 // for 640x480 virtualized screen 5039 uiInfo.uiDC.yscale = uiInfo.uiDC.glconfig.vidHeight * (1.0/480.0); 5040 uiInfo.uiDC.xscale = uiInfo.uiDC.glconfig.vidWidth * (1.0/640.0); 5041 if ( uiInfo.uiDC.glconfig.vidWidth * 480 > uiInfo.uiDC.glconfig.vidHeight * 640 ) { 5042 // wide screen 5043 uiInfo.uiDC.bias = 0.5 * ( uiInfo.uiDC.glconfig.vidWidth - ( uiInfo.uiDC.glconfig.vidHeight * (640.0/480.0) ) ); 5044 } 5045 else { 5046 // no wide screen 5047 uiInfo.uiDC.bias = 0; 5048 } 5049 5050 5051 //UI_Load(); 5052 uiInfo.uiDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip; 5053 uiInfo.uiDC.setColor = &UI_SetColor; 5054 uiInfo.uiDC.drawHandlePic = &UI_DrawHandlePic; 5055 uiInfo.uiDC.drawStretchPic = &trap_R_DrawStretchPic; 5056 uiInfo.uiDC.drawText = &Text_Paint; 5057 uiInfo.uiDC.textWidth = &Text_Width; 5058 uiInfo.uiDC.textHeight = &Text_Height; 5059 uiInfo.uiDC.registerModel = &trap_R_RegisterModel; 5060 uiInfo.uiDC.modelBounds = &trap_R_ModelBounds; 5061 uiInfo.uiDC.fillRect = &UI_FillRect; 5062 uiInfo.uiDC.drawRect = &_UI_DrawRect; 5063 uiInfo.uiDC.drawSides = &_UI_DrawSides; 5064 uiInfo.uiDC.drawTopBottom = &_UI_DrawTopBottom; 5065 uiInfo.uiDC.clearScene = &trap_R_ClearScene; 5066 uiInfo.uiDC.drawSides = &_UI_DrawSides; 5067 uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene; 5068 uiInfo.uiDC.renderScene = &trap_R_RenderScene; 5069 uiInfo.uiDC.registerFont = &trap_R_RegisterFont; 5070 uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw; 5071 uiInfo.uiDC.getValue = &UI_GetValue; 5072 uiInfo.uiDC.ownerDrawVisible = &UI_OwnerDrawVisible; 5073 uiInfo.uiDC.runScript = &UI_RunMenuScript; 5074 uiInfo.uiDC.getTeamColor = &UI_GetTeamColor; 5075 uiInfo.uiDC.setCVar = trap_Cvar_Set; 5076 uiInfo.uiDC.getCVarString = trap_Cvar_VariableStringBuffer; 5077 uiInfo.uiDC.getCVarValue = trap_Cvar_VariableValue; 5078 uiInfo.uiDC.drawTextWithCursor = &Text_PaintWithCursor; 5079 uiInfo.uiDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode; 5080 uiInfo.uiDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode; 5081 uiInfo.uiDC.startLocalSound = &trap_S_StartLocalSound; 5082 uiInfo.uiDC.ownerDrawHandleKey = &UI_OwnerDrawHandleKey; 5083 uiInfo.uiDC.feederCount = &UI_FeederCount; 5084 uiInfo.uiDC.feederItemImage = &UI_FeederItemImage; 5085 uiInfo.uiDC.feederItemText = &UI_FeederItemText; 5086 uiInfo.uiDC.feederSelection = &UI_FeederSelection; 5087 uiInfo.uiDC.setBinding = &trap_Key_SetBinding; 5088 uiInfo.uiDC.getBindingBuf = &trap_Key_GetBindingBuf; 5089 uiInfo.uiDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf; 5090 uiInfo.uiDC.executeText = &trap_Cmd_ExecuteText; 5091 uiInfo.uiDC.Error = &Com_Error; 5092 uiInfo.uiDC.Print = &Com_Printf; 5093 uiInfo.uiDC.Pause = &UI_Pause; 5094 uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth; 5095 uiInfo.uiDC.registerSound = &trap_S_RegisterSound; 5096 uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; 5097 uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; 5098 uiInfo.uiDC.playCinematic = &UI_PlayCinematic; 5099 uiInfo.uiDC.stopCinematic = &UI_StopCinematic; 5100 uiInfo.uiDC.drawCinematic = &UI_DrawCinematic; 5101 uiInfo.uiDC.runCinematicFrame = &UI_RunCinematicFrame; 5102 5103 Init_Display(&uiInfo.uiDC); 5104 5105 String_Init(); 5106 5107 uiInfo.uiDC.cursor = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" ); 5108 uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip( "white" ); 5109 5110 AssetCache(); 5111 5112 start = trap_Milliseconds(); 5113 5114 uiInfo.teamCount = 0; 5115 uiInfo.characterCount = 0; 5116 uiInfo.aliasCount = 0; 5117 5118 #ifdef PRE_RELEASE_TADEMO 5119 UI_ParseTeamInfo("demoteaminfo.txt"); 5120 UI_ParseGameInfo("demogameinfo.txt"); 5121 #else 5122 UI_ParseTeamInfo("teaminfo.txt"); 5123 UI_LoadTeams(); 5124 UI_ParseGameInfo("gameinfo.txt"); 5125 #endif 5126 5127 menuSet = UI_Cvar_VariableString("ui_menuFiles"); 5128 if (menuSet == NULL || menuSet[0] == '\0') { 5129 menuSet = "ui/menus.txt"; 5130 } 5131 5132 #if 0 5133 if (uiInfo.inGameLoad) { 5134 UI_LoadMenus("ui/ingame.txt", qtrue); 5135 } else { // bk010222: left this: UI_LoadMenus(menuSet, qtrue); 5136 } 5137 #else 5138 UI_LoadMenus(menuSet, qtrue); 5139 UI_LoadMenus("ui/ingame.txt", qfalse); 5140 #endif 5141 5142 Menus_CloseAll(); 5143 5144 trap_LAN_LoadCachedServers(); 5145 UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); 5146 5147 UI_BuildQ3Model_List(); 5148 UI_LoadBots(); 5149 5150 // sets defaults for ui temp cvars 5151 uiInfo.effectsColor = gamecodetoui[(int)trap_Cvar_VariableValue("color1")-1]; 5152 uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair"); 5153 trap_Cvar_Set("ui_mousePitch", (trap_Cvar_VariableValue("m_pitch") >= 0) ? "0" : "1"); 5154 5155 uiInfo.serverStatus.currentServerCinematic = -1; 5156 uiInfo.previewMovie = -1; 5157 5158 if (trap_Cvar_VariableValue("ui_TeamArenaFirstRun") == 0) { 5159 trap_Cvar_Set("s_volume", "0.8"); 5160 trap_Cvar_Set("s_musicvolume", "0.5"); 5161 trap_Cvar_Set("ui_TeamArenaFirstRun", "1"); 5162 } 5163 5164 trap_Cvar_Register(NULL, "debug_protocol", "", 0 ); 5165 5166 trap_Cvar_Set("ui_actualNetGameType", va("%d", ui_netGameType.integer)); 5167 } 5168 5169 5170 /* 5171 ================= 5172 UI_KeyEvent 5173 ================= 5174 */ 5175 void _UI_KeyEvent( int key, qboolean down ) { 5176 5177 if (Menu_Count() > 0) { 5178 menuDef_t *menu = Menu_GetFocused(); 5179 if (menu) { 5180 if (key == K_ESCAPE && down && !Menus_AnyFullScreenVisible()) { 5181 Menus_CloseAll(); 5182 } else { 5183 Menu_HandleKey(menu, key, down ); 5184 } 5185 } else { 5186 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 5187 trap_Key_ClearStates(); 5188 trap_Cvar_Set( "cl_paused", "0" ); 5189 } 5190 } 5191 5192 //if ((s > 0) && (s != menu_null_sound)) { 5193 // trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND ); 5194 //} 5195 } 5196 5197 /* 5198 ================= 5199 UI_MouseEvent 5200 ================= 5201 */ 5202 void _UI_MouseEvent( int dx, int dy ) 5203 { 5204 // update mouse screen position 5205 uiInfo.uiDC.cursorx += dx; 5206 if (uiInfo.uiDC.cursorx < 0) 5207 uiInfo.uiDC.cursorx = 0; 5208 else if (uiInfo.uiDC.cursorx > SCREEN_WIDTH) 5209 uiInfo.uiDC.cursorx = SCREEN_WIDTH; 5210 5211 uiInfo.uiDC.cursory += dy; 5212 if (uiInfo.uiDC.cursory < 0) 5213 uiInfo.uiDC.cursory = 0; 5214 else if (uiInfo.uiDC.cursory > SCREEN_HEIGHT) 5215 uiInfo.uiDC.cursory = SCREEN_HEIGHT; 5216 5217 if (Menu_Count() > 0) { 5218 //menuDef_t *menu = Menu_GetFocused(); 5219 //Menu_HandleMouseMove(menu, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory); 5220 Display_MouseMove(NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory); 5221 } 5222 5223 } 5224 5225 void UI_LoadNonIngame() { 5226 const char *menuSet = UI_Cvar_VariableString("ui_menuFiles"); 5227 if (menuSet == NULL || menuSet[0] == '\0') { 5228 menuSet = "ui/menus.txt"; 5229 } 5230 UI_LoadMenus(menuSet, qfalse); 5231 uiInfo.inGameLoad = qfalse; 5232 } 5233 5234 void _UI_SetActiveMenu( uiMenuCommand_t menu ) { 5235 char buf[256]; 5236 5237 // this should be the ONLY way the menu system is brought up 5238 // enusure minumum menu data is cached 5239 if (Menu_Count() > 0) { 5240 vec3_t v; 5241 v[0] = v[1] = v[2] = 0; 5242 switch ( menu ) { 5243 case UIMENU_NONE: 5244 trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI ); 5245 trap_Key_ClearStates(); 5246 trap_Cvar_Set( "cl_paused", "0" ); 5247 Menus_CloseAll(); 5248 5249 return; 5250 case UIMENU_MAIN: 5251 //trap_Cvar_Set( "sv_killserver", "1" ); 5252 trap_Key_SetCatcher( KEYCATCH_UI ); 5253 //trap_S_StartLocalSound( trap_S_RegisterSound("sound/misc/menu_background.wav", qfalse) , CHAN_LOCAL_SOUND ); 5254 //trap_S_StartBackgroundTrack("sound/misc/menu_background.wav", NULL); 5255 if (uiInfo.inGameLoad) { 5256 UI_LoadNonIngame(); 5257 } 5258 Menus_CloseAll(); 5259 Menus_ActivateByName("main"); 5260 trap_Cvar_VariableStringBuffer("com_errorMessage", buf, sizeof(buf)); 5261 if (strlen(buf)) { 5262 if (!ui_singlePlayerActive.integer) { 5263 Menus_ActivateByName("error_popmenu"); 5264 } else { 5265 trap_Cvar_Set("com_errorMessage", ""); 5266 } 5267 } 5268 return; 5269 case UIMENU_TEAM: 5270 trap_Key_SetCatcher( KEYCATCH_UI ); 5271 Menus_ActivateByName("team"); 5272 return; 5273 case UIMENU_NEED_CD: 5274 // no cd check in TA 5275 //trap_Key_SetCatcher( KEYCATCH_UI ); 5276 //Menus_ActivateByName("needcd"); 5277 //UI_ConfirmMenu( "Insert the CD", NULL, NeedCDAction ); 5278 return; 5279 case UIMENU_BAD_CD_KEY: 5280 // no cd check in TA 5281 //trap_Key_SetCatcher( KEYCATCH_UI ); 5282 //Menus_ActivateByName("badcd"); 5283 //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); 5284 return; 5285 case UIMENU_POSTGAME: 5286 //trap_Cvar_Set( "sv_killserver", "1" ); 5287 trap_Key_SetCatcher( KEYCATCH_UI ); 5288 if (uiInfo.inGameLoad) { 5289 UI_LoadNonIngame(); 5290 } 5291 Menus_CloseAll(); 5292 Menus_ActivateByName("endofgame"); 5293 //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); 5294 return; 5295 case UIMENU_INGAME: 5296 trap_Cvar_Set( "cl_paused", "1" ); 5297 trap_Key_SetCatcher( KEYCATCH_UI ); 5298 UI_BuildPlayerList(); 5299 Menus_CloseAll(); 5300 Menus_ActivateByName("ingame"); 5301 return; 5302 } 5303 } 5304 } 5305 5306 qboolean _UI_IsFullscreen( void ) { 5307 return Menus_AnyFullScreenVisible(); 5308 } 5309 5310 5311 5312 static connstate_t lastConnState; 5313 static char lastLoadingText[MAX_INFO_VALUE]; 5314 5315 static void UI_ReadableSize ( char *buf, int bufsize, int value ) 5316 { 5317 if (value > 1024*1024*1024 ) { // gigs 5318 Com_sprintf( buf, bufsize, "%d", value / (1024*1024*1024) ); 5319 Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d GB", 5320 (value % (1024*1024*1024))*100 / (1024*1024*1024) ); 5321 } else if (value > 1024*1024 ) { // megs 5322 Com_sprintf( buf, bufsize, "%d", value / (1024*1024) ); 5323 Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d MB", 5324 (value % (1024*1024))*100 / (1024*1024) ); 5325 } else if (value > 1024 ) { // kilos 5326 Com_sprintf( buf, bufsize, "%d KB", value / 1024 ); 5327 } else { // bytes 5328 Com_sprintf( buf, bufsize, "%d bytes", value ); 5329 } 5330 } 5331 5332 // Assumes time is in msec 5333 static void UI_PrintTime ( char *buf, int bufsize, int time ) { 5334 time /= 1000; // change to seconds 5335 5336 if (time > 3600) { // in the hours range 5337 Com_sprintf( buf, bufsize, "%d hr %d min", time / 3600, (time % 3600) / 60 ); 5338 } else if (time > 60) { // mins 5339 Com_sprintf( buf, bufsize, "%d min %d sec", time / 60, time % 60 ); 5340 } else { // secs 5341 Com_sprintf( buf, bufsize, "%d sec", time ); 5342 } 5343 } 5344 5345 void Text_PaintCenter(float x, float y, float scale, vec4_t color, const char *text, float adjust) { 5346 int len = Text_Width(text, scale, 0); 5347 Text_Paint(x - len / 2, y, scale, color, text, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE); 5348 } 5349 5350 void Text_PaintCenter_AutoWrapped(float x, float y, float xmax, float ystep, float scale, vec4_t color, const char *str, float adjust) { 5351 int width; 5352 char *s1,*s2,*s3; 5353 char c_bcp; 5354 char buf[1024]; 5355 5356 if (!str || str[0]=='\0') 5357 return; 5358 5359 Q_strncpyz(buf, str, sizeof(buf)); 5360 s1 = s2 = s3 = buf; 5361 5362 while (1) { 5363 do { 5364 s3++; 5365 } while (*s3!=' ' && *s3!='\0'); 5366 c_bcp = *s3; 5367 *s3 = '\0'; 5368 width = Text_Width(s1, scale, 0); 5369 *s3 = c_bcp; 5370 if (width > xmax) { 5371 if (s1==s2) 5372 { 5373 // fuck, don't have a clean cut, we'll overflow 5374 s2 = s3; 5375 } 5376 *s2 = '\0'; 5377 Text_PaintCenter(x, y, scale, color, s1, adjust); 5378 y += ystep; 5379 if (c_bcp == '\0') 5380 { 5381 // that was the last word 5382 // we could start a new loop, but that wouldn't be much use 5383 // even if the word is too long, we would overflow it (see above) 5384 // so just print it now if needed 5385 s2++; 5386 if (*s2 != '\0') // if we are printing an overflowing line we have s2 == s3 5387 Text_PaintCenter(x, y, scale, color, s2, adjust); 5388 break; 5389 } 5390 s2++; 5391 s1 = s2; 5392 s3 = s2; 5393 } 5394 else 5395 { 5396 s2 = s3; 5397 if (c_bcp == '\0') // we reached the end 5398 { 5399 Text_PaintCenter(x, y, scale, color, s1, adjust); 5400 break; 5401 } 5402 } 5403 } 5404 } 5405 5406 static void UI_DisplayDownloadInfo( const char *downloadName, float centerPoint, float yStart, float scale ) { 5407 static char dlText[] = "Downloading:"; 5408 static char etaText[] = "Estimated time left:"; 5409 static char xferText[] = "Transfer rate:"; 5410 5411 int downloadSize, downloadCount, downloadTime; 5412 char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64]; 5413 int xferRate; 5414 int leftWidth; 5415 const char *s; 5416 5417 downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" ); 5418 downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" ); 5419 downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" ); 5420 5421 leftWidth = 320; 5422 5423 UI_SetColor(colorWhite); 5424 Text_PaintCenter(centerPoint, yStart + 112, scale, colorWhite, dlText, 0); 5425 Text_PaintCenter(centerPoint, yStart + 192, scale, colorWhite, etaText, 0); 5426 Text_PaintCenter(centerPoint, yStart + 248, scale, colorWhite, xferText, 0); 5427 5428 if (downloadSize > 0) { 5429 s = va( "%s (%d%%)", downloadName, downloadCount * 100 / downloadSize ); 5430 } else { 5431 s = downloadName; 5432 } 5433 5434 Text_PaintCenter(centerPoint, yStart+136, scale, colorWhite, s, 0); 5435 5436 UI_ReadableSize( dlSizeBuf, sizeof dlSizeBuf, downloadCount ); 5437 UI_ReadableSize( totalSizeBuf, sizeof totalSizeBuf, downloadSize ); 5438 5439 if (downloadCount < 4096 || !downloadTime) { 5440 Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0); 5441 Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); 5442 } else { 5443 if ((uiInfo.uiDC.realTime - downloadTime) / 1000) { 5444 xferRate = downloadCount / ((uiInfo.uiDC.realTime - downloadTime) / 1000); 5445 } else { 5446 xferRate = 0; 5447 } 5448 UI_ReadableSize( xferRateBuf, sizeof xferRateBuf, xferRate ); 5449 5450 // Extrapolate estimated completion time 5451 if (downloadSize && xferRate) { 5452 int n = downloadSize / xferRate; // estimated time for entire d/l in secs 5453 5454 // We do it in K (/1024) because we'd overflow around 4MB 5455 UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf, 5456 (n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000); 5457 5458 Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, dlTimeBuf, 0); 5459 Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); 5460 } else { 5461 Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0); 5462 if (downloadSize) { 5463 Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0); 5464 } else { 5465 Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s copied)", dlSizeBuf), 0); 5466 } 5467 } 5468 5469 if (xferRate) { 5470 Text_PaintCenter(leftWidth, yStart+272, scale, colorWhite, va("%s/Sec", xferRateBuf), 0); 5471 } 5472 } 5473 } 5474 5475 /* 5476 ======================== 5477 UI_DrawConnectScreen 5478 5479 This will also be overlaid on the cgame info screen during loading 5480 to prevent it from blinking away too rapidly on local or lan games. 5481 ======================== 5482 */ 5483 void UI_DrawConnectScreen( qboolean overlay ) { 5484 char *s; 5485 uiClientState_t cstate; 5486 char info[MAX_INFO_VALUE]; 5487 char text[256]; 5488 float centerPoint, yStart, scale; 5489 5490 menuDef_t *menu = Menus_FindByName("Connect"); 5491 5492 5493 if ( !overlay && menu ) { 5494 Menu_Paint(menu, qtrue); 5495 } 5496 5497 if (!overlay) { 5498 centerPoint = 320; 5499 yStart = 130; 5500 scale = 0.5f; 5501 } else { 5502 centerPoint = 320; 5503 yStart = 32; 5504 scale = 0.6f; 5505 return; 5506 } 5507 5508 // see what information we should display 5509 trap_GetClientState( &cstate ); 5510 5511 info[0] = '\0'; 5512 if( trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ) ) { 5513 Text_PaintCenter(centerPoint, yStart, scale, colorWhite, va( "Loading %s", Info_ValueForKey( info, "mapname" )), 0); 5514 } 5515 5516 if (!Q_stricmp(cstate.servername,"localhost")) { 5517 Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite, va("Starting up..."), ITEM_TEXTSTYLE_SHADOWEDMORE); 5518 } else { 5519 strcpy(text, va("Connecting to %s", cstate.servername)); 5520 Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite,text , ITEM_TEXTSTYLE_SHADOWEDMORE); 5521 } 5522 5523 // display global MOTD at bottom 5524 Text_PaintCenter(centerPoint, 600, scale, colorWhite, Info_ValueForKey( cstate.updateInfoString, "motd" ), 0); 5525 // print any server info (server full, bad version, etc) 5526 if ( cstate.connState < CA_CONNECTED ) { 5527 Text_PaintCenter_AutoWrapped(centerPoint, yStart + 176, 630, 20, scale, colorWhite, cstate.messageString, 0); 5528 } 5529 5530 if ( lastConnState > cstate.connState ) { 5531 lastLoadingText[0] = '\0'; 5532 } 5533 lastConnState = cstate.connState; 5534 5535 switch ( cstate.connState ) { 5536 case CA_CONNECTING: 5537 s = va("Awaiting connection...%i", cstate.connectPacketCount); 5538 break; 5539 case CA_CHALLENGING: 5540 s = va("Awaiting challenge...%i", cstate.connectPacketCount); 5541 break; 5542 case CA_CONNECTED: { 5543 char downloadName[MAX_INFO_VALUE]; 5544 5545 trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof(downloadName) ); 5546 if (*downloadName) { 5547 UI_DisplayDownloadInfo( downloadName, centerPoint, yStart, scale ); 5548 return; 5549 } 5550 } 5551 s = "Awaiting gamestate..."; 5552 break; 5553 case CA_LOADING: 5554 return; 5555 case CA_PRIMED: 5556 return; 5557 default: 5558 return; 5559 } 5560 5561 5562 if (Q_stricmp(cstate.servername,"localhost")) { 5563 Text_PaintCenter(centerPoint, yStart + 80, scale, colorWhite, s, 0); 5564 } 5565 5566 // password required / connection rejected information goes here 5567 } 5568 5569 5570 /* 5571 ================ 5572 cvars 5573 ================ 5574 */ 5575 5576 typedef struct { 5577 vmCvar_t *vmCvar; 5578 char *cvarName; 5579 char *defaultString; 5580 int cvarFlags; 5581 } cvarTable_t; 5582 5583 vmCvar_t ui_ffa_fraglimit; 5584 vmCvar_t ui_ffa_timelimit; 5585 5586 vmCvar_t ui_tourney_fraglimit; 5587 vmCvar_t ui_tourney_timelimit; 5588 5589 vmCvar_t ui_team_fraglimit; 5590 vmCvar_t ui_team_timelimit; 5591 vmCvar_t ui_team_friendly; 5592 5593 vmCvar_t ui_ctf_capturelimit; 5594 vmCvar_t ui_ctf_timelimit; 5595 vmCvar_t ui_ctf_friendly; 5596 5597 vmCvar_t ui_arenasFile; 5598 vmCvar_t ui_botsFile; 5599 vmCvar_t ui_spScores1; 5600 vmCvar_t ui_spScores2; 5601 vmCvar_t ui_spScores3; 5602 vmCvar_t ui_spScores4; 5603 vmCvar_t ui_spScores5; 5604 vmCvar_t ui_spAwards; 5605 vmCvar_t ui_spVideos; 5606 vmCvar_t ui_spSkill; 5607 5608 vmCvar_t ui_spSelection; 5609 5610 vmCvar_t ui_browserMaster; 5611 vmCvar_t ui_browserGameType; 5612 vmCvar_t ui_browserSortKey; 5613 vmCvar_t ui_browserShowFull; 5614 vmCvar_t ui_browserShowEmpty; 5615 5616 vmCvar_t ui_brassTime; 5617 vmCvar_t ui_drawCrosshair; 5618 vmCvar_t ui_drawCrosshairNames; 5619 vmCvar_t ui_marks; 5620 5621 vmCvar_t ui_server1; 5622 vmCvar_t ui_server2; 5623 vmCvar_t ui_server3; 5624 vmCvar_t ui_server4; 5625 vmCvar_t ui_server5; 5626 vmCvar_t ui_server6; 5627 vmCvar_t ui_server7; 5628 vmCvar_t ui_server8; 5629 vmCvar_t ui_server9; 5630 vmCvar_t ui_server10; 5631 vmCvar_t ui_server11; 5632 vmCvar_t ui_server12; 5633 vmCvar_t ui_server13; 5634 vmCvar_t ui_server14; 5635 vmCvar_t ui_server15; 5636 vmCvar_t ui_server16; 5637 5638 vmCvar_t ui_cdkeychecked; 5639 5640 vmCvar_t ui_redteam; 5641 vmCvar_t ui_redteam1; 5642 vmCvar_t ui_redteam2; 5643 vmCvar_t ui_redteam3; 5644 vmCvar_t ui_redteam4; 5645 vmCvar_t ui_redteam5; 5646 vmCvar_t ui_blueteam; 5647 vmCvar_t ui_blueteam1; 5648 vmCvar_t ui_blueteam2; 5649 vmCvar_t ui_blueteam3; 5650 vmCvar_t ui_blueteam4; 5651 vmCvar_t ui_blueteam5; 5652 vmCvar_t ui_teamName; 5653 vmCvar_t ui_dedicated; 5654 vmCvar_t ui_gameType; 5655 vmCvar_t ui_netGameType; 5656 vmCvar_t ui_actualNetGameType; 5657 vmCvar_t ui_joinGameType; 5658 vmCvar_t ui_netSource; 5659 vmCvar_t ui_serverFilterType; 5660 vmCvar_t ui_opponentName; 5661 vmCvar_t ui_menuFiles; 5662 vmCvar_t ui_currentTier; 5663 vmCvar_t ui_currentMap; 5664 vmCvar_t ui_currentNetMap; 5665 vmCvar_t ui_mapIndex; 5666 vmCvar_t ui_currentOpponent; 5667 vmCvar_t ui_selectedPlayer; 5668 vmCvar_t ui_selectedPlayerName; 5669 vmCvar_t ui_lastServerRefresh_0; 5670 vmCvar_t ui_lastServerRefresh_1; 5671 vmCvar_t ui_lastServerRefresh_2; 5672 vmCvar_t ui_lastServerRefresh_3; 5673 vmCvar_t ui_singlePlayerActive; 5674 vmCvar_t ui_scoreAccuracy; 5675 vmCvar_t ui_scoreImpressives; 5676 vmCvar_t ui_scoreExcellents; 5677 vmCvar_t ui_scoreCaptures; 5678 vmCvar_t ui_scoreDefends; 5679 vmCvar_t ui_scoreAssists; 5680 vmCvar_t ui_scoreGauntlets; 5681 vmCvar_t ui_scoreScore; 5682 vmCvar_t ui_scorePerfect; 5683 vmCvar_t ui_scoreTeam; 5684 vmCvar_t ui_scoreBase; 5685 vmCvar_t ui_scoreTimeBonus; 5686 vmCvar_t ui_scoreSkillBonus; 5687 vmCvar_t ui_scoreShutoutBonus; 5688 vmCvar_t ui_scoreTime; 5689 vmCvar_t ui_captureLimit; 5690 vmCvar_t ui_fragLimit; 5691 vmCvar_t ui_smallFont; 5692 vmCvar_t ui_bigFont; 5693 vmCvar_t ui_findPlayer; 5694 vmCvar_t ui_Q3Model; 5695 vmCvar_t ui_hudFiles; 5696 vmCvar_t ui_recordSPDemo; 5697 vmCvar_t ui_realCaptureLimit; 5698 vmCvar_t ui_realWarmUp; 5699 vmCvar_t ui_serverStatusTimeOut; 5700 5701 5702 // bk001129 - made static to avoid aliasing 5703 static cvarTable_t cvarTable[] = { 5704 { &ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", CVAR_ARCHIVE }, 5705 { &ui_ffa_timelimit, "ui_ffa_timelimit", "0", CVAR_ARCHIVE }, 5706 5707 { &ui_tourney_fraglimit, "ui_tourney_fraglimit", "0", CVAR_ARCHIVE }, 5708 { &ui_tourney_timelimit, "ui_tourney_timelimit", "15", CVAR_ARCHIVE }, 5709 5710 { &ui_team_fraglimit, "ui_team_fraglimit", "0", CVAR_ARCHIVE }, 5711 { &ui_team_timelimit, "ui_team_timelimit", "20", CVAR_ARCHIVE }, 5712 { &ui_team_friendly, "ui_team_friendly", "1", CVAR_ARCHIVE }, 5713 5714 { &ui_ctf_capturelimit, "ui_ctf_capturelimit", "8", CVAR_ARCHIVE }, 5715 { &ui_ctf_timelimit, "ui_ctf_timelimit", "30", CVAR_ARCHIVE }, 5716 { &ui_ctf_friendly, "ui_ctf_friendly", "0", CVAR_ARCHIVE }, 5717 5718 { &ui_arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM }, 5719 { &ui_botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM }, 5720 { &ui_spScores1, "g_spScores1", "", CVAR_ARCHIVE | CVAR_ROM }, 5721 { &ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE | CVAR_ROM }, 5722 { &ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE | CVAR_ROM }, 5723 { &ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE | CVAR_ROM }, 5724 { &ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE | CVAR_ROM }, 5725 { &ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE | CVAR_ROM }, 5726 { &ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE | CVAR_ROM }, 5727 { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE }, 5728 5729 { &ui_spSelection, "ui_spSelection", "", CVAR_ROM }, 5730 5731 { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, 5732 { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, 5733 { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE }, 5734 { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE }, 5735 { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE }, 5736 5737 { &ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, 5738 { &ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, 5739 { &ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, 5740 { &ui_marks, "cg_marks", "1", CVAR_ARCHIVE }, 5741 5742 { &ui_server1, "server1", "", CVAR_ARCHIVE }, 5743 { &ui_server2, "server2", "", CVAR_ARCHIVE }, 5744 { &ui_server3, "server3", "", CVAR_ARCHIVE }, 5745 { &ui_server4, "server4", "", CVAR_ARCHIVE }, 5746 { &ui_server5, "server5", "", CVAR_ARCHIVE }, 5747 { &ui_server6, "server6", "", CVAR_ARCHIVE }, 5748 { &ui_server7, "server7", "", CVAR_ARCHIVE }, 5749 { &ui_server8, "server8", "", CVAR_ARCHIVE }, 5750 { &ui_server9, "server9", "", CVAR_ARCHIVE }, 5751 { &ui_server10, "server10", "", CVAR_ARCHIVE }, 5752 { &ui_server11, "server11", "", CVAR_ARCHIVE }, 5753 { &ui_server12, "server12", "", CVAR_ARCHIVE }, 5754 { &ui_server13, "server13", "", CVAR_ARCHIVE }, 5755 { &ui_server14, "server14", "", CVAR_ARCHIVE }, 5756 { &ui_server15, "server15", "", CVAR_ARCHIVE }, 5757 { &ui_server16, "server16", "", CVAR_ARCHIVE }, 5758 { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, 5759 { &ui_new, "ui_new", "0", CVAR_TEMP }, 5760 { &ui_debug, "ui_debug", "0", CVAR_TEMP }, 5761 { &ui_initialized, "ui_initialized", "0", CVAR_TEMP }, 5762 { &ui_teamName, "ui_teamName", "Pagans", CVAR_ARCHIVE }, 5763 { &ui_opponentName, "ui_opponentName", "Stroggs", CVAR_ARCHIVE }, 5764 { &ui_redteam, "ui_redteam", "Pagans", CVAR_ARCHIVE }, 5765 { &ui_blueteam, "ui_blueteam", "Stroggs", CVAR_ARCHIVE }, 5766 { &ui_dedicated, "ui_dedicated", "0", CVAR_ARCHIVE }, 5767 { &ui_gameType, "ui_gametype", "3", CVAR_ARCHIVE }, 5768 { &ui_joinGameType, "ui_joinGametype", "0", CVAR_ARCHIVE }, 5769 { &ui_netGameType, "ui_netGametype", "3", CVAR_ARCHIVE }, 5770 { &ui_actualNetGameType, "ui_actualNetGametype", "3", CVAR_ARCHIVE }, 5771 { &ui_redteam1, "ui_redteam1", "0", CVAR_ARCHIVE }, 5772 { &ui_redteam2, "ui_redteam2", "0", CVAR_ARCHIVE }, 5773 { &ui_redteam3, "ui_redteam3", "0", CVAR_ARCHIVE }, 5774 { &ui_redteam4, "ui_redteam4", "0", CVAR_ARCHIVE }, 5775 { &ui_redteam5, "ui_redteam5", "0", CVAR_ARCHIVE }, 5776 { &ui_blueteam1, "ui_blueteam1", "0", CVAR_ARCHIVE }, 5777 { &ui_blueteam2, "ui_blueteam2", "0", CVAR_ARCHIVE }, 5778 { &ui_blueteam3, "ui_blueteam3", "0", CVAR_ARCHIVE }, 5779 { &ui_blueteam4, "ui_blueteam4", "0", CVAR_ARCHIVE }, 5780 { &ui_blueteam5, "ui_blueteam5", "0", CVAR_ARCHIVE }, 5781 { &ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE }, 5782 { &ui_menuFiles, "ui_menuFiles", "ui/menus.txt", CVAR_ARCHIVE }, 5783 { &ui_currentTier, "ui_currentTier", "0", CVAR_ARCHIVE }, 5784 { &ui_currentMap, "ui_currentMap", "0", CVAR_ARCHIVE }, 5785 { &ui_currentNetMap, "ui_currentNetMap", "0", CVAR_ARCHIVE }, 5786 { &ui_mapIndex, "ui_mapIndex", "0", CVAR_ARCHIVE }, 5787 { &ui_currentOpponent, "ui_currentOpponent", "0", CVAR_ARCHIVE }, 5788 { &ui_selectedPlayer, "cg_selectedPlayer", "0", CVAR_ARCHIVE}, 5789 { &ui_selectedPlayerName, "cg_selectedPlayerName", "", CVAR_ARCHIVE}, 5790 { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0", "", CVAR_ARCHIVE}, 5791 { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1", "", CVAR_ARCHIVE}, 5792 { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2", "", CVAR_ARCHIVE}, 5793 { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3", "", CVAR_ARCHIVE}, 5794 { &ui_singlePlayerActive, "ui_singlePlayerActive", "0", 0}, 5795 { &ui_scoreAccuracy, "ui_scoreAccuracy", "0", CVAR_ARCHIVE}, 5796 { &ui_scoreImpressives, "ui_scoreImpressives", "0", CVAR_ARCHIVE}, 5797 { &ui_scoreExcellents, "ui_scoreExcellents", "0", CVAR_ARCHIVE}, 5798 { &ui_scoreCaptures, "ui_scoreCaptures", "0", CVAR_ARCHIVE}, 5799 { &ui_scoreDefends, "ui_scoreDefends", "0", CVAR_ARCHIVE}, 5800 { &ui_scoreAssists, "ui_scoreAssists", "0", CVAR_ARCHIVE}, 5801 { &ui_scoreGauntlets, "ui_scoreGauntlets", "0",CVAR_ARCHIVE}, 5802 { &ui_scoreScore, "ui_scoreScore", "0", CVAR_ARCHIVE}, 5803 { &ui_scorePerfect, "ui_scorePerfect", "0", CVAR_ARCHIVE}, 5804 { &ui_scoreTeam, "ui_scoreTeam", "0 to 0", CVAR_ARCHIVE}, 5805 { &ui_scoreBase, "ui_scoreBase", "0", CVAR_ARCHIVE}, 5806 { &ui_scoreTime, "ui_scoreTime", "00:00", CVAR_ARCHIVE}, 5807 { &ui_scoreTimeBonus, "ui_scoreTimeBonus", "0", CVAR_ARCHIVE}, 5808 { &ui_scoreSkillBonus, "ui_scoreSkillBonus", "0", CVAR_ARCHIVE}, 5809 { &ui_scoreShutoutBonus, "ui_scoreShutoutBonus", "0", CVAR_ARCHIVE}, 5810 { &ui_fragLimit, "ui_fragLimit", "10", 0}, 5811 { &ui_captureLimit, "ui_captureLimit", "5", 0}, 5812 { &ui_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, 5813 { &ui_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, 5814 { &ui_findPlayer, "ui_findPlayer", "Sarge", CVAR_ARCHIVE}, 5815 { &ui_Q3Model, "ui_q3model", "0", CVAR_ARCHIVE}, 5816 { &ui_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE}, 5817 { &ui_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE}, 5818 { &ui_teamArenaFirstRun, "ui_teamArenaFirstRun", "0", CVAR_ARCHIVE}, 5819 { &ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE}, 5820 { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, 5821 { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, 5822 5823 }; 5824 5825 // bk001129 - made static to avoid aliasing 5826 static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]); 5827 5828 5829 /* 5830 ================= 5831 UI_RegisterCvars 5832 ================= 5833 */ 5834 void UI_RegisterCvars( void ) { 5835 int i; 5836 cvarTable_t *cv; 5837 5838 for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { 5839 trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags ); 5840 } 5841 } 5842 5843 /* 5844 ================= 5845 UI_UpdateCvars 5846 ================= 5847 */ 5848 void UI_UpdateCvars( void ) { 5849 int i; 5850 cvarTable_t *cv; 5851 5852 for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { 5853 trap_Cvar_Update( cv->vmCvar ); 5854 } 5855 } 5856 5857 5858 /* 5859 ================= 5860 ArenaServers_StopRefresh 5861 ================= 5862 */ 5863 static void UI_StopServerRefresh( void ) 5864 { 5865 int count; 5866 5867 if (!uiInfo.serverStatus.refreshActive) { 5868 // not currently refreshing 5869 return; 5870 } 5871 uiInfo.serverStatus.refreshActive = qfalse; 5872 Com_Printf("%d servers listed in browser with %d players.\n", 5873 uiInfo.serverStatus.numDisplayServers, 5874 uiInfo.serverStatus.numPlayersOnServers); 5875 count = trap_LAN_GetServerCount(ui_netSource.integer); 5876 if (count - uiInfo.serverStatus.numDisplayServers > 0) { 5877 Com_Printf("%d servers not listed due to packet loss or pings higher than %d\n", 5878 count - uiInfo.serverStatus.numDisplayServers, 5879 (int) trap_Cvar_VariableValue("cl_maxPing")); 5880 } 5881 5882 } 5883 5884 /* 5885 ================= 5886 ArenaServers_MaxPing 5887 ================= 5888 */ 5889 #ifndef MISSIONPACK // bk001206 5890 static int ArenaServers_MaxPing( void ) { 5891 int maxPing; 5892 5893 maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" ); 5894 if( maxPing < 100 ) { 5895 maxPing = 100; 5896 } 5897 return maxPing; 5898 } 5899 #endif 5900 5901 /* 5902 ================= 5903 UI_DoServerRefresh 5904 ================= 5905 */ 5906 static void UI_DoServerRefresh( void ) 5907 { 5908 qboolean wait = qfalse; 5909 5910 if (!uiInfo.serverStatus.refreshActive) { 5911 return; 5912 } 5913 if (ui_netSource.integer != AS_FAVORITES) { 5914 if (ui_netSource.integer == AS_LOCAL) { 5915 if (!trap_LAN_GetServerCount(ui_netSource.integer)) { 5916 wait = qtrue; 5917 } 5918 } else { 5919 if (trap_LAN_GetServerCount(ui_netSource.integer) < 0) { 5920 wait = qtrue; 5921 } 5922 } 5923 } 5924 5925 if (uiInfo.uiDC.realTime < uiInfo.serverStatus.refreshtime) { 5926 if (wait) { 5927 return; 5928 } 5929 } 5930 5931 // if still trying to retrieve pings 5932 if (trap_LAN_UpdateVisiblePings(ui_netSource.integer)) { 5933 uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; 5934 } else if (!wait) { 5935 // get the last servers in the list 5936 UI_BuildServerDisplayList(2); 5937 // stop the refresh 5938 UI_StopServerRefresh(); 5939 } 5940 // 5941 UI_BuildServerDisplayList(qfalse); 5942 } 5943 5944 /* 5945 ================= 5946 UI_StartServerRefresh 5947 ================= 5948 */ 5949 static void UI_StartServerRefresh(qboolean full) 5950 { 5951 int i; 5952 char *ptr; 5953 5954 qtime_t q; 5955 trap_RealTime(&q); 5956 trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); 5957 5958 if (!full) { 5959 UI_UpdatePendingPings(); 5960 return; 5961 } 5962 5963 uiInfo.serverStatus.refreshActive = qtrue; 5964 uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000; 5965 // clear number of displayed servers 5966 uiInfo.serverStatus.numDisplayServers = 0; 5967 uiInfo.serverStatus.numPlayersOnServers = 0; 5968 // mark all servers as visible so we store ping updates for them 5969 trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue); 5970 // reset all the pings 5971 trap_LAN_ResetPings(ui_netSource.integer); 5972 // 5973 if( ui_netSource.integer == AS_LOCAL ) { 5974 trap_Cmd_ExecuteText( EXEC_NOW, "localservers\n" ); 5975 uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; 5976 return; 5977 } 5978 5979 uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000; 5980 if( ui_netSource.integer == AS_GLOBAL || ui_netSource.integer == AS_MPLAYER ) { 5981 if( ui_netSource.integer == AS_GLOBAL ) { 5982 i = 0; 5983 } 5984 else { 5985 i = 1; 5986 } 5987 5988 ptr = UI_Cvar_VariableString("debug_protocol"); 5989 if (strlen(ptr)) { 5990 trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", i, ptr)); 5991 } 5992 else { 5993 trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", i, (int)trap_Cvar_VariableValue( "protocol" ) ) ); 5994 } 5995 } 5996 } 5997