Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ui_main.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 /*
00024 =======================================================================
00025 
00026 USER INTERFACE MAIN
00027 
00028 =======================================================================
00029 */
00030 
00031 // use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build
00032 //#define PRE_RELEASE_TADEMO
00033 
00034 #include "ui_local.h"
00035 
00036 uiInfo_t uiInfo;
00037 
00038 static const char *MonthAbbrev[] = {
00039     "Jan","Feb","Mar",
00040     "Apr","May","Jun",
00041     "Jul","Aug","Sep",
00042     "Oct","Nov","Dec"
00043 };
00044 
00045 
00046 static const char *skillLevels[] = {
00047   "I Can Win",
00048   "Bring It On",
00049   "Hurt Me Plenty",
00050   "Hardcore",
00051   "Nightmare"
00052 };
00053 
00054 static const int numSkillLevels = sizeof(skillLevels) / sizeof(const char*);
00055 
00056 
00057 static const char *netSources[] = {
00058     "Local",
00059     "Mplayer",
00060     "Internet",
00061     "Favorites"
00062 };
00063 static const int numNetSources = sizeof(netSources) / sizeof(const char*);
00064 
00065 static const serverFilter_t serverFilters[] = {
00066     {"All", "" },
00067     {"Quake 3 Arena", "" },
00068     {"Team Arena", "missionpack" },
00069     {"Rocket Arena", "arena" },
00070     {"Alliance", "alliance20" },
00071     {"Weapons Factory Arena", "wfa" },
00072     {"OSP", "osp" },
00073 };
00074 
00075 static const char *teamArenaGameTypes[] = {
00076     "FFA",
00077     "TOURNAMENT",
00078     "SP",
00079     "TEAM DM",
00080     "CTF",
00081     "1FCTF",
00082     "OVERLOAD",
00083     "HARVESTER",
00084     "TEAMTOURNAMENT"
00085 };
00086 
00087 static int const numTeamArenaGameTypes = sizeof(teamArenaGameTypes) / sizeof(const char*);
00088 
00089 
00090 static const char *teamArenaGameNames[] = {
00091     "Free For All",
00092     "Tournament",
00093     "Single Player",
00094     "Team Deathmatch",
00095     "Capture the Flag",
00096     "One Flag CTF",
00097     "Overload",
00098     "Harvester",
00099     "Team Tournament",
00100 };
00101 
00102 static int const numTeamArenaGameNames = sizeof(teamArenaGameNames) / sizeof(const char*);
00103 
00104 
00105 static const int numServerFilters = sizeof(serverFilters) / sizeof(serverFilter_t);
00106 
00107 static const char *sortKeys[] = {
00108     "Server Name",
00109     "Map Name",
00110     "Open Player Spots",
00111     "Game Type",
00112     "Ping Time"
00113 };
00114 static const int numSortKeys = sizeof(sortKeys) / sizeof(const char*);
00115 
00116 static char* netnames[] = {
00117     "???",
00118     "UDP",
00119     "IPX",
00120     NULL
00121 };
00122 
00123 #ifndef MISSIONPACK // bk001206
00124 static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
00125 #endif
00126 
00127 static int gamecodetoui[] = {4,2,3,0,5,1,6};
00128 static int uitogamecode[] = {4,6,2,3,1,5,7};
00129 
00130 
00131 static void UI_StartServerRefresh(qboolean full);
00132 static void UI_StopServerRefresh( void );
00133 static void UI_DoServerRefresh( void );
00134 static void UI_FeederSelection(float feederID, int index);
00135 static void UI_BuildServerDisplayList(qboolean force);
00136 static void UI_BuildServerStatus(qboolean force);
00137 static void UI_BuildFindPlayerList(qboolean force);
00138 static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 );
00139 static int UI_MapCountByGameType(qboolean singlePlayer);
00140 static int UI_HeadCountByTeam( void );
00141 static void UI_ParseGameInfo(const char *teamFile);
00142 static void UI_ParseTeamInfo(const char *teamFile);
00143 static const char *UI_SelectedMap(int index, int *actual);
00144 static const char *UI_SelectedHead(int index, int *actual);
00145 static int UI_GetIndexFromSelection(int actual);
00146 
00147 int ProcessNewUI( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 );
00148 
00149 /*
00150 ================
00151 vmMain
00152 
00153 This is the only way control passes into the module.
00154 This must be the very first function compiled into the .qvm file
00155 ================
00156 */
00157 vmCvar_t  ui_new;
00158 vmCvar_t  ui_debug;
00159 vmCvar_t  ui_initialized;
00160 vmCvar_t  ui_teamArenaFirstRun;
00161 
00162 void _UI_Init( qboolean );
00163 void _UI_Shutdown( void );
00164 void _UI_KeyEvent( int key, qboolean down );
00165 void _UI_MouseEvent( int dx, int dy );
00166 void _UI_Refresh( int realtime );
00167 qboolean _UI_IsFullscreen( void );
00168 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  ) {
00169   switch ( command ) {
00170       case UI_GETAPIVERSION:
00171           return UI_API_VERSION;
00172 
00173       case UI_INIT:
00174           _UI_Init(arg0);
00175           return 0;
00176 
00177       case UI_SHUTDOWN:
00178           _UI_Shutdown();
00179           return 0;
00180 
00181       case UI_KEY_EVENT:
00182           _UI_KeyEvent( arg0, arg1 );
00183           return 0;
00184 
00185       case UI_MOUSE_EVENT:
00186           _UI_MouseEvent( arg0, arg1 );
00187           return 0;
00188 
00189       case UI_REFRESH:
00190           _UI_Refresh( arg0 );
00191           return 0;
00192 
00193       case UI_IS_FULLSCREEN:
00194           return _UI_IsFullscreen();
00195 
00196       case UI_SET_ACTIVE_MENU:
00197           _UI_SetActiveMenu( arg0 );
00198           return 0;
00199 
00200       case UI_CONSOLE_COMMAND:
00201           return UI_ConsoleCommand(arg0);
00202 
00203       case UI_DRAW_CONNECT_SCREEN:
00204           UI_DrawConnectScreen( arg0 );
00205           return 0;
00206       case UI_HASUNIQUECDKEY: // mod authors need to observe this
00207         return qtrue; // bk010117 - change this to qfalse for mods!
00208 
00209     }
00210 
00211     return -1;
00212 }
00213 
00214 
00215 
00216 void AssetCache() {
00217     int n;
00218     //if (Assets.textFont == NULL) {
00219     //}
00220     //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND );
00221     //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus));
00222     uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR );
00223     uiInfo.uiDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE );
00224     uiInfo.uiDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED );
00225     uiInfo.uiDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW );
00226     uiInfo.uiDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN );
00227     uiInfo.uiDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL );
00228     uiInfo.uiDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE );
00229     uiInfo.uiDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN );
00230     uiInfo.uiDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE );
00231     uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR );
00232     uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN );
00233     uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP );
00234     uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT );
00235     uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT );
00236     uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB );
00237     uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR );
00238     uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB );
00239 
00240     for( n = 0; n < NUM_CROSSHAIRS; n++ ) {
00241         uiInfo.uiDC.Assets.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) );
00242     }
00243 
00244     uiInfo.newHighScoreSound = trap_S_RegisterSound("sound/feedback/voc_newhighscore.wav", qfalse);
00245 }
00246 
00247 void _UI_DrawSides(float x, float y, float w, float h, float size) {
00248     UI_AdjustFrom640( &x, &y, &w, &h );
00249     size *= uiInfo.uiDC.xscale;
00250     trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
00251     trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
00252 }
00253 
00254 void _UI_DrawTopBottom(float x, float y, float w, float h, float size) {
00255     UI_AdjustFrom640( &x, &y, &w, &h );
00256     size *= uiInfo.uiDC.yscale;
00257     trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
00258     trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
00259 }
00260 /*
00261 ================
00262 UI_DrawRect
00263 
00264 Coordinates are 640*480 virtual values
00265 =================
00266 */
00267 void _UI_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
00268     trap_R_SetColor( color );
00269 
00270   _UI_DrawTopBottom(x, y, width, height, size);
00271   _UI_DrawSides(x, y, width, height, size);
00272 
00273     trap_R_SetColor( NULL );
00274 }
00275 
00276 int Text_Width(const char *text, float scale, int limit) {
00277   int count,len;
00278     float out;
00279     glyphInfo_t *glyph;
00280     float useScale;
00281     const char *s = text;
00282     fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
00283     if (scale <= ui_smallFont.value) {
00284         font = &uiInfo.uiDC.Assets.smallFont;
00285     } else if (scale >= ui_bigFont.value) {
00286         font = &uiInfo.uiDC.Assets.bigFont;
00287     }
00288     useScale = scale * font->glyphScale;
00289   out = 0;
00290   if (text) {
00291     len = strlen(text);
00292         if (limit > 0 && len > limit) {
00293             len = limit;
00294         }
00295         count = 0;
00296         while (s && *s && count < len) {
00297             if ( Q_IsColorString(s) ) {
00298                 s += 2;
00299                 continue;
00300             } else {
00301                 glyph = &font->glyphs[(int)*s];
00302                 out += glyph->xSkip;
00303                 s++;
00304                 count++;
00305             }
00306     }
00307   }
00308   return out * useScale;
00309 }
00310 
00311 int Text_Height(const char *text, float scale, int limit) {
00312   int len, count;
00313     float max;
00314     glyphInfo_t *glyph;
00315     float useScale;
00316     const char *s = text; // bk001206 - unsigned
00317     fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
00318     if (scale <= ui_smallFont.value) {
00319         font = &uiInfo.uiDC.Assets.smallFont;
00320     } else if (scale >= ui_bigFont.value) {
00321         font = &uiInfo.uiDC.Assets.bigFont;
00322     }
00323     useScale = scale * font->glyphScale;
00324   max = 0;
00325   if (text) {
00326     len = strlen(text);
00327         if (limit > 0 && len > limit) {
00328             len = limit;
00329         }
00330         count = 0;
00331         while (s && *s && count < len) {
00332             if ( Q_IsColorString(s) ) {
00333                 s += 2;
00334                 continue;
00335             } else {
00336                 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00337           if (max < glyph->height) {
00338               max = glyph->height;
00339               }
00340                 s++;
00341                 count++;
00342             }
00343     }
00344   }
00345   return max * useScale;
00346 }
00347 
00348 void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
00349   float w, h;
00350   w = width * scale;
00351   h = height * scale;
00352   UI_AdjustFrom640( &x, &y, &w, &h );
00353   trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
00354 }
00355 
00356 void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
00357   int len, count;
00358     vec4_t newColor;
00359     glyphInfo_t *glyph;
00360     float useScale;
00361     fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
00362     if (scale <= ui_smallFont.value) {
00363         font = &uiInfo.uiDC.Assets.smallFont;
00364     } else if (scale >= ui_bigFont.value) {
00365         font = &uiInfo.uiDC.Assets.bigFont;
00366     }
00367     useScale = scale * font->glyphScale;
00368   if (text) {
00369     const char *s = text; // bk001206 - unsigned
00370         trap_R_SetColor( color );
00371         memcpy(&newColor[0], &color[0], sizeof(vec4_t));
00372     len = strlen(text);
00373         if (limit > 0 && len > limit) {
00374             len = limit;
00375         }
00376         count = 0;
00377         while (s && *s && count < len) {
00378             glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00379       //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
00380       //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
00381             if ( Q_IsColorString( s ) ) {
00382                 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
00383                 newColor[3] = color[3];
00384                 trap_R_SetColor( newColor );
00385                 s += 2;
00386                 continue;
00387             } else {
00388                 float yadj = useScale * glyph->top;
00389                 if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
00390                     int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
00391                     colorBlack[3] = newColor[3];
00392                     trap_R_SetColor( colorBlack );
00393                     Text_PaintChar(x + ofs, y - yadj + ofs, 
00394                                                         glyph->imageWidth,
00395                                                         glyph->imageHeight,
00396                                                         useScale, 
00397                                                         glyph->s,
00398                                                         glyph->t,
00399                                                         glyph->s2,
00400                                                         glyph->t2,
00401                                                         glyph->glyph);
00402                     trap_R_SetColor( newColor );
00403                     colorBlack[3] = 1.0;
00404                 }
00405                 Text_PaintChar(x, y - yadj, 
00406                                                     glyph->imageWidth,
00407                                                     glyph->imageHeight,
00408                                                     useScale, 
00409                                                     glyph->s,
00410                                                     glyph->t,
00411                                                     glyph->s2,
00412                                                     glyph->t2,
00413                                                     glyph->glyph);
00414 
00415                 x += (glyph->xSkip * useScale) + adjust;
00416                 s++;
00417                 count++;
00418             }
00419     }
00420       trap_R_SetColor( NULL );
00421   }
00422 }
00423 
00424 void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) {
00425   int len, count;
00426     vec4_t newColor;
00427     glyphInfo_t *glyph, *glyph2;
00428     float yadj;
00429     float useScale;
00430     fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
00431     if (scale <= ui_smallFont.value) {
00432         font = &uiInfo.uiDC.Assets.smallFont;
00433     } else if (scale >= ui_bigFont.value) {
00434         font = &uiInfo.uiDC.Assets.bigFont;
00435     }
00436     useScale = scale * font->glyphScale;
00437   if (text) {
00438     const char *s = text; // bk001206 - unsigned
00439         trap_R_SetColor( color );
00440         memcpy(&newColor[0], &color[0], sizeof(vec4_t));
00441     len = strlen(text);
00442         if (limit > 0 && len > limit) {
00443             len = limit;
00444         }
00445         count = 0;
00446         glyph2 = &font->glyphs[ (int) cursor]; // bk001206 - possible signed char
00447         while (s && *s && count < len) {
00448             glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00449       //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
00450       //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
00451             if ( Q_IsColorString( s ) ) {
00452                 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
00453                 newColor[3] = color[3];
00454                 trap_R_SetColor( newColor );
00455                 s += 2;
00456                 continue;
00457             } else {
00458                 yadj = useScale * glyph->top;
00459                 if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
00460                     int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
00461                     colorBlack[3] = newColor[3];
00462                     trap_R_SetColor( colorBlack );
00463                     Text_PaintChar(x + ofs, y - yadj + ofs, 
00464                                                         glyph->imageWidth,
00465                                                         glyph->imageHeight,
00466                                                         useScale, 
00467                                                         glyph->s,
00468                                                         glyph->t,
00469                                                         glyph->s2,
00470                                                         glyph->t2,
00471                                                         glyph->glyph);
00472                     colorBlack[3] = 1.0;
00473                     trap_R_SetColor( newColor );
00474                 }
00475                 Text_PaintChar(x, y - yadj, 
00476                                                     glyph->imageWidth,
00477                                                     glyph->imageHeight,
00478                                                     useScale, 
00479                                                     glyph->s,
00480                                                     glyph->t,
00481                                                     glyph->s2,
00482                                                     glyph->t2,
00483                                                     glyph->glyph);
00484 
00485           yadj = useScale * glyph2->top;
00486             if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) {
00487                     Text_PaintChar(x, y - yadj, 
00488                                                         glyph2->imageWidth,
00489                                                         glyph2->imageHeight,
00490                                                         useScale, 
00491                                                         glyph2->s,
00492                                                         glyph2->t,
00493                                                         glyph2->s2,
00494                                                         glyph2->t2,
00495                                                         glyph2->glyph);
00496                 }
00497 
00498                 x += (glyph->xSkip * useScale);
00499                 s++;
00500                 count++;
00501             }
00502     }
00503     // need to paint cursor at end of text
00504     if (cursorPos == len && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) {
00505         yadj = useScale * glyph2->top;
00506         Text_PaintChar(x, y - yadj, 
00507                           glyph2->imageWidth,
00508                           glyph2->imageHeight,
00509                           useScale, 
00510                           glyph2->s,
00511                           glyph2->t,
00512                           glyph2->s2,
00513                           glyph2->t2,
00514                           glyph2->glyph);
00515 
00516     }
00517 
00518       trap_R_SetColor( NULL );
00519   }
00520 }
00521 
00522 
00523 static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) {
00524   int len, count;
00525     vec4_t newColor;
00526     glyphInfo_t *glyph;
00527   if (text) {
00528     const char *s = text; // bk001206 - unsigned
00529         float max = *maxX;
00530         float useScale;
00531         fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
00532         if (scale <= ui_smallFont.value) {
00533             font = &uiInfo.uiDC.Assets.smallFont;
00534         } else if (scale > ui_bigFont.value) {
00535             font = &uiInfo.uiDC.Assets.bigFont;
00536         }
00537         useScale = scale * font->glyphScale;
00538         trap_R_SetColor( color );
00539     len = strlen(text);                  
00540         if (limit > 0 && len > limit) {
00541             len = limit;
00542         }
00543         count = 0;
00544         while (s && *s && count < len) {
00545             glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00546             if ( Q_IsColorString( s ) ) {
00547                 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
00548                 newColor[3] = color[3];
00549                 trap_R_SetColor( newColor );
00550                 s += 2;
00551                 continue;
00552             } else {
00553           float yadj = useScale * glyph->top;
00554                 if (Text_Width(s, useScale, 1) + x > max) {
00555                     *maxX = 0;
00556                     break;
00557                 }
00558             Text_PaintChar(x, y - yadj, 
00559                              glyph->imageWidth,
00560                                glyph->imageHeight,
00561                                useScale, 
00562                                    glyph->s,
00563                                        glyph->t,
00564                                        glyph->s2,
00565                                          glyph->t2,
00566                                            glyph->glyph);
00567           x += (glyph->xSkip * useScale) + adjust;
00568                 *maxX = x;
00569                 count++;
00570                 s++;
00571         }
00572         }
00573       trap_R_SetColor( NULL );
00574   }
00575 
00576 }
00577 
00578 
00579 void UI_ShowPostGame(qboolean newHigh) {
00580     trap_Cvar_Set ("cg_cameraOrbit", "0");
00581     trap_Cvar_Set("cg_thirdPerson", "0");
00582     trap_Cvar_Set( "sv_killserver", "1" );
00583     uiInfo.soundHighScore = newHigh;
00584   _UI_SetActiveMenu(UIMENU_POSTGAME);
00585 }
00586 /*
00587 =================
00588 _UI_Refresh
00589 =================
00590 */
00591 
00592 void UI_DrawCenteredPic(qhandle_t image, int w, int h) {
00593   int x, y;
00594   x = (SCREEN_WIDTH - w) / 2;
00595   y = (SCREEN_HEIGHT - h) / 2;
00596   UI_DrawHandlePic(x, y, w, h, image);
00597 }
00598 
00599 int frameCount = 0;
00600 int startTime;
00601 
00602 #define UI_FPS_FRAMES   4
00603 void _UI_Refresh( int realtime )
00604 {
00605     static int index;
00606     static int  previousTimes[UI_FPS_FRAMES];
00607 
00608     //if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
00609     //  return;
00610     //}
00611 
00612     uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime;
00613     uiInfo.uiDC.realTime = realtime;
00614 
00615     previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime;
00616     index++;
00617     if ( index > UI_FPS_FRAMES ) {
00618         int i, total;
00619         // average multiple frames together to smooth changes out a bit
00620         total = 0;
00621         for ( i = 0 ; i < UI_FPS_FRAMES ; i++ ) {
00622             total += previousTimes[i];
00623         }
00624         if ( !total ) {
00625             total = 1;
00626         }
00627         uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total;
00628     }
00629 
00630 
00631 
00632     UI_UpdateCvars();
00633 
00634     if (Menu_Count() > 0) {
00635         // paint all the menus
00636         Menu_PaintAll();
00637         // refresh server browser list
00638         UI_DoServerRefresh();
00639         // refresh server status
00640         UI_BuildServerStatus(qfalse);
00641         // refresh find player list
00642         UI_BuildFindPlayerList(qfalse);
00643     } 
00644     
00645     // draw cursor
00646     UI_SetColor( NULL );
00647     if (Menu_Count() > 0) {
00648         UI_DrawHandlePic( uiInfo.uiDC.cursorx-16, uiInfo.uiDC.cursory-16, 32, 32, uiInfo.uiDC.Assets.cursor);
00649     }
00650 
00651 #ifndef NDEBUG
00652     if (uiInfo.uiDC.debug)
00653     {
00654         // cursor coordinates
00655         //FIXME
00656         //UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
00657     }
00658 #endif
00659 
00660 }
00661 
00662 /*
00663 =================
00664 _UI_Shutdown
00665 =================
00666 */
00667 void _UI_Shutdown( void ) {
00668     trap_LAN_SaveCachedServers();
00669 }
00670 
00671 char *defaultMenu = NULL;
00672 
00673 char *GetMenuBuffer(const char *filename) {
00674     int len;
00675     fileHandle_t    f;
00676     static char buf[MAX_MENUFILE];
00677 
00678     len = trap_FS_FOpenFile( filename, &f, FS_READ );
00679     if ( !f ) {
00680         trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) );
00681         return defaultMenu;
00682     }
00683     if ( len >= MAX_MENUFILE ) {
00684         trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE ) );
00685         trap_FS_FCloseFile( f );
00686         return defaultMenu;
00687     }
00688 
00689     trap_FS_Read( buf, len, f );
00690     buf[len] = 0;
00691     trap_FS_FCloseFile( f );
00692     //COM_Compress(buf);
00693   return buf;
00694 
00695 }
00696 
00697 qboolean Asset_Parse(int handle) {
00698     pc_token_t token;
00699     const char *tempStr;
00700 
00701     if (!trap_PC_ReadToken(handle, &token))
00702         return qfalse;
00703     if (Q_stricmp(token.string, "{") != 0) {
00704         return qfalse;
00705     }
00706     
00707     while ( 1 ) {
00708 
00709         memset(&token, 0, sizeof(pc_token_t));
00710 
00711         if (!trap_PC_ReadToken(handle, &token))
00712             return qfalse;
00713 
00714         if (Q_stricmp(token.string, "}") == 0) {
00715             return qtrue;
00716         }
00717 
00718         // font
00719         if (Q_stricmp(token.string, "font") == 0) {
00720             int pointSize;
00721             if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
00722                 return qfalse;
00723             }
00724             trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.textFont);
00725             uiInfo.uiDC.Assets.fontRegistered = qtrue;
00726             continue;
00727         }
00728 
00729         if (Q_stricmp(token.string, "smallFont") == 0) {
00730             int pointSize;
00731             if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
00732                 return qfalse;
00733             }
00734             trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont);
00735             continue;
00736         }
00737 
00738         if (Q_stricmp(token.string, "bigFont") == 0) {
00739             int pointSize;
00740             if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
00741                 return qfalse;
00742             }
00743             trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont);
00744             continue;
00745         }
00746 
00747 
00748         // gradientbar
00749         if (Q_stricmp(token.string, "gradientbar") == 0) {
00750             if (!PC_String_Parse(handle, &tempStr)) {
00751                 return qfalse;
00752             }
00753             uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr);
00754             continue;
00755         }
00756 
00757         // enterMenuSound
00758         if (Q_stricmp(token.string, "menuEnterSound") == 0) {
00759             if (!PC_String_Parse(handle, &tempStr)) {
00760                 return qfalse;
00761             }
00762             uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse );
00763             continue;
00764         }
00765 
00766         // exitMenuSound
00767         if (Q_stricmp(token.string, "menuExitSound") == 0) {
00768             if (!PC_String_Parse(handle, &tempStr)) {
00769                 return qfalse;
00770             }
00771             uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse );
00772             continue;
00773         }
00774 
00775         // itemFocusSound
00776         if (Q_stricmp(token.string, "itemFocusSound") == 0) {
00777             if (!PC_String_Parse(handle, &tempStr)) {
00778                 return qfalse;
00779             }
00780             uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse );
00781             continue;
00782         }
00783 
00784         // menuBuzzSound
00785         if (Q_stricmp(token.string, "menuBuzzSound") == 0) {
00786             if (!PC_String_Parse(handle, &tempStr)) {
00787                 return qfalse;
00788             }
00789             uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse );
00790             continue;
00791         }
00792 
00793         if (Q_stricmp(token.string, "cursor") == 0) {
00794             if (!PC_String_Parse(handle, &uiInfo.uiDC.Assets.cursorStr)) {
00795                 return qfalse;
00796             }
00797             uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( uiInfo.uiDC.Assets.cursorStr);
00798             continue;
00799         }
00800 
00801         if (Q_stricmp(token.string, "fadeClamp") == 0) {
00802             if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeClamp)) {
00803                 return qfalse;
00804             }
00805             continue;
00806         }
00807 
00808         if (Q_stricmp(token.string, "fadeCycle") == 0) {
00809             if (!PC_Int_Parse(handle, &uiInfo.uiDC.Assets.fadeCycle)) {
00810                 return qfalse;
00811             }
00812             continue;
00813         }
00814 
00815         if (Q_stricmp(token.string, "fadeAmount") == 0) {
00816             if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeAmount)) {
00817                 return qfalse;
00818             }
00819             continue;
00820         }
00821 
00822         if (Q_stricmp(token.string, "shadowX") == 0) {
00823             if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowX)) {
00824                 return qfalse;
00825             }
00826             continue;
00827         }
00828 
00829         if (Q_stricmp(token.string, "shadowY") == 0) {
00830             if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowY)) {
00831                 return qfalse;
00832             }
00833             continue;
00834         }
00835 
00836         if (Q_stricmp(token.string, "shadowColor") == 0) {
00837             if (!PC_Color_Parse(handle, &uiInfo.uiDC.Assets.shadowColor)) {
00838                 return qfalse;
00839             }
00840             uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3];
00841             continue;
00842         }
00843 
00844     }
00845     return qfalse;
00846 }
00847 
00848 void Font_Report() {
00849   int i;
00850   Com_Printf("Font Info\n");
00851   Com_Printf("=========\n");
00852   for ( i = 32; i < 96; i++) {
00853     Com_Printf("Glyph handle %i: %i\n", i, uiInfo.uiDC.Assets.textFont.glyphs[i].glyph);
00854   }
00855 }
00856 
00857 void UI_Report() {
00858   String_Report();
00859   //Font_Report();
00860 
00861 }
00862 
00863 void UI_ParseMenu(const char *menuFile) {
00864     int handle;
00865     pc_token_t token;
00866 
00867     Com_Printf("Parsing menu file:%s\n", menuFile);
00868 
00869     handle = trap_PC_LoadSource(menuFile);
00870     if (!handle) {
00871         return;
00872     }
00873 
00874     while ( 1 ) {
00875         memset(&token, 0, sizeof(pc_token_t));
00876         if (!trap_PC_ReadToken( handle, &token )) {
00877             break;
00878         }
00879 
00880         //if ( Q_stricmp( token, "{" ) ) {
00881         //  Com_Printf( "Missing { in menu file\n" );
00882         //  break;
00883         //}
00884 
00885         //if ( menuCount == MAX_MENUS ) {
00886         //  Com_Printf( "Too many menus!\n" );
00887         //  break;
00888         //}
00889 
00890         if ( token.string[0] == '}' ) {
00891             break;
00892         }
00893 
00894         if (Q_stricmp(token.string, "assetGlobalDef") == 0) {
00895             if (Asset_Parse(handle)) {
00896                 continue;
00897             } else {
00898                 break;
00899             }
00900         }
00901 
00902         if (Q_stricmp(token.string, "menudef") == 0) {
00903             // start a new menu
00904             Menu_New(handle);
00905         }
00906     }
00907     trap_PC_FreeSource(handle);
00908 }
00909 
00910 qboolean Load_Menu(int handle) {
00911     pc_token_t token;
00912 
00913     if (!trap_PC_ReadToken(handle, &token))
00914         return qfalse;
00915     if (token.string[0] != '{') {
00916         return qfalse;
00917     }
00918 
00919     while ( 1 ) {
00920 
00921         if (!trap_PC_ReadToken(handle, &token))
00922             return qfalse;
00923     
00924         if ( token.string[0] == 0 ) {
00925             return qfalse;
00926         }
00927 
00928         if ( token.string[0] == '}' ) {
00929             return qtrue;
00930         }
00931 
00932         UI_ParseMenu(token.string); 
00933     }
00934     return qfalse;
00935 }
00936 
00937 void UI_LoadMenus(const char *menuFile, qboolean reset) {
00938     pc_token_t token;
00939     int handle;
00940     int start;
00941 
00942     start = trap_Milliseconds();
00943 
00944     handle = trap_PC_LoadSource( menuFile );
00945     if (!handle) {
00946         trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) );
00947         handle = trap_PC_LoadSource( "ui/menus.txt" );
00948         if (!handle) {
00949             trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n", menuFile ) );
00950         }
00951     }
00952 
00953     ui_new.integer = 1;
00954 
00955     if (reset) {
00956         Menu_Reset();
00957     }
00958 
00959     while ( 1 ) {
00960         if (!trap_PC_ReadToken(handle, &token))
00961             break;
00962         if( token.string[0] == 0 || token.string[0] == '}') {
00963             break;
00964         }
00965 
00966         if ( token.string[0] == '}' ) {
00967             break;
00968         }
00969 
00970         if (Q_stricmp(token.string, "loadmenu") == 0) {
00971             if (Load_Menu(handle)) {
00972                 continue;
00973             } else {
00974                 break;
00975             }
00976         }
00977     }
00978 
00979     Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start);
00980 
00981     trap_PC_FreeSource( handle );
00982 }
00983 
00984 void UI_Load() {
00985     char lastName[1024];
00986   menuDef_t *menu = Menu_GetFocused();
00987     char *menuSet = UI_Cvar_VariableString("ui_menuFiles");
00988     if (menu && menu->window.name) {
00989         strcpy(lastName, menu->window.name);
00990     }
00991     if (menuSet == NULL || menuSet[0] == '\0') {
00992         menuSet = "ui/menus.txt";
00993     }
00994 
00995     String_Init();
00996 
00997 #ifdef PRE_RELEASE_TADEMO
00998     UI_ParseGameInfo("demogameinfo.txt");
00999 #else
01000     UI_ParseGameInfo("gameinfo.txt");
01001     UI_LoadArenas();
01002 #endif
01003 
01004     UI_LoadMenus(menuSet, qtrue);
01005     Menus_CloseAll();
01006     Menus_ActivateByName(lastName);
01007 
01008 }
01009 
01010 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};
01011 #ifndef MISSIONPACK // bk001206
01012 static int numHandicaps = sizeof(handicapValues) / sizeof(const char*);
01013 #endif
01014 
01015 static void UI_DrawHandicap(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
01016   int i, h;
01017 
01018   h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") );
01019   i = 20 - h / 5;
01020 
01021   Text_Paint(rect->x, rect->y, scale, color, handicapValues[i], 0, 0, textStyle);
01022 }
01023 
01024 static void UI_DrawClanName(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
01025   Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_teamName"), 0, 0, textStyle);
01026 }
01027 
01028 
01029 static void UI_SetCapFragLimits(qboolean uiVars) {
01030     int cap = 5;
01031     int frag = 10;
01032     if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_OBELISK) {
01033         cap = 4;
01034     } else if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_HARVESTER) {
01035         cap = 15;
01036     }
01037     if (uiVars) {
01038         trap_Cvar_Set("ui_captureLimit", va("%d", cap));
01039         trap_Cvar_Set("ui_fragLimit", va("%d", frag));
01040     } else {
01041         trap_Cvar_Set("capturelimit", va("%d", cap));
01042         trap_Cvar_Set("fraglimit", va("%d", frag));
01043     }
01044 }
01045 // ui_gameType assumes gametype 0 is -1 ALL and will not show
01046 static void UI_DrawGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
01047   Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_gameType.integer].gameType, 0, 0, textStyle);
01048 }
01049 
01050 static void UI_DrawNetGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
01051     if (ui_netGameType.integer < 0 || ui_netGameType.integer > uiInfo.numGameTypes) {
01052         trap_Cvar_Set("ui_netGameType", "0");
01053         trap_Cvar_Set("ui_actualNetGameType", "0");
01054     }
01055   Text_Paint(rect->x, rect->y, scale, color, uiInfo.