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

cg_newdraw.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 #ifndef MISSIONPACK // bk001204
00024 #error This file not be used for classic Q3A.
00025 #endif
00026 
00027 #include "cg_local.h"
00028 #include "../ui/ui_shared.h"
00029 
00030 extern displayContextDef_t cgDC;
00031 
00032 
00033 // set in CG_ParseTeamInfo
00034 
00035 //static int sortedTeamPlayers[TEAM_MAXOVERLAY];
00036 //static int numSortedTeamPlayers;
00037 int drawTeamOverlayModificationCount = -1;
00038 
00039 //static char systemChat[256];
00040 //static char teamChat1[256];
00041 //static char teamChat2[256];
00042 
00043 void CG_InitTeamChat() {
00044   memset(teamChat1, 0, sizeof(teamChat1));
00045   memset(teamChat2, 0, sizeof(teamChat2));
00046   memset(systemChat, 0, sizeof(systemChat));
00047 }
00048 
00049 void CG_SetPrintString(int type, const char *p) {
00050   if (type == SYSTEM_PRINT) {
00051     strcpy(systemChat, p);
00052   } else {
00053     strcpy(teamChat2, teamChat1);
00054     strcpy(teamChat1, p);
00055   }
00056 }
00057 
00058 void CG_CheckOrderPending() {
00059     if (cgs.gametype < GT_CTF) {
00060         return;
00061     }
00062     if (cgs.orderPending) {
00063         //clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer];
00064         const char *p1, *p2, *b;
00065         p1 = p2 = b = NULL;
00066         switch (cgs.currentOrder) {
00067             case TEAMTASK_OFFENSE:
00068                 p1 = VOICECHAT_ONOFFENSE;
00069                 p2 = VOICECHAT_OFFENSE;
00070                 b = "+button7; wait; -button7";
00071             break;
00072             case TEAMTASK_DEFENSE:
00073                 p1 = VOICECHAT_ONDEFENSE;
00074                 p2 = VOICECHAT_DEFEND;
00075                 b = "+button8; wait; -button8";
00076             break;                  
00077             case TEAMTASK_PATROL:
00078                 p1 = VOICECHAT_ONPATROL;
00079                 p2 = VOICECHAT_PATROL;
00080                 b = "+button9; wait; -button9";
00081             break;
00082             case TEAMTASK_FOLLOW: 
00083                 p1 = VOICECHAT_ONFOLLOW;
00084                 p2 = VOICECHAT_FOLLOWME;
00085                 b = "+button10; wait; -button10";
00086             break;
00087             case TEAMTASK_CAMP:
00088                 p1 = VOICECHAT_ONCAMPING;
00089                 p2 = VOICECHAT_CAMP;
00090             break;
00091             case TEAMTASK_RETRIEVE:
00092                 p1 = VOICECHAT_ONGETFLAG;
00093                 p2 = VOICECHAT_RETURNFLAG;
00094             break;
00095             case TEAMTASK_ESCORT:
00096                 p1 = VOICECHAT_ONFOLLOWCARRIER;
00097                 p2 = VOICECHAT_FOLLOWFLAGCARRIER;
00098             break;
00099         }
00100 
00101         if (cg_currentSelectedPlayer.integer == numSortedTeamPlayers) {
00102             // to everyone
00103             trap_SendConsoleCommand(va("cmd vsay_team %s\n", p2));
00104         } else {
00105             // for the player self
00106             if (sortedTeamPlayers[cg_currentSelectedPlayer.integer] == cg.snap->ps.clientNum && p1) {
00107                 trap_SendConsoleCommand(va("teamtask %i\n", cgs.currentOrder));
00108                 //trap_SendConsoleCommand(va("cmd say_team %s\n", p2));
00109                 trap_SendConsoleCommand(va("cmd vsay_team %s\n", p1));
00110             } else if (p2) {
00111                 //trap_SendConsoleCommand(va("cmd say_team %s, %s\n", ci->name,p));
00112                 trap_SendConsoleCommand(va("cmd vtell %d %s\n", sortedTeamPlayers[cg_currentSelectedPlayer.integer], p2));
00113             }
00114         }
00115         if (b) {
00116             trap_SendConsoleCommand(b);
00117         }
00118         cgs.orderPending = qfalse;
00119     }
00120 }
00121 
00122 static void CG_SetSelectedPlayerName() {
00123   if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) {
00124         clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[cg_currentSelectedPlayer.integer];
00125       if (ci) {
00126             trap_Cvar_Set("cg_selectedPlayerName", ci->name);
00127             trap_Cvar_Set("cg_selectedPlayer", va("%d", sortedTeamPlayers[cg_currentSelectedPlayer.integer]));
00128             cgs.currentOrder = ci->teamTask;
00129       }
00130     } else {
00131         trap_Cvar_Set("cg_selectedPlayerName", "Everyone");
00132     }
00133 }
00134 int CG_GetSelectedPlayer() {
00135     if (cg_currentSelectedPlayer.integer < 0 || cg_currentSelectedPlayer.integer >= numSortedTeamPlayers) {
00136         cg_currentSelectedPlayer.integer = 0;
00137     }
00138     return cg_currentSelectedPlayer.integer;
00139 }
00140 
00141 void CG_SelectNextPlayer() {
00142     CG_CheckOrderPending();
00143     if (cg_currentSelectedPlayer.integer >= 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) {
00144         cg_currentSelectedPlayer.integer++;
00145     } else {
00146         cg_currentSelectedPlayer.integer = 0;
00147     }
00148     CG_SetSelectedPlayerName();
00149 }
00150 
00151 void CG_SelectPrevPlayer() {
00152     CG_CheckOrderPending();
00153     if (cg_currentSelectedPlayer.integer > 0 && cg_currentSelectedPlayer.integer < numSortedTeamPlayers) {
00154         cg_currentSelectedPlayer.integer--;
00155     } else {
00156         cg_currentSelectedPlayer.integer = numSortedTeamPlayers;
00157     }
00158     CG_SetSelectedPlayerName();
00159 }
00160 
00161 
00162 static void CG_DrawPlayerArmorIcon( rectDef_t *rect, qboolean draw2D ) {
00163     centity_t   *cent;
00164     playerState_t   *ps;
00165     vec3_t      angles;
00166     vec3_t      origin;
00167 
00168   if ( cg_drawStatus.integer == 0 ) {
00169         return;
00170     }
00171 
00172     cent = &cg_entities[cg.snap->ps.clientNum];
00173     ps = &cg.snap->ps;
00174 
00175     if ( draw2D || ( !cg_draw3dIcons.integer && cg_drawIcons.integer) ) { // bk001206 - parentheses
00176         CG_DrawPic( rect->x, rect->y + rect->h/2 + 1, rect->w, rect->h, cgs.media.armorIcon );
00177   } else if (cg_draw3dIcons.integer) {
00178       VectorClear( angles );
00179     origin[0] = 90;
00180     origin[1] = 0;
00181     origin[2] = -10;
00182     angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
00183   
00184     CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cgs.media.armorModel, 0, origin, angles );
00185   }
00186 
00187 }
00188 
00189 static void CG_DrawPlayerArmorValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) {
00190     char    num[16];
00191   int value;
00192     centity_t   *cent;
00193     playerState_t   *ps;
00194 
00195   cent = &cg_entities[cg.snap->ps.clientNum];
00196     ps = &cg.snap->ps;
00197 
00198     value = ps->stats[STAT_ARMOR];
00199   
00200 
00201     if (shader) {
00202     trap_R_SetColor( color );
00203         CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00204       trap_R_SetColor( NULL );
00205     } else {
00206         Com_sprintf (num, sizeof(num), "%i", value);
00207         value = CG_Text_Width(num, scale, 0);
00208       CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00209     }
00210 }
00211 
00212 #ifndef MISSIONPACK // bk001206 
00213 static float healthColors[4][4] = { 
00214 //      { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} };
00215   // bk0101016 - float const
00216   { 1.0f, 0.69f, 0.0f, 1.0f } ,     // normal
00217   { 1.0f, 0.2f, 0.2f, 1.0f },       // low health
00218   { 0.5f, 0.5f, 0.5f, 1.0f},        // weapon firing
00219   { 1.0f, 1.0f, 1.0f, 1.0f } };     // health > 100
00220 #endif
00221 
00222 static void CG_DrawPlayerAmmoIcon( rectDef_t *rect, qboolean draw2D ) {
00223     centity_t   *cent;
00224     playerState_t   *ps;
00225     vec3_t      angles;
00226     vec3_t      origin;
00227 
00228     cent = &cg_entities[cg.snap->ps.clientNum];
00229     ps = &cg.snap->ps;
00230 
00231     if ( draw2D || (!cg_draw3dIcons.integer && cg_drawIcons.integer) ) { // bk001206 - parentheses
00232       qhandle_t icon;
00233     icon = cg_weapons[ cg.predictedPlayerState.weapon ].ammoIcon;
00234         if ( icon ) {
00235           CG_DrawPic( rect->x, rect->y, rect->w, rect->h, icon );
00236         }
00237   } else if (cg_draw3dIcons.integer) {
00238     if ( cent->currentState.weapon && cg_weapons[ cent->currentState.weapon ].ammoModel ) {
00239         VectorClear( angles );
00240         origin[0] = 70;
00241         origin[1] = 0;
00242         origin[2] = 0;
00243         angles[YAW] = 90 + 20 * sin( cg.time / 1000.0 );
00244         CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, cg_weapons[ cent->currentState.weapon ].ammoModel, 0, origin, angles );
00245     }
00246   }
00247 }
00248 
00249 static void CG_DrawPlayerAmmoValue(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle) {
00250     char    num[16];
00251     int value;
00252     centity_t   *cent;
00253     playerState_t   *ps;
00254 
00255     cent = &cg_entities[cg.snap->ps.clientNum];
00256     ps = &cg.snap->ps;
00257 
00258     if ( cent->currentState.weapon ) {
00259         value = ps->ammo[cent->currentState.weapon];
00260         if ( value > -1 ) {
00261             if (shader) {
00262             trap_R_SetColor( color );
00263                 CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00264               trap_R_SetColor( NULL );
00265             } else {
00266                 Com_sprintf (num, sizeof(num), "%i", value);
00267                 value = CG_Text_Width(num, scale, 0);
00268                 CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00269             }
00270         }
00271     }
00272 
00273 }
00274 
00275 
00276 
00277 static void CG_DrawPlayerHead(rectDef_t *rect, qboolean draw2D) {
00278     vec3_t      angles;
00279     float       size, stretch;
00280     float       frac;
00281     float       x = rect->x;
00282 
00283     VectorClear( angles );
00284 
00285     if ( cg.damageTime && cg.time - cg.damageTime < DAMAGE_TIME ) {
00286         frac = (float)(cg.time - cg.damageTime ) / DAMAGE_TIME;
00287         size = rect->w * 1.25 * ( 1.5 - frac * 0.5 );
00288 
00289         stretch = size - rect->w * 1.25;
00290         // kick in the direction of damage
00291         x -= stretch * 0.5 + cg.damageX * stretch * 0.5;
00292 
00293         cg.headStartYaw = 180 + cg.damageX * 45;
00294 
00295         cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
00296         cg.headEndPitch = 5 * cos( crandom()*M_PI );
00297 
00298         cg.headStartTime = cg.time;
00299         cg.headEndTime = cg.time + 100 + random() * 2000;
00300     } else {
00301         if ( cg.time >= cg.headEndTime ) {
00302             // select a new head angle
00303             cg.headStartYaw = cg.headEndYaw;
00304             cg.headStartPitch = cg.headEndPitch;
00305             cg.headStartTime = cg.headEndTime;
00306             cg.headEndTime = cg.time + 100 + random() * 2000;
00307 
00308             cg.headEndYaw = 180 + 20 * cos( crandom()*M_PI );
00309             cg.headEndPitch = 5 * cos( crandom()*M_PI );
00310         }
00311 
00312         size = rect->w * 1.25;
00313     }
00314 
00315     // if the server was frozen for a while we may have a bad head start time
00316     if ( cg.headStartTime > cg.time ) {
00317         cg.headStartTime = cg.time;
00318     }
00319 
00320     frac = ( cg.time - cg.headStartTime ) / (float)( cg.headEndTime - cg.headStartTime );
00321     frac = frac * frac * ( 3 - 2 * frac );
00322     angles[YAW] = cg.headStartYaw + ( cg.headEndYaw - cg.headStartYaw ) * frac;
00323     angles[PITCH] = cg.headStartPitch + ( cg.headEndPitch - cg.headStartPitch ) * frac;
00324 
00325     CG_DrawHead( x, rect->y, rect->w, rect->h, cg.snap->ps.clientNum, angles );
00326 }
00327 
00328 static void CG_DrawSelectedPlayerHealth( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00329     clientInfo_t *ci;
00330     int value;
00331     char num[16];
00332 
00333   ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00334   if (ci) {
00335         if (shader) {
00336             trap_R_SetColor( color );
00337             CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00338             trap_R_SetColor( NULL );
00339         } else {
00340             Com_sprintf (num, sizeof(num), "%i", ci->health);
00341           value = CG_Text_Width(num, scale, 0);
00342           CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00343         }
00344     }
00345 }
00346 
00347 static void CG_DrawSelectedPlayerArmor( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00348     clientInfo_t *ci;
00349     int value;
00350     char num[16];
00351   ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00352   if (ci) {
00353     if (ci->armor > 0) {
00354             if (shader) {
00355                 trap_R_SetColor( color );
00356                 CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00357                 trap_R_SetColor( NULL );
00358             } else {
00359                 Com_sprintf (num, sizeof(num), "%i", ci->armor);
00360                 value = CG_Text_Width(num, scale, 0);
00361                 CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00362             }
00363         }
00364     }
00365 }
00366 
00367 qhandle_t CG_StatusHandle(int task) {
00368     qhandle_t h = cgs.media.assaultShader;
00369     switch (task) {
00370         case TEAMTASK_OFFENSE :
00371             h = cgs.media.assaultShader;
00372             break;
00373         case TEAMTASK_DEFENSE :
00374             h = cgs.media.defendShader;
00375             break;
00376         case TEAMTASK_PATROL :
00377             h = cgs.media.patrolShader;
00378             break;
00379         case TEAMTASK_FOLLOW :
00380             h = cgs.media.followShader;
00381             break;
00382         case TEAMTASK_CAMP :
00383             h = cgs.media.campShader;
00384             break;
00385         case TEAMTASK_RETRIEVE :
00386             h = cgs.media.retrieveShader; 
00387             break;
00388         case TEAMTASK_ESCORT :
00389             h = cgs.media.escortShader; 
00390             break;
00391         default : 
00392             h = cgs.media.assaultShader;
00393             break;
00394     }
00395     return h;
00396 }
00397 
00398 static void CG_DrawSelectedPlayerStatus( rectDef_t *rect ) {
00399     clientInfo_t *ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00400     if (ci) {
00401         qhandle_t h;
00402         if (cgs.orderPending) {
00403             // blink the icon
00404             if ( cg.time > cgs.orderTime - 2500 && (cg.time >> 9 ) & 1 ) {
00405                 return;
00406             }
00407             h = CG_StatusHandle(cgs.currentOrder);
00408         }   else {
00409             h = CG_StatusHandle(ci->teamTask);
00410         }
00411         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h );
00412     }
00413 }
00414 
00415 
00416 static void CG_DrawPlayerStatus( rectDef_t *rect ) {
00417     clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum];
00418     if (ci) {
00419         qhandle_t h = CG_StatusHandle(ci->teamTask);
00420         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, h);
00421     }
00422 }
00423 
00424 
00425 static void CG_DrawSelectedPlayerName( rectDef_t *rect, float scale, vec4_t color, qboolean voice, int textStyle) {
00426     clientInfo_t *ci;
00427   ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]);
00428   if (ci) {
00429     CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, ci->name, 0, 0, textStyle);
00430   }
00431 }
00432 
00433 static void CG_DrawSelectedPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle ) {
00434     clientInfo_t *ci;
00435   ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00436   if (ci) {
00437         const char *p = CG_ConfigString(CS_LOCATIONS + ci->location);
00438         if (!p || !*p) {
00439             p = "unknown";
00440         }
00441     CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle);
00442   }
00443 }
00444 
00445 static void CG_DrawPlayerLocation( rectDef_t *rect, float scale, vec4_t color, int textStyle  ) {
00446     clientInfo_t *ci = &cgs.clientinfo[cg.snap->ps.clientNum];
00447   if (ci) {
00448         const char *p = CG_ConfigString(CS_LOCATIONS + ci->location);
00449         if (!p || !*p) {
00450             p = "unknown";
00451         }
00452     CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, p, 0, 0, textStyle);
00453   }
00454 }
00455 
00456 
00457 
00458 static void CG_DrawSelectedPlayerWeapon( rectDef_t *rect ) {
00459     clientInfo_t *ci;
00460 
00461   ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00462   if (ci) {
00463       if ( cg_weapons[ci->curWeapon].weaponIcon ) {
00464         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_weapons[ci->curWeapon].weaponIcon );
00465         } else {
00466       CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader);
00467     }
00468   }
00469 }
00470 
00471 static void CG_DrawPlayerScore( rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00472   char num[16];
00473   int value = cg.snap->ps.persistant[PERS_SCORE];
00474 
00475     if (shader) {
00476         trap_R_SetColor( color );
00477         CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00478         trap_R_SetColor( NULL );
00479     } else {
00480         Com_sprintf (num, sizeof(num), "%i", value);
00481         value = CG_Text_Width(num, scale, 0);
00482       CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00483     }
00484 }
00485 
00486 static void CG_DrawPlayerItem( rectDef_t *rect, float scale, qboolean draw2D) {
00487     int     value;
00488   vec3_t origin, angles;
00489 
00490     value = cg.snap->ps.stats[STAT_HOLDABLE_ITEM];
00491     if ( value ) {
00492         CG_RegisterItemVisuals( value );
00493 
00494         if (qtrue) {
00495           CG_RegisterItemVisuals( value );
00496           CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon );
00497         } else {
00498             VectorClear( angles );
00499             origin[0] = 90;
00500         origin[1] = 0;
00501         origin[2] = -10;
00502         angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
00503             CG_Draw3DModel(rect->x, rect->y, rect->w, rect->h, cg_items[ value ].models[0], 0, origin, angles );
00504         }
00505     }
00506 
00507 }
00508 
00509 
00510 static void CG_DrawSelectedPlayerPowerup( rectDef_t *rect, qboolean draw2D ) {
00511     clientInfo_t *ci;
00512   int j;
00513   float x, y;
00514 
00515   ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00516   if (ci) {
00517     x = rect->x;
00518     y = rect->y;
00519 
00520         for (j = 0; j < PW_NUM_POWERUPS; j++) {
00521             if (ci->powerups & (1 << j)) {
00522                 gitem_t *item;
00523                 item = BG_FindItemForPowerup( j );
00524                 if (item) {
00525                   CG_DrawPic( x, y, rect->w, rect->h, trap_R_RegisterShader( item->icon ) );
00526                     x += 3;
00527                     y += 3;
00528           return;
00529                 }
00530             }
00531         }
00532 
00533   }
00534 }
00535 
00536 
00537 static void CG_DrawSelectedPlayerHead( rectDef_t *rect, qboolean draw2D, qboolean voice ) {
00538     clipHandle_t    cm;
00539     clientInfo_t    *ci;
00540     float           len;
00541     vec3_t          origin;
00542     vec3_t          mins, maxs, angles;
00543 
00544 
00545   ci = cgs.clientinfo + ((voice) ? cgs.currentVoiceClient : sortedTeamPlayers[CG_GetSelectedPlayer()]);
00546 
00547   if (ci) {
00548     if ( cg_draw3dIcons.integer ) {
00549         cm = ci->headModel;
00550         if ( !cm ) {
00551             return;
00552         }
00553 
00554         // offset the origin y and z to center the head
00555         trap_R_ModelBounds( cm, mins, maxs );
00556 
00557         origin[2] = -0.5 * ( mins[2] + maxs[2] );
00558         origin[1] = 0.5 * ( mins[1] + maxs[1] );
00559 
00560         // calculate distance so the head nearly fills the box
00561         // assume heads are taller than wide
00562         len = 0.7 * ( maxs[2] - mins[2] );      
00563         origin[0] = len / 0.268;    // len / tan( fov/2 )
00564 
00565         // allow per-model tweaking
00566         VectorAdd( origin, ci->headOffset, origin );
00567 
00568         angles[PITCH] = 0;
00569         angles[YAW] = 180;
00570         angles[ROLL] = 0;
00571     
00572       CG_Draw3DModel( rect->x, rect->y, rect->w, rect->h, ci->headModel, ci->headSkin, origin, angles );
00573     } else if ( cg_drawIcons.integer ) {
00574         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, ci->modelIcon );
00575     }
00576 
00577     // if they are deferred, draw a cross out
00578     if ( ci->deferred ) {
00579         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.deferShader );
00580     }
00581   }
00582 
00583 }
00584 
00585 
00586 static void CG_DrawPlayerHealth(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00587     playerState_t   *ps;
00588   int value;
00589     char    num[16];
00590 
00591     ps = &cg.snap->ps;
00592 
00593     value = ps->stats[STAT_HEALTH];
00594 
00595     if (shader) {
00596         trap_R_SetColor( color );
00597         CG_DrawPic(rect->x, rect->y, rect->w, rect->h, shader);
00598         trap_R_SetColor( NULL );
00599     } else {
00600         Com_sprintf (num, sizeof(num), "%i", value);
00601       value = CG_Text_Width(num, scale, 0);
00602       CG_Text_Paint(rect->x + (rect->w - value) / 2, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00603     }
00604 }
00605 
00606 
00607 static void CG_DrawRedScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00608     int value;
00609     char num[16];
00610     if ( cgs.scores1 == SCORE_NOT_PRESENT ) {
00611         Com_sprintf (num, sizeof(num), "-");
00612     }
00613     else {
00614         Com_sprintf (num, sizeof(num), "%i", cgs.scores1);
00615     }
00616     value = CG_Text_Width(num, scale, 0);
00617     CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00618 }
00619 
00620 static void CG_DrawBlueScore(rectDef_t *rect, float scale, vec4_t color, qhandle_t shader, int textStyle ) {
00621     int value;
00622     char num[16];
00623 
00624     if ( cgs.scores2 == SCORE_NOT_PRESENT ) {
00625         Com_sprintf (num, sizeof(num), "-");
00626     }
00627     else {
00628         Com_sprintf (num, sizeof(num), "%i", cgs.scores2);
00629     }
00630     value = CG_Text_Width(num, scale, 0);
00631     CG_Text_Paint(rect->x + rect->w - value, rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00632 }
00633 
00634 // FIXME: team name support
00635 static void CG_DrawRedName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) {
00636   CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_redTeamName.string , 0, 0, textStyle);
00637 }
00638 
00639 static void CG_DrawBlueName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) {
00640   CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cg_blueTeamName.string, 0, 0, textStyle);
00641 }
00642 
00643 static void CG_DrawBlueFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) {
00644   int i;
00645   for ( i = 0 ; i < cgs.maxclients ; i++ ) {
00646       if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED  && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) {
00647       CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle);
00648       return;
00649     }
00650   }
00651 }
00652 
00653 static void CG_DrawBlueFlagStatus(rectDef_t *rect, qhandle_t shader) {
00654     if (cgs.gametype != GT_CTF && cgs.gametype != GT_1FCTF) {
00655         if (cgs.gametype == GT_HARVESTER) {
00656           vec4_t color = {0, 0, 1, 1};
00657           trap_R_SetColor(color);
00658         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.blueCubeIcon );
00659           trap_R_SetColor(NULL);
00660         }
00661         return;
00662     }
00663   if (shader) {
00664         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
00665   } else {
00666       gitem_t *item = BG_FindItemForPowerup( PW_BLUEFLAG );
00667     if (item) {
00668           vec4_t color = {0, 0, 1, 1};
00669           trap_R_SetColor(color);
00670         if( cgs.blueflag >= 0 && cgs.blueflag <= 2 ) {
00671             CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.blueflag] );
00672             } else {
00673             CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] );
00674             }
00675           trap_R_SetColor(NULL);
00676       }
00677   }
00678 }
00679 
00680 static void CG_DrawBlueFlagHead(rectDef_t *rect) {
00681   int i;
00682   for ( i = 0 ; i < cgs.maxclients ; i++ ) {
00683       if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_RED  && cgs.clientinfo[i].powerups & ( 1<< PW_BLUEFLAG )) {
00684       vec3_t angles;
00685       VectorClear( angles );
00686           angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );;
00687       CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles );
00688       return;
00689     }
00690   }
00691 }
00692 
00693 static void CG_DrawRedFlagName(rectDef_t *rect, float scale, vec4_t color, int textStyle ) {
00694   int i;
00695   for ( i = 0 ; i < cgs.maxclients ; i++ ) {
00696       if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE  && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) {
00697       CG_Text_Paint(rect->x, rect->y + rect->h, scale, color, cgs.clientinfo[i].name, 0, 0, textStyle);
00698       return;
00699     }
00700   }
00701 }
00702 
00703 static void CG_DrawRedFlagStatus(rectDef_t *rect, qhandle_t shader) {
00704     if (cgs.gametype != GT_CTF && cgs.gametype != GT_1FCTF) {
00705         if (cgs.gametype == GT_HARVESTER) {
00706           vec4_t color = {1, 0, 0, 1};
00707           trap_R_SetColor(color);
00708         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.redCubeIcon );
00709           trap_R_SetColor(NULL);
00710         }
00711         return;
00712     }
00713   if (shader) {
00714         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
00715   } else {
00716       gitem_t *item = BG_FindItemForPowerup( PW_REDFLAG );
00717     if (item) {
00718           vec4_t color = {1, 0, 0, 1};
00719           trap_R_SetColor(color);
00720         if( cgs.redflag >= 0 && cgs.redflag <= 2) {
00721             CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[cgs.redflag] );
00722             } else {
00723             CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[0] );
00724             }
00725           trap_R_SetColor(NULL);
00726       }
00727   }
00728 }
00729 
00730 static void CG_DrawRedFlagHead(rectDef_t *rect) {
00731   int i;
00732   for ( i = 0 ; i < cgs.maxclients ; i++ ) {
00733       if ( cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_BLUE  && cgs.clientinfo[i].powerups & ( 1<< PW_REDFLAG )) {
00734       vec3_t angles;
00735       VectorClear( angles );
00736           angles[YAW] = 180 + 20 * sin( cg.time / 650.0 );;
00737       CG_DrawHead( rect->x, rect->y, rect->w, rect->h, 0,angles );
00738       return;
00739     }
00740   }
00741 }
00742 
00743 static void CG_HarvesterSkulls(rectDef_t *rect, float scale, vec4_t color, qboolean force2D, int textStyle ) {
00744     char num[16];
00745     vec3_t origin, angles;
00746     qhandle_t handle;
00747     int value = cg.snap->ps.generic1;
00748 
00749     if (cgs.gametype != GT_HARVESTER) {
00750         return;
00751     }
00752 
00753     if( value > 99 ) {
00754         value = 99;
00755     }
00756 
00757     Com_sprintf (num, sizeof(num), "%i", value);
00758     value = CG_Text_Width(num, scale, 0);
00759     CG_Text_Paint(rect->x + (rect->w - value), rect->y + rect->h, scale, color, num, 0, 0, textStyle);
00760 
00761     if (cg_drawIcons.integer) {
00762         if (!force2D && cg_draw3dIcons.integer) {
00763             VectorClear(angles);
00764             origin[0] = 90;
00765             origin[1] = 0;
00766             origin[2] = -10;
00767             angles[YAW] = ( cg.time & 2047 ) * 360 / 2048.0;
00768             if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
00769                 handle = cgs.media.redCubeModel;
00770             } else {
00771                 handle = cgs.media.blueCubeModel;
00772             }
00773             CG_Draw3DModel( rect->x, rect->y, 35, 35, handle, 0, origin, angles );
00774         } else {
00775             if( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) {
00776                 handle = cgs.media.redCubeIcon;
00777             } else {
00778                 handle = cgs.media.blueCubeIcon;
00779             }
00780             CG_DrawPic( rect->x + 3, rect->y + 16, 20, 20, handle );
00781         }
00782     }
00783 }
00784 
00785 static void CG_OneFlagStatus(rectDef_t *rect) {
00786     if (cgs.gametype != GT_1FCTF) {
00787         return;
00788     } else {
00789         gitem_t *item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
00790         if (item) {
00791             if( cgs.flagStatus >= 0 && cgs.flagStatus <= 4 ) {
00792                 vec4_t color = {1, 1, 1, 1};
00793                 int index = 0;
00794                 if (cgs.flagStatus == FLAG_TAKEN_RED) {
00795                     color[1] = color[2] = 0;
00796                     index = 1;
00797                 } else if (cgs.flagStatus == FLAG_TAKEN_BLUE) {
00798                     color[0] = color[1] = 0;
00799                     index = 1;
00800                 } else if (cgs.flagStatus == FLAG_DROPPED) {
00801                     index = 2;
00802                 }
00803               trap_R_SetColor(color);
00804                 CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cgs.media.flagShaders[index] );
00805             }
00806         }
00807     }
00808 }
00809 
00810 
00811 static void CG_DrawCTFPowerUp(rectDef_t *rect) {
00812     int     value;
00813 
00814     if (cgs.gametype < GT_CTF) {
00815         return;
00816     }
00817     value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP];
00818     if ( value ) {
00819         CG_RegisterItemVisuals( value );
00820         CG_DrawPic( rect->x, rect->y, rect->w, rect->h, cg_items[ value ].icon );
00821     }
00822 }
00823 
00824 
00825 
00826 static void CG_DrawTeamColor(rectDef_t *rect, vec4_t color) {
00827     CG_DrawTeamBackground(rect->x, rect->y, rect->w, rect->h, color[3], cg.snap->ps.persistant[PERS_TEAM]);
00828 }
00829 
00830 static void CG_DrawAreaPowerUp(rectDef_t *rect, int align, float special, float scale, vec4_t color) {
00831     char num[16];
00832     int     sorted[MAX_POWERUPS];
00833     int     sortedTime[MAX_POWERUPS];
00834     int     i, j, k;
00835     int     active;
00836     playerState_t   *ps;
00837     int     t;
00838     gitem_t *item;
00839     float   f;
00840     rectDef_t r2;
00841     float *inc;
00842     r2.x = rect->x;
00843     r2.y = rect->y;
00844     r2.w = rect->w;
00845     r2.h = rect->h;
00846 
00847     inc = (align == HUD_VERTICAL) ? &r2.y : &r2.x;
00848 
00849     ps = &cg.snap->ps;
00850 
00851     if ( ps->stats[STAT_HEALTH] <= 0 ) {
00852         return;
00853     }
00854 
00855     // sort the list by time remaining
00856     active = 0;
00857     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
00858         if ( !ps->powerups[ i ] ) {
00859             continue;
00860         }
00861         t = ps->powerups[ i ] - cg.time;
00862         // ZOID--don't draw if the power up has unlimited time (999 seconds)
00863         // This is true of the CTF flags
00864         if ( t <= 0 || t >= 999000) {
00865             continue;
00866         }
00867 
00868         // insert into the list
00869         for ( j = 0 ; j < active ; j++ ) {
00870             if ( sortedTime[j] >= t ) {
00871                 for ( k = active - 1 ; k >= j ; k-- ) {
00872                     sorted[k+1] = sorted[k];
00873                     sortedTime[k+1] = sortedTime[k];
00874                 }
00875                 break;
00876             }
00877         }
00878         sorted[j] = i;
00879         sortedTime[j] = t;
00880         active++;
00881     }
00882 
00883     // draw the icons and timers
00884     for ( i = 0 ; i < active ; i++ ) {
00885         item = BG_FindItemForPowerup( sorted[i] );
00886 
00887         if (item) {
00888             t = ps->powerups[ sorted[i] ];
00889             if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
00890                 trap_R_SetColor( NULL );
00891             } else {
00892                 vec4_t  modulate;
00893 
00894                 f = (float)( t - cg.time ) / POWERUP_BLINK_TIME;
00895                 f -= (int)f;
00896                 modulate[0] = modulate[1] = modulate[2] = modulate[3] = f;
00897                 trap_R_SetColor( modulate );
00898             }
00899 
00900             CG_DrawPic( r2.x, r2.y, r2.w * .75, r2.h, trap_R_RegisterShader( item->icon ) );
00901 
00902             Com_sprintf (num, sizeof(num), "%i", sortedTime[i] / 1000);
00903             CG_Text_Paint(r2.x + (r2.w * .75) + 3 , r2.y + r2.h, scale, color, num, 0, 0, 0);
00904             *inc += r2.w + special;
00905         }
00906 
00907     }
00908     trap_R_SetColor( NULL );
00909 
00910 }
00911 
00912 float CG_GetValue(int ownerDraw) {
00913     centity_t   *cent;
00914     clientInfo_t *ci;
00915     playerState_t   *ps;
00916 
00917   cent = &cg_entities[cg.snap->ps.clientNum];
00918     ps = &cg.snap->ps;
00919 
00920   switch (ownerDraw) {
00921   case CG_SELECTEDPLAYER_ARMOR:
00922     ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00923     return ci->armor;
00924     break;
00925   case CG_SELECTEDPLAYER_HEALTH:
00926     ci = cgs.clientinfo + sortedTeamPlayers[CG_GetSelectedPlayer()];
00927     return ci->health;
00928     break;
00929   case CG_PLAYER_ARMOR_VALUE:
00930         return ps->stats[STAT_ARMOR];
00931     break;
00932   case CG_PLAYER_AMMO_VALUE:
00933         if ( cent->currentState.weapon ) {
00934           return ps->ammo[cent->currentState.weapon];
00935         }
00936     break;
00937   case CG_PLAYER_SCORE:
00938       return cg.snap->ps.persistant[PERS_SCORE];
00939     break;
00940   case CG_PLAYER_HEALTH:
00941         return ps->stats[STAT_HEALTH];
00942     break;
00943   case CG_RED_SCORE:
00944         return cgs.scores1;
00945     break;
00946   case CG_BLUE_SCORE:
00947         return cgs.scores2;
00948     break;
00949   default:
00950     break;
00951   }
00952     return -1;
00953 }
00954 
00955 qboolean CG_OtherTeamHasFlag() {
00956     if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) {
00957         int team = cg.snap->ps.persistant[PERS_TEAM];
00958         if (cgs.gametype == GT_1FCTF) {
00959             if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_BLUE) {
00960                 return qtrue;
00961             } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_RED) {
00962                 return qtrue;
00963             } else {
00964                 return qfalse;
00965             }
00966         } else {
00967             if (team == TEAM_RED && cgs.redflag == FLAG_TAKEN) {
00968                 return qtrue;
00969             } else if (team == TEAM_BLUE && cgs.blueflag == FLAG_TAKEN) {
00970                 return qtrue;
00971             } else {
00972                 return qfalse;
00973             }
00974         }
00975     }
00976     return qfalse;
00977 }
00978 
00979 qboolean CG_YourTeamHasFlag() {
00980     if (cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF) {
00981         int team = cg.snap->ps.persistant[PERS_TEAM];
00982         if (cgs.gametype == GT_1FCTF) {
00983             if (team == TEAM_RED && cgs.flagStatus == FLAG_TAKEN_RED) {
00984                 return qtrue;
00985             } else if (team == TEAM_BLUE && cgs.flagStatus == FLAG_TAKEN_BLUE) {
00986                 return qtrue;
00987             } else {
00988                 return qfalse;
00989             }
00990         } else {
00991             if (team == TEAM_RED && cgs.blueflag == FLAG_TAKEN) {
00992                 return qtrue;
00993             } else if (team == TEAM_BLUE && cgs.redflag == FLAG_TAKEN) {
00994                 return qtrue;
00995             } else {
00996                 return qfalse;
00997             }
00998         }
00999     }
01000     return qfalse;
01001 }
01002 
01003 // THINKABOUTME: should these be exclusive or inclusive.. 
01004 // 
01005 qboolean CG_OwnerDrawVisible(int flags)