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

cg_draw.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 // cg_draw.c -- draw all of the graphical elements during
00024 // active (after loading) gameplay
00025 
00026 #include "cg_local.h"
00027 
00028 #ifdef MISSIONPACK
00029 #include "../ui/ui_shared.h"
00030 
00031 // used for scoreboard
00032 extern displayContextDef_t cgDC;
00033 menuDef_t *menuScoreboard = NULL;
00034 #else
00035 int drawTeamOverlayModificationCount = -1;
00036 #endif
00037 
00038 int sortedTeamPlayers[TEAM_MAXOVERLAY];
00039 int numSortedTeamPlayers;
00040 
00041 char systemChat[256];
00042 char teamChat1[256];
00043 char teamChat2[256];
00044 
00045 #ifdef MISSIONPACK
00046 
00047 int CG_Text_Width(const char *text, float scale, int limit) {
00048   int count,len;
00049     float out;
00050     glyphInfo_t *glyph;
00051     float useScale;
00052 // FIXME: see ui_main.c, same problem
00053 //  const unsigned char *s = text;
00054     const char *s = text;
00055     fontInfo_t *font = &cgDC.Assets.textFont;
00056     if (scale <= cg_smallFont.value) {
00057         font = &cgDC.Assets.smallFont;
00058     } else if (scale > cg_bigFont.value) {
00059         font = &cgDC.Assets.bigFont;
00060     }
00061     useScale = scale * font->glyphScale;
00062   out = 0;
00063   if (text) {
00064     len = strlen(text);
00065         if (limit > 0 && len > limit) {
00066             len = limit;
00067         }
00068         count = 0;
00069         while (s && *s && count < len) {
00070             if ( Q_IsColorString(s) ) {
00071                 s += 2;
00072                 continue;
00073             } else {
00074                 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00075                 out += glyph->xSkip;
00076                 s++;
00077                 count++;
00078             }
00079     }
00080   }
00081   return out * useScale;
00082 }
00083 
00084 int CG_Text_Height(const char *text, float scale, int limit) {
00085   int len, count;
00086     float max;
00087     glyphInfo_t *glyph;
00088     float useScale;
00089 // TTimo: FIXME
00090 //  const unsigned char *s = text;
00091     const char *s = text;
00092     fontInfo_t *font = &cgDC.Assets.textFont;
00093     if (scale <= cg_smallFont.value) {
00094         font = &cgDC.Assets.smallFont;
00095     } else if (scale > cg_bigFont.value) {
00096         font = &cgDC.Assets.bigFont;
00097     }
00098     useScale = scale * font->glyphScale;
00099   max = 0;
00100   if (text) {
00101     len = strlen(text);
00102         if (limit > 0 && len > limit) {
00103             len = limit;
00104         }
00105         count = 0;
00106         while (s && *s && count < len) {
00107             if ( Q_IsColorString(s) ) {
00108                 s += 2;
00109                 continue;
00110             } else {
00111                 glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00112           if (max < glyph->height) {
00113               max = glyph->height;
00114               }
00115                 s++;
00116                 count++;
00117             }
00118     }
00119   }
00120   return max * useScale;
00121 }
00122 
00123 void CG_Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
00124   float w, h;
00125   w = width * scale;
00126   h = height * scale;
00127   CG_AdjustFrom640( &x, &y, &w, &h );
00128   trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
00129 }
00130 
00131 void CG_Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
00132   int len, count;
00133     vec4_t newColor;
00134     glyphInfo_t *glyph;
00135     float useScale;
00136     fontInfo_t *font = &cgDC.Assets.textFont;
00137     if (scale <= cg_smallFont.value) {
00138         font = &cgDC.Assets.smallFont;
00139     } else if (scale > cg_bigFont.value) {
00140         font = &cgDC.Assets.bigFont;
00141     }
00142     useScale = scale * font->glyphScale;
00143   if (text) {
00144 // TTimo: FIXME
00145 //      const unsigned char *s = text;
00146         const char *s = text;
00147         trap_R_SetColor( color );
00148         memcpy(&newColor[0], &color[0], sizeof(vec4_t));
00149     len = strlen(text);
00150         if (limit > 0 && len > limit) {
00151             len = limit;
00152         }
00153         count = 0;
00154         while (s && *s && count < len) {
00155             glyph = &font->glyphs[(int)*s]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build
00156       //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
00157       //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
00158             if ( Q_IsColorString( s ) ) {
00159                 memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
00160                 newColor[3] = color[3];
00161                 trap_R_SetColor( newColor );
00162                 s += 2;
00163                 continue;
00164             } else {
00165                 float yadj = useScale * glyph->top;
00166                 if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
00167                     int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
00168                     colorBlack[3] = newColor[3];
00169                     trap_R_SetColor( colorBlack );
00170                     CG_Text_PaintChar(x + ofs, y - yadj + ofs, 
00171                                                         glyph->imageWidth,
00172                                                         glyph->imageHeight,
00173                                                         useScale, 
00174                                                         glyph->s,
00175                                                         glyph->t,
00176                                                         glyph->s2,
00177                                                         glyph->t2,
00178                                                         glyph->glyph);
00179                     colorBlack[3] = 1.0;
00180                     trap_R_SetColor( newColor );
00181                 }
00182                 CG_Text_PaintChar(x, y - yadj, 
00183                                                     glyph->imageWidth,
00184                                                     glyph->imageHeight,
00185                                                     useScale, 
00186                                                     glyph->s,
00187                                                     glyph->t,
00188                                                     glyph->s2,
00189                                                     glyph->t2,
00190                                                     glyph->glyph);
00191                 // CG_DrawPic(x, y - yadj, scale * cgDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * cgDC.Assets.textFont.glyphs[text[i]].imageHeight, cgDC.Assets.textFont.glyphs[text[i]].glyph);
00192                 x += (glyph->xSkip * useScale) + adjust;
00193                 s++;
00194                 count++;
00195             }
00196     }
00197       trap_R_SetColor( NULL );
00198   }
00199 }
00200 
00201 
00202 #endif
00203 
00204 /*
00205 ==============
00206 CG_DrawField
00207 
00208 Draws large numbers for status bar and powerups
00209 ==============
00210 */
00211 #ifndef MISSIONPACK
00212 static void CG_DrawField (int x, int y, int width, int value) {
00213     char    num[16], *ptr;
00214     int     l;
00215     int     frame;
00216 
00217     if ( width < 1 ) {
00218         return;
00219     }
00220 
00221     // draw number string
00222     if ( width > 5 ) {
00223         width = 5;
00224     }
00225 
00226     switch ( width ) {
00227     case 1:
00228         value = value > 9 ? 9 : value;
00229         value = value < 0 ? 0 : value;
00230         break;
00231     case 2:
00232         value = value > 99 ? 99 : value;
00233         value = value < -9 ? -9 : value;
00234         break;
00235     case 3:
00236         value = value > 999 ? 999 : value;
00237         value = value < -99 ? -99 : value;
00238         break;
00239     case 4:
00240         value = value > 9999 ? 9999 : value;
00241         value = value < -999 ? -999 : value;
00242         break;
00243     }
00244 
00245     Com_sprintf (num, sizeof(num), "%i", value);
00246     l = strlen(num);
00247     if (l > width)
00248         l = width;
00249     x += 2 + CHAR_WIDTH*(width - l);
00250 
00251     ptr = num;
00252     while (*ptr && l)
00253     {
00254         if (*ptr == '-')
00255             frame = STAT_MINUS;
00256         else
00257             frame = *ptr -'0';
00258 
00259         CG_DrawPic( x,y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame] );
00260         x += CHAR_WIDTH;
00261         ptr++;
00262         l--;
00263     }
00264 }
00265 #endif // MISSIONPACK
00266 
00267 /*
00268 ================
00269 CG_Draw3DModel
00270 
00271 ================
00272 */
00273 void CG_Draw3DModel( float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles ) {
00274     refdef_t        refdef;
00275     refEntity_t     ent;
00276 
00277     if ( !cg_draw3dIcons.integer || !cg_drawIcons.integer ) {
00278         return;
00279     }
00280 
00281     CG_AdjustFrom640( &x, &y, &w, &h );
00282 
00283     memset( &refdef, 0, sizeof( refdef ) );
00284 
00285     memset( &ent, 0, sizeof( ent ) );
00286     AnglesToAxis( angles, ent.axis );
00287     VectorCopy( origin, ent.origin );
00288     ent.hModel = model;
00289     ent.customSkin = skin;
00290     ent.renderfx = RF_NOSHADOW;     // no stencil shadows
00291 
00292     refdef.rdflags = RDF_NOWORLDMODEL;
00293 
00294     AxisClear( refdef.viewaxis );
00295 
00296     refdef.fov_x = 30;
00297     refdef.fov_y = 30;
00298 
00299     refdef.x = x;
00300     refdef.y = y;
00301     refdef.width = w;
00302     refdef.height = h;
00303 
00304     refdef.time = cg.time;
00305 
00306     trap_R_ClearScene();
00307     trap_R_AddRefEntityToScene( &ent );
00308     trap_R_RenderScene( &refdef );
00309 }
00310 
00311 /*
00312 ================
00313 CG_DrawHead
00314 
00315 Used for both the status bar and the scoreboard
00316 ================
00317 */
00318 void CG_DrawHead( float x, float y, float w, float h, int clientNum, vec3_t headAngles ) {
00319     clipHandle_t    cm;
00320     clientInfo_t    *ci;
00321     float           len;
00322     vec3_t          origin;
00323     vec3_t          mins, maxs;
00324 
00325     ci = &cgs.clientinfo[ clientNum ];
00326 
00327     if ( cg_draw3dIcons.integer ) {
00328         cm = ci->headModel;
00329         if ( !cm ) {
00330             return;
00331         }
00332 
00333         // offset the origin y and z to center the head
00334         trap_R_ModelBounds( cm, mins, maxs );
00335 
00336         origin[2] = -0.5 * ( mins[2] + maxs[2] );
00337         origin[1] = 0.5 * ( mins[1] + maxs[1] );
00338 
00339         // calculate distance so the head nearly fills the box
00340         // assume heads are taller than wide
00341         len = 0.7 * ( maxs[2] - mins[2] );      
00342         origin[0] = len / 0.268;    // len / tan( fov/2 )
00343 
00344         // allow per-model tweaking
00345         VectorAdd( origin, ci->headOffset, origin );
00346 
00347         CG_Draw3DModel( x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles );
00348     } else if ( cg_drawIcons.integer ) {
00349         CG_DrawPic( x, y, w, h, ci->modelIcon );
00350     }
00351 
00352     // if they are deferred, draw a cross out
00353     if ( ci->deferred ) {
00354         CG_DrawPic( x, y, w, h, cgs.media.deferShader );
00355     }
00356 }
00357 
00358 /*
00359 ================
00360 CG_DrawFlagModel
00361 
00362 Used for both the status bar and the scoreboard
00363 ================
00364 */
00365 void CG_DrawFlagModel( float x, float y, float w, float h, int team, qboolean force2D ) {
00366     qhandle_t       cm;
00367     float           len;
00368     vec3_t          origin, angles;
00369     vec3_t          mins, maxs;
00370     qhandle_t       handle;
00371 
00372     if ( !force2D && cg_draw3dIcons.integer ) {
00373 
00374         VectorClear( angles );
00375 
00376         cm = cgs.media.redFlagModel;
00377 
00378         // offset the origin y and z to center the flag
00379         trap_R_ModelBounds( cm, mins, maxs );
00380 
00381         origin[2] = -0.5 * ( mins[2] + maxs[2] );
00382         origin[1] = 0.5 * ( mins[1] + maxs[1] );
00383 
00384         // calculate distance so the flag nearly fills the box
00385         // assume heads are taller than wide
00386         len = 0.5 * ( maxs[2] - mins[2] );      
00387         origin[0] = len / 0.268;    // len / tan( fov/2 )
00388 
00389         angles[YAW] = 60 * sin( cg.time / 2000.0 );;
00390 
00391         if( team == TEAM_RED ) {
00392             handle = cgs.media.redFlagModel;
00393         } else if( team == TEAM_BLUE ) {
00394             handle = cgs.media.blueFlagModel;
00395         } else if( team == TEAM_FREE ) {
00396             handle = cgs.media.neutralFlagModel;
00397         } else {
00398             return;
00399         }
00400         CG_Draw3DModel( x, y, w, h, handle, 0, origin, angles );
00401     } else if ( cg_drawIcons.integer ) {
00402         gitem_t *item;
00403 
00404         if( team == TEAM_RED ) {
00405             item = BG_FindItemForPowerup( PW_REDFLAG );
00406         } else if( team == TEAM_BLUE ) {
00407             item = BG_FindItemForPowerup( PW_BLUEFLAG );
00408         } else if( team == TEAM_FREE ) {
00409             item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
00410         } else {
00411             return;
00412         }
00413         if (item) {
00414           CG_DrawPic( x, y, w, h, cg_items[ ITEM_INDEX(item) ].icon );
00415         }
00416     }
00417 }
00418 
00419 /*
00420 ================
00421 CG_DrawStatusBarHead
00422 
00423 ================
00424 */
00425 #ifndef MISSIONPACK
00426 
00427 static void CG_DrawStatusBarHead( float x ) {
00428     vec3_t      angles;
00429     float       size, stretch;
00430     float       frac;
00431 
00432     VectorClear( angles );
00433 
00434     if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) {
00435         frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME;
00436         size = ICON_SIZE * 1.25 * ( 1.5 - frac * 0.5 );
00437 
00438         stretch = size - ICON_SIZE * 1.25;
00439         // kick in the direction of damage
00440         x -= stretch * 0.5 + cg.damageX * stretch * 0.5;
00441 
00442         cg.headStartYaw = 180 + cg.damageX * 45;
00443 
00444         cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
00445         cg.headEndPitch = 5 * cos( crandom()*M_PI );
00446 
00447         cg.headStartTime = cg.time;
00448         cg.headEndTime = cg.time + 100 + random() * 2000;
00449     } else {
00450         if ( cg.time >= cg.headEndTime ) {
00451             // select a new head angle
00452             cg.headStartYaw = cg.headEndYaw;
00453             cg.headStartPitch = cg.headEndPitch;
00454             cg.headStartTime = cg.headEndTime;
00455             cg.headEndTime = cg.time + 100 + random() * 2000;
00456 
00457             cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
00458             cg.headEndPitch = 5 * cos( crandom()*M_PI );
00459         }
00460 
00461         size = ICON_SIZE * 1.25;
00462     }
00463 
00464     // if the server was frozen for a while we may have a bad head start time
00465     if ( cg.headStartTime > cg.time ) {
00466         cg.headStartTime = cg.time;
00467     }
00468 
00469     frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime );
00470     frac = frac * frac * ( 3 - 2 * frac );
00471     angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
00472     angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
00473 
00474     CG_DrawHead( x, 480 - size, size, size, 
00475                 cg.snap->ps.clientNum, angles );
00476 }
00477 #endif // MISSIONPACK
00478 
00479 /*
00480 ================
00481 CG_DrawStatusBarFlag
00482 
00483 ================
00484 */
00485 #ifndef MISSIONPACK
00486 static void CG_DrawStatusBarFlag( float x, int team ) {
00487     CG_DrawFlagModel( x, 480 - ICON_SIZE, ICON_SIZE, ICON_SIZE, team, qfalse );
00488 }
00489 #endif // MISSIONPACK
00490 
00491 /*
00492 ================
00493 CG_DrawTeamBackground
00494 
00495 ================
00496 */
00497 void CG_DrawTeamBackground( int x, int y, int w, int h, float alpha, int team )
00498 {
00499     vec4_t      hcolor;
00500 
00501     hcolor[3] = alpha;
00502     if ( team == TEAM_RED ) {
00503         hcolor[0] = 1;
00504         hcolor[1] = 0;
00505         hcolor[2] = 0;
00506     } else if ( team == TEAM_BLUE ) {
00507         hcolor[0] = 0;
00508         hcolor[1] = 0;
00509         hcolor[2] = 1;
00510     } else {
00511         return;
00512     }
00513     trap_R_SetColor( hcolor );
00514     CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
00515     trap_R_SetColor( NULL );
00516 }
00517 
00518 /*
00519 ================
00520 CG_DrawStatusBar
00521 
00522 ================
00523 */
00524 #ifndef MISSIONPACK
00525 static void CG_DrawStatusBar( void ) {
00526     int         color;
00527     centity_t   *cent;
00528     playerState_t   *ps;
00529     int         value;
00530     vec4_t      hcolor;
00531     vec3_t      angles;
00532     vec3_t      origin;
00533 #ifdef MISSIONPACK
00534     qhandle_t   handle;
00535 #endif
00536     static float colors[4][4] = { 
00537 //      { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
00538         { 1.0f, 0.69f, 0.0f, 1.0f },    // normal
00539         { 1.0f, 0.2f, 0.2f, 1.0f },     // low health
00540         { 0.5f, 0.5f, 0.5f, 1.0f },     // weapon firing
00541         { 1.0f, 1.0f, 1.0f, 1.0f } };   // health > 100
00542 
00543     if ( cg_drawStatus.integer == 0 ) {
00544         return;
00545     }
00546 
00547     // draw the team background
00548     CG_DrawTeamBackground( 0, 420, 640, 60, 0.33f, cg.snap->ps.persistant[PERS_TEAM] );
00549 
00550     cent = &cg_entities[cg.snap->ps.clientNum];
00551     ps = &cg.snap->ps;
00552 
00553     VectorClear( angles );
00554 
00555     // draw any 3D icons first, so the changes back to 2D are minimized
00556     if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) {
00557         origin[0] = 70;
00558         origin[1] = 0;
00559         origin[2] = 0;
00560         angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 );
00561         CG_Draw3DModel( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
00562                        cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
00563     }
00564 
00565     CG_DrawStatusBarHead( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE );
00566 
00567     if( cg.predictedPlayerState.powerups[PW_REDFLAG] ) {
00568         CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_RED );
00569     } else if( cg.predictedPlayerState.powerups[PW_BLUEFLAG] ) {
00570         CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_BLUE );
00571     } else if( cg.predictedPlayerState.powerups[PW_NEUTRALFLAG] ) {
00572         CG_DrawStatusBarFlag( 185 + CHAR_WIDTH*3 + TEXT_ICON_SPACE + ICON_SIZE, TEAM_FREE );
00573     }
00574 
00575     if ( ps->stats[ STAT_ARMOR ] ) {
00576         origin[0] = 90;
00577         origin[1] = 0;
00578         origin[2] = -10;
00579         angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
00580         CG_Draw3DModel( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE,
00581                        cgs.media.armorModel, 0, origin, angles );
00582     }
00583 #ifdef MISSIONPACK
00584     if( cgs.gametype == GT_HARVESTER ) {
00585         origin[0] = 90;
00586         origin[1] = 0;
00587         origin[2] = -10;
00588         angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
00589         if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
00590             handle = cgs.media.redCubeModel;
00591         } else {
00592             handle = cgs.media.blueCubeModel;
00593         }
00594         CG_Draw3DModel( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 416, ICON_SIZE, ICON_SIZE, handle, 0, origin, angles );
00595     }
00596 #endif
00597     //
00598     // ammo
00599     //
00600     if ( cent->currentState.weapon ) {
00601         value = ps->ammo[cent->currentState.weapon];
00602         if ( value > -1 ) {
00603             if ( cg.predictedPlayerState.weaponstate == WEAPON_FIRING
00604                 && cg.predictedPlayerState.weaponTime > 100 ) {
00605                 // draw as dark grey when reloading
00606                 color = 2;  // dark grey
00607             } else {
00608                 if ( value >= 0 ) {
00609                     color = 0;  // green
00610                 } else {
00611                     color = 1;  // red
00612                 }
00613             }
00614             trap_R_SetColor( colors[color] );
00615             
00616             CG_DrawField (0, 432, 3, value);
00617             trap_R_SetColor( NULL );
00618 
00619             // if we didn't draw a 3D icon, draw a 2D icon for ammo
00620             if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
00621                 qhandle_t   icon;
00622 
00623                 icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon;
00624                 if ( icon ) {
00625                     CG_DrawPic( CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, icon );
00626                 }
00627             }
00628         }
00629     }
00630 
00631     //
00632     // health
00633     //
00634     value = ps->stats[STAT_HEALTH];
00635     if ( value > 100 ) {
00636         trap_R_SetColor( colors[3] );       // white
00637     } else if (value > 25) {
00638         trap_R_SetColor( colors[0] );   // green
00639     } else if (value > 0) {
00640         color = (cg.time >> 8) & 1; // flash
00641         trap_R_SetColor( colors[color] );
00642     } else {
00643         trap_R_SetColor( colors[1] );   // red
00644     }
00645 
00646     // stretch the health up when taking damage
00647     CG_DrawField ( 185, 432, 3, value);
00648     CG_ColorForHealth( hcolor );
00649     trap_R_SetColor( hcolor );
00650 
00651 
00652     //
00653     // armor
00654     //
00655     value = ps->stats[STAT_ARMOR];
00656     if (value > 0 ) {
00657         trap_R_SetColor( colors[0] );
00658         CG_DrawField (370, 432, 3, value);
00659         trap_R_SetColor( NULL );
00660         // if we didn't draw a 3D icon, draw a 2D icon for armor
00661         if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
00662             CG_DrawPic( 370 + CHAR_WIDTH*3 + TEXT_ICON_SPACE, 432, ICON_SIZE, ICON_SIZE, cgs.media.armorIcon );
00663         }
00664 
00665     }
00666 #ifdef MISSIONPACK
00667     //
00668     // cubes
00669     //
00670     if( cgs.gametype == GT_HARVESTER ) {
00671         value = ps->generic1;
00672         if( value > 99 ) {
00673             value = 99;
00674         }
00675         trap_R_SetColor( colors[0] );
00676         CG_DrawField (640 - (CHAR_WIDTH*2 + TEXT_ICON_SPACE + ICON_SIZE), 432, 2, value);
00677         trap_R_SetColor( NULL );
00678         // if we didn't draw a 3D icon, draw a 2D icon for armor
00679         if ( !cg_draw3dIcons.integer && cg_drawIcons.integer ) {
00680             if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
00681                 handle = cgs.media.redCubeIcon;
00682             } else {
00683                 handle = cgs.media.blueCubeIcon;
00684             }
00685             CG_DrawPic( 640 - (TEXT_ICON_SPACE + ICON_SIZE), 432, ICON_SIZE, ICON_SIZE, handle );
00686         }
00687     }
00688 #endif
00689 }
00690 #endif
00691 
00692 /*
00693 ===========================================================================================
00694 
00695   UPPER RIGHT CORNER
00696 
00697 ===========================================================================================
00698 */
00699 
00700 /*
00701 ================
00702 CG_DrawAttacker
00703 
00704 ================
00705 */
00706 static float CG_DrawAttacker( float y ) {
00707     int         t;
00708     float       size;
00709     vec3_t      angles;
00710     const char  *info;
00711     const char  *name;
00712     int         clientNum;
00713 
00714     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00715         return y;
00716     }
00717 
00718     if ( !cg.attackerTime ) {
00719         return y;
00720     }
00721 
00722     clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER];
00723     if ( clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum ) {
00724         return y;
00725     }
00726 
00727     t = cg.time - cg.attackerTime;
00728     if ( t > ATTACKER_HEAD_TIME ) {
00729         cg.attackerTime = 0;
00730         return y;
00731     }
00732 
00733     size = ICON_SIZE * 1.25;
00734 
00735     angles[PITCH] = 0;
00736     angles[YAW] = 180;
00737     angles[ROLL] = 0;
00738     CG_DrawHead( 640 - size, y, size, size, clientNum, angles );
00739 
00740     info = CG_ConfigString( CS_PLAYERS + clientNum );
00741     name = Info_ValueForKey(  info, "n" );
00742     y += size;
00743     CG_DrawBigString( 640 - ( Q_PrintStrlen( name ) * BIGCHAR_WIDTH), y, name, 0.5 );
00744 
00745     return y + BIGCHAR_HEIGHT + 2;
00746 }
00747 
00748 /*
00749 ==================
00750 CG_DrawSnapshot
00751 ==================
00752 */
00753 static float CG_DrawSnapshot( float y ) {
00754     char        *s;
00755     int         w;
00756 
00757     s = va( "time:%i snap:%i cmd:%i", cg.snap->serverTime, 
00758         cg.latestSnapshotNum, cgs.serverCommandSequence );
00759     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
00760 
00761     CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
00762 
00763     return y + BIGCHAR_HEIGHT + 4;
00764 }
00765 
00766 /*
00767 ==================
00768 CG_DrawFPS
00769 ==================
00770 */
00771 #define FPS_FRAMES  4
00772 static float CG_DrawFPS( float y ) {
00773     char        *s;
00774     int         w;
00775     static int  previousTimes[FPS_FRAMES];
00776     static int  index;
00777     int     i, total;
00778     int     fps;
00779     static  int previous;
00780     int     t, frameTime;
00781 
00782     // don't use serverTime, because that will be drifting to
00783     // correct for internet lag changes, timescales, timedemos, etc
00784     t = trap_Milliseconds();
00785     frameTime = t - previous;
00786     previous = t;
00787 
00788     previousTimes[index % FPS_FRAMES] = frameTime;
00789     index++;
00790     if ( index > FPS_FRAMES ) {
00791         // average multiple frames together to smooth changes out a bit
00792         total = 0;
00793         for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
00794             total += previousTimes[i];
00795         }
00796         if ( !total ) {
00797             total = 1;
00798         }
00799         fps = 1000 * FPS_FRAMES / total;
00800 
00801         s = va( "%ifps", fps );
00802         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
00803 
00804         CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
00805     }
00806 
00807     return y + BIGCHAR_HEIGHT + 4;
00808 }
00809 
00810 /*
00811 =================
00812 CG_DrawTimer
00813 =================
00814 */
00815 static float CG_DrawTimer( float y ) {
00816     char        *s;
00817     int         w;
00818     int         mins, seconds, tens;
00819     int         msec;
00820 
00821     msec = cg.time - cgs.levelStartTime;
00822 
00823     seconds = msec / 1000;
00824     mins = seconds / 60;
00825     seconds -= mins * 60;
00826     tens = seconds / 10;
00827     seconds -= tens * 10;
00828 
00829     s = va( "%i:%i%i", mins, tens, seconds );
00830     w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
00831 
00832     CG_DrawBigString( 635 - w, y + 2, s, 1.0F);
00833 
00834     return y + BIGCHAR_HEIGHT + 4;
00835 }
00836 
00837 
00838 /*
00839 =================
00840 CG_DrawTeamOverlay
00841 =================
00842 */
00843 
00844 static float CG_DrawTeamOverlay( float y, qboolean right, qboolean upper ) {
00845     int x, w, h, xx;
00846     int i, j, len;
00847     const char *p;
00848     vec4_t      hcolor;
00849     int pwidth, lwidth;
00850     int plyrs;
00851     char st[16];
00852     clientInfo_t *ci;
00853     gitem_t *item;
00854     int ret_y, count;
00855 
00856     if ( !cg_drawTeamOverlay.integer ) {
00857         return y;
00858     }
00859 
00860     if ( cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE ) {
00861         return y; // Not on any team
00862     }
00863 
00864     plyrs = 0;
00865 
00866     // max player name width
00867     pwidth = 0;
00868     count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers;
00869     for (i = 0; i < count; i++) {
00870         ci = cgs.clientinfo + sortedTeamPlayers[i];
00871         if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
00872             plyrs++;
00873             len = CG_DrawStrlen(ci->name);
00874             if (len > pwidth)
00875                 pwidth = len;
00876         }
00877     }
00878 
00879     if (!plyrs)
00880         return y;
00881 
00882     if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH)
00883         pwidth = TEAM_OVERLAY_MAXNAME_WIDTH;
00884 
00885     // max location name width
00886     lwidth = 0;
00887     for (i = 1; i < MAX_LOCATIONS; i++) {
00888         p = CG_ConfigString(CS_LOCATIONS + i);
00889         if (p && *p) {
00890             len = CG_DrawStrlen(p);
00891             if (len > lwidth)
00892                 lwidth = len;
00893         }
00894     }
00895 
00896     if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH)
00897         lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH;
00898 
00899     w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH;
00900 
00901     if ( right )
00902         x = 640 - w;
00903     else
00904         x = 0;
00905 
00906     h = plyrs * TINYCHAR_HEIGHT;
00907 
00908     if ( upper ) {
00909         ret_y = y + h;
00910     } else {
00911         y -= h;
00912         ret_y = y;
00913     }
00914 
00915     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
00916         hcolor[0] = 1.0f;
00917         hcolor[1] = 0.0f;
00918         hcolor[2] = 0.0f;
00919         hcolor[3] = 0.33f;
00920     } else { // if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
00921         hcolor[0] = 0.0f;
00922         hcolor[1] = 0.0f;
00923         hcolor[2] = 1.0f;
00924         hcolor[3] = 0.33f;
00925     }
00926     trap_R_SetColor( hcolor );
00927     CG_DrawPic( x, y, w, h, cgs.media.teamStatusBar );
00928     trap_R_SetColor( NULL );
00929 
00930     for (i = 0; i < count; i++) {
00931         ci = cgs.clientinfo + sortedTeamPlayers[i];
00932         if ( ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) {
00933 
00934             hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0;
00935 
00936             xx = x + TINYCHAR_WIDTH;
00937 
00938             CG_DrawStringExt( xx, y,
00939                 ci->name, hcolor, qfalse, qfalse,
00940                 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH);
00941 
00942             if (lwidth) {
00943                 p = CG_ConfigString(CS_LOCATIONS + ci->location);
00944                 if (!p || !*p)
00945                     p = "unknown";
00946                 len = CG_DrawStrlen(p);
00947                 if (len > lwidth)
00948                     len = lwidth;
00949 
00950 //              xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + 
00951 //                  ((lwidth/2 - len/2) * TINYCHAR_WIDTH);
00952                 xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth;
00953                 CG_DrawStringExt( xx, y,
00954                     p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT,
00955                     TEAM_OVERLAY_MAXLOCATION_WIDTH);
00956             }
00957 
00958             CG_GetColorForHealth( ci->health, ci->armor, hcolor );
00959 
00960             Com_sprintf (st, sizeof(st), "%3i %3i", ci->health, ci->armor);
00961 
00962             xx = x + TINYCHAR_WIDTH * 3 + 
00963                 TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth;
00964 
00965             CG_DrawStringExt( xx, y,
00966                 st, hcolor, qfalse, qfalse,
00967                 TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0 );
00968 
00969             // draw weapon icon
00970             xx += TINYCHAR_WIDTH * 3;
00971 
00972             if ( cg_weapons[ci->curWeapon].weaponIcon ) {
00973                 CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
00974                     cg_weapons[ci->curWeapon].weaponIcon );
00975             } else {
00976                 CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
00977                     cgs.media.deferShader );
00978             }
00979 
00980             // Draw powerup icons
00981             if (right) {
00982                 xx = x;
00983             } else {
00984                 xx = x + w - TINYCHAR_WIDTH;
00985             }
00986             for (j = 0; j <= PW_NUM_POWERUPS; j++) {
00987                 if (ci->powerups & (1 << j)) {
00988 
00989                     item = BG_FindItemForPowerup( j );
00990 
00991                     if (item) {
00992                         CG_DrawPic( xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 
00993                         trap_R_RegisterShader( item->icon ) );
00994                         if (right) {
00995                             xx -= TINYCHAR_WIDTH;
00996                         } else {
00997                             xx += TINYCHAR_WIDTH;
00998                         }
00999                     }
01000                 }
01001             }
01002 
01003             y += TINYCHAR_HEIGHT;
01004         }
01005     }
01006 
01007     return ret_y;
01008 //#endif
01009 }
01010 
01011 
01012 /*
01013 =====================
01014 CG_DrawUpperRight
01015 
01016 =====================
01017 */
01018 static void CG_DrawUpperRight( void ) {
01019     float   y;
01020 
01021     y = 0;
01022 
01023     if ( cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1 ) {
01024         y = CG_DrawTeamOverlay( y, qtrue, qtrue );
01025     } 
01026     if ( cg_drawSnapshot.integer ) {
01027         y = CG_DrawSnapshot( y );
01028     }
01029     if ( cg_drawFPS.integer ) {
01030         y = CG_DrawFPS( y );
01031     }
01032     if ( cg_drawTimer.integer ) {
01033         y = CG_DrawTimer( y );
01034     }
01035     if ( cg_drawAttacker.integer ) {
01036         y = CG_DrawAttacker( y );
01037     }
01038 
01039 }
01040 
01041 /*
01042 ===========================================================================================
01043 
01044   LOWER RIGHT CORNER
01045 
01046 ===========================================================================================
01047 */
01048 
01049 /*
01050 =================
01051 CG_DrawScores
01052 
01053 Draw the small two score display
01054 =================
01055 */
01056 #ifndef MISSIONPACK
01057 static float CG_DrawScores( float y ) {
01058     const char  *s;
01059     int         s1, s2, score;
01060     int         x, w;
01061     int         v;
01062     vec4_t      color;
01063     float       y1;
01064     gitem_t     *item;
01065 
01066     s1 = cgs.scores1;
01067     s2 = cgs.scores2;
01068 
01069     y -=  BIGCHAR_HEIGHT + 8;
01070 
01071     y1 = y;
01072 
01073     // draw from the right side to left
01074     if ( cgs.gametype >= GT_TEAM ) {
01075         x = 640;
01076         color[0] = 0.0f;
01077         color[1] = 0.0f;
01078         color[2] = 1.0f;
01079         color[3] = 0.33f;
01080         s = va( "%2i", s2 );
01081         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01082         x -= w;
01083         CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01084         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
01085             CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
01086         }
01087         CG_DrawBigString( x + 4, y, s, 1.0F);
01088 
01089         if ( cgs.gametype == GT_CTF ) {
01090             // Display flag status
01091             item = BG_FindItemForPowerup( PW_BLUEFLAG );
01092 
01093             if (item) {
01094                 y1 = y - BIGCHAR_HEIGHT - 8;
01095                 if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
01096                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.blueFlagShader[cgs.blueflag] );
01097                 }
01098             }
01099         }
01100         color[0] = 1.0f;
01101         color[1] = 0.0f;
01102         color[2] = 0.0f;
01103         color[3] = 0.33f;
01104         s = va( "%2i", s1 );
01105         w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01106         x -= w;
01107         CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01108         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) {
01109             CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
01110         }
01111         CG_DrawBigString( x + 4, y, s, 1.0F);
01112 
01113         if ( cgs.gametype == GT_CTF ) {
01114             // Display flag status
01115             item = BG_FindItemForPowerup( PW_REDFLAG );
01116 
01117             if (item) {
01118                 y1 = y - BIGCHAR_HEIGHT - 8;
01119                 if( cgs.redflag >= 0 && cgs.redflag <= 2 ) {
01120                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.redFlagShader[cgs.redflag] );
01121                 }
01122             }
01123         }
01124 
01125 #ifdef MISSIONPACK
01126         if ( cgs.gametype == GT_1FCTF ) {
01127             // Display flag status
01128             item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
01129 
01130             if (item) {
01131                 y1 = y - BIGCHAR_HEIGHT - 8;
01132                 if( cgs.flagStatus >= 0 && cgs.flagStatus <= 3 ) {
01133                     CG_DrawPic( x, y1-4, w, BIGCHAR_HEIGHT+8, cgs.media.flagShader[cgs.flagStatus] );
01134                 }
01135             }
01136         }
01137 #endif
01138         if ( cgs.gametype >= GT_CTF ) {
01139             v = cgs.capturelimit;
01140         } else {
01141             v = cgs.fraglimit;
01142         }
01143         if ( v ) {
01144             s = va( "%2i", v );
01145             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01146             x -= w;
01147             CG_DrawBigString( x + 4, y, s, 1.0F);
01148         }
01149 
01150     } else {
01151         qboolean    spectator;
01152 
01153         x = 640;
01154         score = cg.snap->ps.persistant[PERS_SCORE];
01155         spectator = ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR );
01156 
01157         // always show your score in the second box if not in first place
01158         if ( s1 != score ) {
01159             s2 = score;
01160         }
01161         if ( s2 != SCORE_NOT_PRESENT ) {
01162             s = va( "%2i", s2 );
01163             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01164             x -= w;
01165             if ( !spectator && score == s2 && score != s1 ) {
01166                 color[0] = 1.0f;
01167                 color[1] = 0.0f;
01168                 color[2] = 0.0f;
01169                 color[3] = 0.33f;
01170                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01171                 CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
01172             } else {
01173                 color[0] = 0.5f;
01174                 color[1] = 0.5f;
01175                 color[2] = 0.5f;
01176                 color[3] = 0.33f;
01177                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01178             }   
01179             CG_DrawBigString( x + 4, y, s, 1.0F);
01180         }
01181 
01182         // first place
01183         if ( s1 != SCORE_NOT_PRESENT ) {
01184             s = va( "%2i", s1 );
01185             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01186             x -= w;
01187             if ( !spectator && score == s1 ) {
01188                 color[0] = 0.0f;
01189                 color[1] = 0.0f;
01190                 color[2] = 1.0f;
01191                 color[3] = 0.33f;
01192                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01193                 CG_DrawPic( x, y-4, w, BIGCHAR_HEIGHT+8, cgs.media.selectShader );
01194             } else {
01195                 color[0] = 0.5f;
01196                 color[1] = 0.5f;
01197                 color[2] = 0.5f;
01198                 color[3] = 0.33f;
01199                 CG_FillRect( x, y-4,  w, BIGCHAR_HEIGHT+8, color );
01200             }   
01201             CG_DrawBigString( x + 4, y, s, 1.0F);
01202         }
01203 
01204         if ( cgs.fraglimit ) {
01205             s = va( "%2i", cgs.fraglimit );
01206             w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH + 8;
01207             x -= w;
01208             CG_DrawBigString( x + 4, y, s, 1.0F);
01209         }
01210 
01211     }
01212 
01213     return y1 - 8;
01214 }
01215 #endif // MISSIONPACK
01216 
01217 /*
01218 ================
01219 CG_DrawPowerups
01220 ================
01221 */
01222 #ifndef MISSIONPACK
01223 static float CG_DrawPowerups( float y ) {
01224     int     sorted[MAX_POWERUPS];
01225     int     sortedTime[MAX_POWERUPS];
01226     int     i, j, k;
01227     int     active;
01228     playerState_t   *ps;
01229     int     t;
01230     gitem_t *item;
01231     int     x;
01232     int     color;
01233     float   size;
01234     float   f;
01235     static float colors[2][4] = { 
01236     { 0.2f, 1.0f, 0.2f, 1.0f } , 
01237     { 1.0f, 0.2f, 0.2f, 1.0f } 
01238   };
01239 
01240     ps = &cg.snap->ps;
01241 
01242     if ( ps->stats[STAT_HEALTH] <= 0 ) {
01243         return y;
01244     }
01245 
01246     // sort the list by time remaining
01247     active = 0;
01248     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
01249         if ( !ps->powerups[ i ] ) {
01250             continue;
01251         }
01252         t = ps->powerups[ i ] - cg.time;
01253         // ZOID--don't draw if the power up has unlimited time (999 seconds)
01254