//----------------------------------------------------------------------------- // // $Id$ // //----------------------------------------------------------------------------- // // $Log: cg_draw.c,v $ // Revision 1.80 2007/01/26 21:17:21 makro // Made the 'vignetting' effect zoom-dependent // // Revision 1.79 2006/04/14 18:16:31 makro // no message // // Revision 1.78 2005/02/15 16:33:38 makro // Tons of updates (entity tree attachment system, UI vectors) // // Revision 1.77 2004/01/26 21:26:08 makro // no message // // Revision 1.76 2003/09/20 19:38:16 makro // Lens flares, what else ? // // Revision 1.75 2003/09/19 00:54:23 makro // Flares again // // Revision 1.74 2003/09/17 23:49:29 makro // Lens flares. Opendoor trigger_multiple fixes // // Revision 1.73 2003/07/30 16:05:46 makro // no message // // Revision 1.72 2003/03/28 22:25:10 makro // no message // // Revision 1.71 2003/03/28 10:36:02 jbravo // Tweaking the replacement system a bit. Reactionmale now the default model // // Revision 1.70 2003/03/09 21:30:38 jbravo // Adding unlagged. Still needs work. // // Revision 1.69 2002/10/30 21:24:47 jbravo // Minor helmet tweaking // // Revision 1.68 2002/10/26 22:03:42 jbravo // Made TeamDM work RQ3 style. // // Revision 1.67 2002/10/26 00:37:18 jbravo // New multiple item code and added PB support to the UI // // Revision 1.66 2002/08/30 06:23:57 niceass // disabled wallhack protection #2!!! // // Revision 1.65 2002/08/29 14:24:26 niceass // new wallhack thing // // Revision 1.64 2002/08/29 04:45:25 niceass // color changes for new outlined font // // Revision 1.63 2002/08/22 03:32:10 niceass // countdown code added // // Revision 1.62 2002/08/21 07:00:07 jbravo // Added CTB respawn queue and fixed game <-> cgame synch problem in CTB // // Revision 1.61 2002/08/07 04:46:20 niceass // ctb changes // // Revision 1.60 2002/07/22 06:31:32 niceass // cleaned up the powerup code // // Revision 1.59 2002/07/19 04:34:48 niceass // drawping fixed // // Revision 1.58 2002/07/16 04:31:18 niceass // I think I fixed cg_drawping // // Revision 1.57 2002/07/11 04:26:12 niceass // testing new cg_drawping code // // Revision 1.56 2002/07/09 05:44:08 niceass // ctb fixes // // Revision 1.55 2002/07/03 02:40:14 niceass // fix cg_drawping problem // // Revision 1.54 2002/06/29 21:58:54 niceass // big changes in cg_drawping // // Revision 1.53 2002/06/29 04:15:15 jbravo // CTF is now CTB. no weapons while the case is in hand other than pistol or knife // // Revision 1.52 2002/06/24 05:55:50 niceass // drawping // // Revision 1.51 2002/06/23 23:09:20 niceass // modified upper right scores // // Revision 1.50 2002/06/23 21:44:07 jbravo // Fixed shots fired stats for non TP modes and some cleanups // // Revision 1.49 2002/06/21 23:20:34 blaze // cg_rq3_overlaycrosshair when set to 1 will draw your non zoomed crosshair overtop of your ssg crosshair // // Revision 1.48 2002/06/20 21:25:06 slicer // Fixed issues with cg_drawCrosshair 0, for SSG Scope and teammates Names // // Revision 1.47 2002/06/17 04:59:50 niceass // redid the voting. Hope you like it. // // Revision 1.46 2002/06/16 20:06:13 jbravo // Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap" // // Revision 1.45 2002/06/16 19:12:52 jbravo // Removed the MISSIONPACK ifdefs and missionpack only code. // // Revision 1.44 2002/06/03 05:23:47 niceass // spectator changes // // Revision 1.43 2002/06/03 00:48:32 niceass // match scoreboard changes // // Revision 1.42 2002/05/19 18:32:11 jbravo // Made new cvars for regular xhair colors. // // Revision 1.41 2002/05/18 03:55:35 niceass // many misc. changes // // Revision 1.40 2002/05/13 05:24:54 jbravo // the ssg color cvars now also control normal xhair color // // Revision 1.39 2002/05/01 21:14:59 jbravo // Misc fixes // // Revision 1.38 2002/05/01 03:27:17 niceass // centerprint fix + prettier // // Revision 1.37 2002/04/29 06:10:48 niceass // better centerprint // // Revision 1.36 2002/04/29 01:24:34 niceass // frag counter added // // Revision 1.35 2002/04/23 00:21:44 jbravo // Cleanups of the new model code. Removed the spectator bar for zcam modes. // // Revision 1.34 2002/04/06 21:43:58 makro // New surfaceparm system // // Revision 1.33 2002/03/31 03:31:24 jbravo // Compiler warning cleanups // // Revision 1.32 2002/03/30 02:29:43 jbravo // Lots of spectator code updates. Removed debugshit, added some color. // // Revision 1.31 2002/03/24 22:50:52 niceass // misc. 2d screen stuff changed // // Revision 1.30 2002/03/23 05:17:42 jbravo // Major cleanup of game -> cgame communication with LCA vars. // // Revision 1.29 2002/03/21 15:02:05 jbravo // More teamname cleanups and fix for fraglines. // // Revision 1.28 2002/03/17 21:32:23 jbravo // Fixed the dynamic teamnames system up a bit to reduce traffic // // Revision 1.27 2002/03/17 13:18:14 jbravo // Replace the fraglimit on the HUD with the clients score // // Revision 1.26 2002/03/17 00:40:23 jbravo // Adding variable team names. g_RQ3_team1name and g_RQ3_team2name. Fixed // Slicers fraglimit check. // // Revision 1.25 2002/03/04 20:50:59 jbravo // No floating scores over dead bodies, triangles disabled, and no viewing // names of enemys just of teammates. // // Revision 1.24 2002/03/04 19:28:21 jbravo // Fixing follownames up as suggested in the forums. // // Revision 1.23 2002/03/02 14:54:24 jbravo // Added the skin and model names to the name of the player thats being // followed, as in AQ // // Revision 1.22 2002/02/28 05:41:54 blaze // weapons stats on client side // // Revision 1.21 2002/02/25 19:41:53 jbravo // Fixed the use ESC and join menu to join teams when dead players are // spectating in TP mode. // Tuned the autorespawn system a bit. Now dead ppl. are dead for a very // small time before they are made into spectators. // // Revision 1.20 2002/02/23 18:07:46 slicer // Changed Sniper code and Cam code // // Revision 1.19 2002/01/11 20:20:57 jbravo // Adding TP to main branch // // Revision 1.18 2002/01/11 19:48:29 jbravo // Formatted the source in non DOS format. // // Revision 1.17 2001/12/31 16:28:41 jbravo // I made a Booboo with the Log tag. // // //----------------------------------------------------------------------------- // Copyright (C) 1999-2000 Id Software, Inc. // // cg_draw.c -- draw all of the graphical elements during // active (after loading) gameplay #include "cg_local.h" // JBravo: warning fix void CG_DrawWeaponStats(void); int drawTeamOverlayModificationCount = -1; int sortedTeamPlayers[TEAM_MAXOVERLAY]; int numSortedTeamPlayers; char systemChat[256]; char teamChat1[256]; char teamChat2[256]; #define LAG_SAMPLES 128 typedef struct { int frameSamples[LAG_SAMPLES]; int frameCount; int snapshotFlags[LAG_SAMPLES]; int snapshotSamples[LAG_SAMPLES]; int snapshotCount; } lagometer_t; lagometer_t lagometer; /* ============== CG_DrawField Draws large numbers for status bar and powerups ============== */ static void CG_DrawField(int x, int y, int width, int value) { char num[16], *ptr; int l; int frame; if (width < 1) { return; } // draw number string if (width > 5) { width = 5; } switch (width) { case 1: value = value > 9 ? 9 : value; value = value < 0 ? 0 : value; break; case 2: value = value > 99 ? 99 : value; value = value < -9 ? -9 : value; break; case 3: value = value > 999 ? 999 : value; value = value < -99 ? -99 : value; break; case 4: value = value > 9999 ? 9999 : value; value = value < -999 ? -999 : value; break; } Com_sprintf(num, sizeof(num), "%i", value); l = strlen(num); if (l > width) l = width; x += 2 + CHAR_WIDTH * (width - l); ptr = num; while (*ptr && l) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr - '0'; CG_DrawPic(x, y, CHAR_WIDTH, CHAR_HEIGHT, cgs.media.numberShaders[frame]); x += CHAR_WIDTH; ptr++; l--; } } /* ================ CG_Draw3DModel ================ */ void CG_Draw3DModel(float x, float y, float w, float h, qhandle_t model, qhandle_t skin, vec3_t origin, vec3_t angles) { refdef_t refdef; refEntity_t ent; if (!cg_draw3dIcons.integer || !cg_drawIcons.integer) { return; } CG_AdjustFrom640(&x, &y, &w, &h); memset(&refdef, 0, sizeof(refdef)); memset(&ent, 0, sizeof(ent)); AnglesToAxis(angles, ent.axis); VectorCopy(origin, ent.origin); ent.hModel = model; ent.customSkin = skin; ent.renderfx = RF_NOSHADOW; // no stencil shadows refdef.rdflags = RDF_NOWORLDMODEL; AxisClear(refdef.viewaxis); refdef.fov_x = 30; refdef.fov_y = 30; refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.time = cg.time; trap_R_ClearScene(); trap_R_AddRefEntityToScene(&ent); trap_R_RenderScene(&refdef); } /* ================ CG_DrawHead Used for both the status bar and the scoreboard ================ */ void CG_DrawHead(float x, float y, float w, float h, int clientNum, vec3_t headAngles) { clipHandle_t cm; clientInfo_t *ci; float len; vec3_t origin; vec3_t mins, maxs; ci = &cgs.clientinfo[clientNum]; if (cg_draw3dIcons.integer) { cm = ci->headModel; if (!cm) { return; } // offset the origin y and z to center the head trap_R_ModelBounds(cm, mins, maxs); origin[2] = -0.5 * (mins[2] + maxs[2]); origin[1] = 0.5 * (mins[1] + maxs[1]); // calculate distance so the head nearly fills the box // assume heads are taller than wide len = 0.7 * (maxs[2] - mins[2]); origin[0] = len / 0.268; // len / tan( fov/2 ) // allow per-model tweaking VectorAdd(origin, ci->headOffset, origin); CG_Draw3DModel(x, y, w, h, ci->headModel, ci->headSkin, origin, headAngles); } else if (cg_drawIcons.integer) { CG_DrawPic(x, y, w, h, ci->modelIcon); } // if they are deferred, draw a cross out if (ci->deferred) { CG_DrawPic(x, y, w, h, cgs.media.deferShader); } } /* ================ CG_DrawFlagModel Used for both the status bar and the scoreboard ================ */ void CG_DrawFlagModel(float x, float y, float w, float h, int team, qboolean force2D) { qhandle_t cm; float len; vec3_t origin, angles; vec3_t mins, maxs; qhandle_t handle; if (!force2D && cg_draw3dIcons.integer) { VectorClear(angles); cm = cgs.media.redFlagModel; // offset the origin y and z to center the flag trap_R_ModelBounds(cm, mins, maxs); origin[2] = -0.5 * (mins[2] + maxs[2]); origin[1] = 0.5 * (mins[1] + maxs[1]); // calculate distance so the flag nearly fills the box // assume heads are taller than wide len = 0.5 * (maxs[2] - mins[2]); origin[0] = len / 0.268; // len / tan( fov/2 ) // angles[YAW] = 60 * sin(cg.time / 2000.0); angles[YAW] = cg.time / 8.0f; if (team == TEAM_RED) { handle = cgs.media.redFlagModel; } else if (team == TEAM_BLUE) { handle = cgs.media.blueFlagModel; } else if (team == TEAM_FREE) { handle = cgs.media.neutralFlagModel; } else { return; } CG_Draw3DModel(x, y, w, h, handle, 0, origin, angles); } else if (cg_drawIcons.integer) { gitem_t *item; if (team == TEAM_RED) { item = BG_FindItemForPowerup(PW_REDFLAG); } else if (team == TEAM_BLUE) { item = BG_FindItemForPowerup(PW_BLUEFLAG); } else if (team == TEAM_FREE) { item = BG_FindItemForPowerup(PW_NEUTRALFLAG); } else { return; } if (item) { CG_DrawPic(x, y, w, h, cg_items[ITEM_INDEX(item)].icon); } } } /* ================ CG_DrawTeamBackground ================ */ void CG_DrawTeamBackground(int x, int y, int w, int h, float alpha, int team) { vec4_t hcolor; hcolor[3] = alpha; if (team == TEAM_RED) { hcolor[0] = 1; hcolor[1] = 0; hcolor[2] = 0; } else if (team == TEAM_BLUE) { hcolor[0] = 0; hcolor[1] = 0; hcolor[2] = 1; } else { return; } trap_R_SetColor(hcolor); CG_DrawPic(x, y, w, h, cgs.media.teamStatusBar); trap_R_SetColor(NULL); } /* ================ CG_DrawStatusBar ================ */ static void CG_DrawStatusBar(void) { int style; centity_t *cent; playerState_t *ps; int value, max; vec4_t hcolor; qhandle_t hicon; qhandle_t icon; //Makro - added x and y for weapon drawing int i, x = cgs.screenXMax - SMICON_SIZE - 8, y = 400; //Makro - now using the same colors for both health and ammo static float colors[5][4] = { {1.0f, 1.0f, 1.0f, 1.0f}, // full green {1.0f, 1.0f, 0.0f, 1.0f}, // firing {0.7f, 0.7f, 0.7f, 1.0f}, // not maximum {0.8f, 0.0f, 0.0f, 1.0f}, // out of ammo {0.0f, 1.0f, 0.0f, 1.0f} //Makro - reloading }; //Makro - health colors static float hcolors[3][4] = { {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, {0.8f, 0.0f, 0.0f, 1.0f} }; cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; //Draw health value = ps->stats[STAT_HEALTH]; style = UI_LEFT | UI_DROPSHADOW; if (value <= 25) style |= UI_PULSE; //Elder: Need bandaging? if ((ps->stats[STAT_RQ3] & RQ3_BANDAGE_NEED) == RQ3_BANDAGE_NEED) hicon = cgs.media.rq3_healthicon2; else hicon = cgs.media.rq3_healthicon; //Makro - old code //Elder: dynamic health color ramps //Blends from green to yellow to red algebraically //100 - Green, 50 - Yellow, 25 - Red, 0 - Faded Red //Note: These formulas are clamped from 0.0 to 1.0 algebraically if (value > 50) { float frac = (value - 50) / 50.0f, ifrac = (1.0f - frac); hcolor[0] = frac * hcolors[0][0] + ifrac * hcolors[1][0]; hcolor[1] = frac * hcolors[0][1] + ifrac * hcolors[1][1]; hcolor[2] = frac * hcolors[0][2] + ifrac * hcolors[1][2]; hcolor[3] = frac * hcolors[0][3] + ifrac * hcolors[1][3]; } else { float frac = value / 50.0f, ifrac = (1.0f - frac); hcolor[0] = frac * hcolors[1][0] + ifrac * hcolors[2][0]; hcolor[1] = frac * hcolors[1][1] + ifrac * hcolors[2][1]; hcolor[2] = frac * hcolors[1][2] + ifrac * hcolors[2][2]; hcolor[3] = frac * hcolors[1][3] + ifrac * hcolors[2][3]; } CG_DrawPic(cgs.screenXMin + 8, 440, SMICON_SIZE, SMICON_SIZE, hicon); //CG_DrawStringExt(44, 444, va("%d", value), hcolor, qtrue, qtrue, 24, 24, 3); //UI_DrawProportionalString(44, 444, va("%d", value), style, hcolor); UI_DrawProportionalString(cgs.screenXMin + 40, 444, va("%d", value), style, hcolor); //Elder: Draw weapon ammo and clips style = UI_LEFT | UI_DROPSHADOW; /* icon = cg_weapons[ cg.predictedPlayerState.weapon ].weaponIcon; origin[0] = 40; origin[1] = 0; origin[2] = -30; if (icon) CG_DrawPic(152, 440, SMICON_SIZE, SMICON_SIZE, icon); */ icon = cg_weapons[cg.predictedPlayerState.weapon].ammoIcon; //Don't draw ammo icon if holding grenade or knife //if (icon && cg.predictedPlayerState.weapon != WP_KNIFE && cg.predictedPlayerState.weapon != WP_GRENADE) if (icon) //CG_DrawPic(252, 440, SMICON_SIZE, SMICON_SIZE, icon); CG_DrawPic(288, 440, SMICON_SIZE, SMICON_SIZE, icon); if (cent->currentState.weapon) { value = ps->ammo[cent->currentState.weapon]; // Select colour //Makro - change from white to yellow to red now, just like health display #if 0 if (cg.predictedPlayerState.weaponstate == WEAPON_FIRING && cg.predictedPlayerState.weaponTime > 100) color = 1; //Makro - added reloading check else if (cg.predictedPlayerState.weaponstate == WEAPON_RELOADING) color = 4; else if (ps->ammo[cent->currentState.weapon] == 0) color = 3; else if (ps->ammo[cent->currentState.weapon] < ClipAmountForAmmo(cent->currentState.weapon)) color = 2; else color = 0; if (value >= 0) UI_DrawProportionalString(200, 444, va("%d", value), style, colors[color]); #else //Makro - new code max = ClipAmountForAmmo(cent->currentState.weapon); if (value > (max / 2)) { float frac = (value - (max / 2.0f)) / (max / 2.0f), ifrac = (1.0f - frac); hcolor[0] = frac * hcolors[0][0] + ifrac * hcolors[1][0]; hcolor[1] = frac * hcolors[0][1] + ifrac * hcolors[1][1]; hcolor[2] = frac * hcolors[0][2] + ifrac * hcolors[1][2]; hcolor[3] = frac * hcolors[0][3] + ifrac * hcolors[1][3]; } else { float frac = value / (max/2.0f), ifrac = (1.0f - frac); hcolor[0] = frac * hcolors[1][0] + ifrac * hcolors[2][0]; hcolor[1] = frac * hcolors[1][1] + ifrac * hcolors[2][1]; hcolor[2] = frac * hcolors[1][2] + ifrac * hcolors[2][2]; hcolor[3] = frac * hcolors[1][3] + ifrac * hcolors[2][3]; } if (value >= 0) { const char* str = va("%d", value); int width = UI_ProportionalStringWidth(str) * UI_ProportionalSizeScale(style); UI_DrawProportionalString(288 - width, 444, str, style, hcolor); } #endif //UI_DrawProportionalString(188, 444, "/"), style, colors[0]); value = ps->stats[STAT_CLIPS]; if (value > -1 && cg.predictedPlayerState.weapon != WP_KNIFE && cg.predictedPlayerState.weapon != WP_GRENADE) //Makro - pretty colours ! UI_DrawProportionalString(320, 444, va("%d", value), style, (value != 0) ? colors[0] : colors[3]); } // Elder: temporary //if (cg.snap->ps.stats[STAT_RELOADTIME] > 0) //UI_DrawProportionalString( 10, 400, va("%i", cg.snap->ps.stats[STAT_RELOADTIME]), style, colors[2]); //Elder: draw a special weapon, if any, on the side for (i = 1; i < MAX_WEAPONS; i++) { int flag = (1 << i); if (i == WP_KNIFE || i == WP_PISTOL || i == WP_GRENADE || i == WP_AKIMBO) continue; if (flag == (flag & cg.snap->ps.stats[STAT_WEAPONS])) { //Makro - old code //break; icon = cg_weapons[i].weaponIcon; if (icon) { CG_DrawPic(x, y, SMICON_SIZE, SMICON_SIZE, icon); y -= SMICON_SIZE; } } } //Elder: draw grenades, if any, on the side //Makro - changed the code a bit; now it's drawn above the special weapons if (cg.snap->ps.ammo[WP_GRENADE] > 0) { icon = cg_weapons[WP_GRENADE].weaponIcon; if (icon) CG_DrawPic(x, y, SMICON_SIZE, SMICON_SIZE, icon); UI_DrawProportionalString(x + 2, y + 3, va("%d", cg.snap->ps.ammo[WP_GRENADE]), UI_RIGHT | UI_DROPSHADOW, colors[0]); y -= SMICON_SIZE; } //Makro - old code /* if (i < MAX_WEAPONS) { icon = cg_weapons[i].weaponIcon; if (icon) CG_DrawPic(640 - SMICON_SIZE, 400, SMICON_SIZE, SMICON_SIZE, icon); } */ } /* =========================================================================================== UPPER RIGHT CORNER =========================================================================================== */ static float CG_DrawCTBCountDown(float y) { char *s; int w; int x = 0; float Color[4]; if ( cg.CTBcountdowntime - cg.time < 0 ) { cg.CTBcountdowntime = 0; return y; } y += 4; s = va("Respawn in %d", (int)ceil( (cg.CTBcountdowntime - cg.time) / 1000.0f) ); w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; x = w; MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f); CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, Color); MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f); CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, Color); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT + 4; } /* ================ CG_DrawAttacker ================ */ static float CG_DrawAttacker(float y) { int t; float size; vec3_t angles; const char *info; const char *name; int clientNum; if (cg.predictedPlayerState.stats[STAT_HEALTH] <= 0) { return y; } if (!cg.attackerTime) { return y; } clientNum = cg.predictedPlayerState.persistant[PERS_ATTACKER]; if (clientNum < 0 || clientNum >= MAX_CLIENTS || clientNum == cg.snap->ps.clientNum) { return y; } t = cg.time - cg.attackerTime; if (t > ATTACKER_HEAD_TIME) { cg.attackerTime = 0; return y; } size = ICON_SIZE * 1.25; angles[PITCH] = 0; angles[YAW] = 180; angles[ROLL] = 0; CG_DrawHead(cgs.screenXMax - size - 9, y, size, size, clientNum, angles); info = CG_ConfigString(CS_PLAYERS + clientNum); name = Info_ValueForKey(info, "n"); y += size; CG_DrawBigString(cgs.screenXMax - (Q_PrintStrlen(name) * BIGCHAR_WIDTH) * 9, y, name, 0.5); return y + BIGCHAR_HEIGHT + 2; } static float CG_DrawSpeed(float y) { char *s; int w; int x = 0; float Color[4]; vec3_t vel; VectorCopy( cg.snap->ps.velocity, vel ); vel[2] = 0.0f; y += 4; s = va("%3.0f UPS", VectorLength(vel) ); w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; x = w; MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f); CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, Color); MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f); CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, Color); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT + 4; } /* ================== CG_DrawSnapshot ================== */ static float CG_DrawSnapshot(float y) { char *s; int w; s = va("time:%i snap:%i cmd:%i", cg.snap->serverTime, cg.latestSnapshotNum, cgs.serverCommandSequence); w = CG_DrawStrlen(s) * BIGCHAR_WIDTH; CG_DrawBigString(635 - w, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } static float CG_DrawScore(float y) { char *s; int w, x = 0; float BColor[4], FColor[4]; int team; y += 4; if (cgs.gametype >= GT_TEAM) team = cg.snap->ps.persistant[PERS_SAVEDTEAM]; else team = cg.snap->ps.persistant[PERS_TEAM]; MAKERGBA(FColor, 0.0f, 0.0f, 0.0f, 1.0f); if (cgs.gametype >= GT_TEAM) { // The other team: if (team == TEAM_RED) { s = va("%i", cgs.scores2); // Blue CG_TeamColor(TEAM_BLUE, BColor); } else { s = va("%i", cgs.scores1); // Red CG_TeamColor(TEAM_RED, BColor); } BColor[3] = 0.4f; //MAKERGBA(BColor, 0.0f, 0.0f, 1.0f, 0.4f); w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; x = w; CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor); CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0f); // Your team: if (team == TEAM_RED) { s = va("%i", cgs.scores1); // Red CG_TeamColor(TEAM_RED, BColor); } else { s = va("%i", cgs.scores2); // Blue CG_TeamColor(TEAM_BLUE, BColor); } BColor[3] = 0.4f; w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; x += w + 9; CG_FillRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor); CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0f); } s = va("%i", cg.snap->ps.persistant[PERS_SCORE]); w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; if (cgs.gametype >= GT_TEAM) { x += w + 5; if (team == TEAM_SPECTATOR) x += 4; } else x = w; //MAKERGBA(BColor, 1.0f, 1.0f, 1.0f, 1.0f); //CG_FillRect(631 - x - 3, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, BColor); CG_DrawCleanRect(cgs.screenXMax - x - 12, y - 1, w + 6, SMALLCHAR_HEIGHT + 6, 1, FColor); MAKERGBA(FColor, 0.75f, 0.75f, 0.75f, 1.0f); CG_DrawStringExt(cgs.screenXMax - x - 9, y + 2, s, FColor, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0); //CG_DrawSmallString(631 - x, y + 2, s, 1.0F); return y + SMALLCHAR_HEIGHT + 4; } /* ================== CG_DrawFPSandPing ================== */ #define FPS_FRAMES 8 // NiceAss: Increased from 4 for a smoother average. #define PING_SNAPS 16 static float CG_DrawFPSandPing(float y) { char *sfps = NULL, *sping = NULL; int w; static int previousTimes[FPS_FRAMES]; static int index; int i, total, l; int fps; static int previous; int t, frameTime, x = 0, num = 0; float Color[4]; const int expand = 4; const int corner = 12; const float shadowAlpha = 0.75f; /*static int Pings[PING_SNAPS]; static int currentSnapshotNum; static int index2;*/ int avgping = 0; // don't use serverTime, because that will be drifting to // correct for internet lag changes, timescales, timedemos, etc t = trap_Milliseconds(); frameTime = t - previous; previous = t; y += 4; previousTimes[index % FPS_FRAMES] = frameTime; /*if (cg.latestSnapshotNum != currentSnapshotNum && cg.snap) { Pings[index2 % PING_SNAPS] = cg.snap->ping; currentSnapshotNum = cg.latestSnapshotNum; index2++; }*/ index++; if (index > FPS_FRAMES) { // average multiple frames together to smooth changes out a bit total = 0; for (i = 0; i < FPS_FRAMES; i++) { total += previousTimes[i]; } if (!total) { total = 1; } fps = 1000 * FPS_FRAMES / total; if (cg_drawFPS.integer) { sfps = va("%ifps", fps); w = CG_DrawStrlen(sfps) * SMALLCHAR_WIDTH; x = w; CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, y - 1 - expand, w + 6 + expand + expand, SMALLCHAR_HEIGHT + 6 + expand + expand, 12, shadowAlpha); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, sfps, 1.0F); x += 9; } } // Draw ping here: //Makro - not during demo playback if (cg_drawPing.integer && !cg.demoPlayback) { for (i = 0; i < (LAG_SAMPLES / 2); i++) { l = (lagometer.frameCount & (LAG_SAMPLES - 1)) - i; if (l < 0) l += LAG_SAMPLES; if (lagometer.snapshotSamples[l] >= 0) { avgping += lagometer.snapshotSamples[l]; num++; } } if (num) avgping /= num; else avgping = 0; sping = va("%ims", avgping); w = CG_DrawStrlen(sping) * SMALLCHAR_WIDTH; x += w; l = (lagometer.frameCount & (LAG_SAMPLES - 1)) - 1; if (l < 0) l += LAG_SAMPLES; CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, y - 1 - expand, w + 6 + expand + expand, SMALLCHAR_HEIGHT + 6 + expand + expand, corner, shadowAlpha); MAKERGBA(Color, 0.0f, 1.0f, 0.0f, 1.0f); // Green, All good if (lagometer.snapshotSamples[l] < 0) MAKERGBA(Color, 1.0f, 0.0f, 0.0f, 1.0f); // Red. Missed packet if (lagometer.snapshotFlags[l] & SNAPFLAG_RATE_DELAYED) // Yellow. Delayed packet MAKERGBA(Color, 1.0f, 1.0f, 0.0f, 1.0f); CG_DrawStringExt(cgs.screenXMax - x - 9, y + 2, sping, Color, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0); } if (!cg_drawFPS.integer && (!cg_drawPing.integer || cg.demoPlayback)) return y; return y + SMALLCHAR_HEIGHT + 4; } /* ================= CG_DrawTimer ================= */ static float CG_DrawTimer(float y) { char *s = NULL; int w; int mins, seconds, tens; int msec; int x = 0; // float Color[4]; const int expand = 4; // const int corner = 12; const float shadowAlpha = 0.75f; y += 4; msec = cg.time - cgs.levelStartTime; seconds = msec / 1000; mins = seconds / 60; seconds -= mins * 60; tens = seconds / 10; seconds -= tens * 10; s = va("%i:%i%i", mins, tens, seconds); w = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; x = w; CG_DrawFuzzyShadow(cgs.screenXMax - x - 12 - expand, y - 1 - expand, w + 6 + expand + expand, SMALLCHAR_HEIGHT + 6 + expand + expand, 12, shadowAlpha ); CG_DrawSmallString(cgs.screenXMax - x - 9, y + 2, s, 1.0F); return y + BIGCHAR_HEIGHT + 4; } /* ================= CG_DrawTeamOverlay ================= */ static float CG_DrawTeamOverlay(float y, qboolean right, qboolean upper) { int x, w, h, xx; int i, j, len; const char *p; vec4_t hcolor; int pwidth, lwidth; int plyrs; char st[16]; clientInfo_t *ci; gitem_t *item; int ret_y, count; if (!cg_drawTeamOverlay.integer) { return y; } if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_TEAM] != TEAM_BLUE) { return y; // Not on any team } plyrs = 0; // max player name width pwidth = 0; count = (numSortedTeamPlayers > 8) ? 8 : numSortedTeamPlayers; for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if (ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { plyrs++; len = CG_DrawStrlen(ci->name); if (len > pwidth) pwidth = len; } } if (!plyrs) return y; if (pwidth > TEAM_OVERLAY_MAXNAME_WIDTH) pwidth = TEAM_OVERLAY_MAXNAME_WIDTH; // max location name width lwidth = 0; for (i = 1; i < MAX_LOCATIONS; i++) { p = CG_ConfigString(CS_LOCATIONS + i); if (p && *p) { len = CG_DrawStrlen(p); if (len > lwidth) lwidth = len; } } if (lwidth > TEAM_OVERLAY_MAXLOCATION_WIDTH) lwidth = TEAM_OVERLAY_MAXLOCATION_WIDTH; w = (pwidth + lwidth + 4 + 7) * TINYCHAR_WIDTH; if (right) x = cgs.screenXMax - w; else x = cgs.screenXMin; h = plyrs * TINYCHAR_HEIGHT; if (upper) { ret_y = y + h; } else { y -= h; ret_y = y; } CG_TeamColor(cg.snap->ps.persistant[PERS_TEAM], hcolor); hcolor[3] = 0.33f; trap_R_SetColor(hcolor); CG_DrawPic(x, y, w, h, cgs.media.teamStatusBar); trap_R_SetColor(NULL); for (i = 0; i < count; i++) { ci = cgs.clientinfo + sortedTeamPlayers[i]; if (ci->infoValid && ci->team == cg.snap->ps.persistant[PERS_TEAM]) { hcolor[0] = hcolor[1] = hcolor[2] = hcolor[3] = 1.0; xx = x + TINYCHAR_WIDTH; CG_DrawStringExt(xx, y, ci->name, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXNAME_WIDTH); if (lwidth) { p = CG_ConfigString(CS_LOCATIONS + ci->location); if (!p || !*p) p = "unknown"; //len = CG_DrawStrlen(p); //if (len > lwidth) // len = lwidth; // xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth + // ((lwidth/2 - len/2) * TINYCHAR_WIDTH); xx = x + TINYCHAR_WIDTH * 2 + TINYCHAR_WIDTH * pwidth; CG_DrawStringExt(xx, y, p, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, TEAM_OVERLAY_MAXLOCATION_WIDTH); } CG_GetColorForHealth(ci->health, ci->armor, hcolor); Com_sprintf(st, sizeof(st), "%3i %3i", ci->health, ci->armor); xx = x + TINYCHAR_WIDTH * 3 + TINYCHAR_WIDTH * pwidth + TINYCHAR_WIDTH * lwidth; CG_DrawStringExt(xx, y, st, hcolor, qfalse, qfalse, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0); // draw weapon icon xx += TINYCHAR_WIDTH * 3; if (cg_weapons[ci->curWeapon].weaponIcon) { CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cg_weapons[ci->curWeapon].weaponIcon); } else { CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, cgs.media.deferShader); } // Draw powerup icons if (right) { xx = x; } else { xx = x + w - TINYCHAR_WIDTH; } for (j = 0; j <= PW_NUM_POWERUPS; j++) { if (ci->powerups & (1 << j)) { item = BG_FindItemForPowerup(j); if (item) { CG_DrawPic(xx, y, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, trap_R_RegisterShader(item->icon)); if (right) { xx -= TINYCHAR_WIDTH; } else { xx += TINYCHAR_WIDTH; } } } } y += TINYCHAR_HEIGHT; } } return ret_y; //#endif } /* ===================== CG_DrawUpperRight ===================== */ static void CG_DrawUpperRight(void) { float y; y = 0; if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 1) y = CG_DrawTeamOverlay(y, qtrue, qtrue); if (cg_drawSnapshot.integer) y = CG_DrawSnapshot(y); y = CG_DrawScore(y); y = CG_DrawFPSandPing(y); if (cg_drawTimer.integer) y = CG_DrawTimer(y); if (cg.CTBcountdowntime) y = CG_DrawCTBCountDown(y); if (cg_drawAttacker.integer) y = CG_DrawAttacker(y); if (cg_RQ3_drawSpeed.integer) y = CG_DrawSpeed(y); } /* =========================================================================================== LOWER RIGHT CORNER =========================================================================================== */ /* ================ CG_DrawPowerups ================ */ static float CG_DrawPowerups(float y) { int sorted[MAX_POWERUPS]; int sortedTime[MAX_POWERUPS]; int i, j, k; int active; playerState_t *ps; int t; gitem_t *item; int x; int color; float size; float f; static float colors[2][4] = { {0.2f, 1.0f, 0.2f, 1.0f}, {1.0f, 0.2f, 0.2f, 1.0f} }; ps = &cg.snap->ps; if (ps->stats[STAT_HEALTH] <= 0) { return y; } // sort the list by time remaining active = 0; for (i = 0; i < MAX_POWERUPS; i++) { if (!ps->powerups[i]) { continue; } t = ps->powerups[i] - cg.time; // ZOID--don't draw if the power up has unlimited time (999 seconds) // This is true of the CTF flags if (t < 0 || t > 999000) { continue; } // insert into the list for (j = 0; j < active; j++) { if (sortedTime[j] >= t) { for (k = active - 1; k >= j; k--) { sorted[k + 1] = sorted[k]; sortedTime[k + 1] = sortedTime[k]; } break; } } sorted[j] = i; sortedTime[j] = t; active++; } // draw the icons and timers x = 640 - ICON_SIZE - CHAR_WIDTH * 2; for (i = 0; i < active; i++) { item = BG_FindItemForPowerup(sorted[i]); if (item) { color = 1; y -= ICON_SIZE; trap_R_SetColor(colors[color]); CG_DrawField(x, y, 2, sortedTime[i] / 1000); t = ps->powerups[sorted[i]]; if (t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME) { trap_R_SetColor(NULL); } else { vec4_t modulate; f = (float) (t - cg.time) / POWERUP_BLINK_TIME; f -= (int) f; modulate[0] = modulate[1] = modulate[2] = modulate[3] = f; trap_R_SetColor(modulate); } if (cg.powerupActive == sorted[i] && cg.time - cg.powerupTime < PULSE_TIME) { f = 1.0 - (((float) cg.time - cg.powerupTime) / PULSE_TIME); size = ICON_SIZE * (1.0 + (PULSE_SCALE - 1.0) * f); } else { size = ICON_SIZE; } CG_DrawPic(640 - size, y + ICON_SIZE / 2 - size / 2, size, size, trap_R_RegisterShader(item->icon)); } } trap_R_SetColor(NULL); return y; } /* ===================== CG_DrawLowerRight ===================== */ static void CG_DrawLowerRight(void) { float y; //y = 480 - ICON_SIZE; y = 320 + SMICON_SIZE; if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 2) { y = CG_DrawTeamOverlay(y, qtrue, qfalse); } // NiceAss: Taken out. Kinda ugly. I'll probably recode this later.... // y = CG_DrawScores( y ); y = CG_DrawPowerups(y); } /* =================== CG_DrawPickupItem =================== */ static int CG_DrawPickupItem(int y) { int value; float *fadeColor; if (cg.snap->ps.stats[STAT_HEALTH] <= 0) { return y; } value = cg.itemPickup; if (value) { fadeColor = CG_FadeColor(cg.itemPickupTime, 3000); if (fadeColor) { CG_RegisterItemVisuals(value); trap_R_SetColor(fadeColor); y -= ICON_SIZE; CG_DrawPic(cgs.screenXMin + 8, y, ICON_SIZE, ICON_SIZE, cg_items[value].icon); CG_DrawBigString(cgs.screenXMin + ICON_SIZE + 16, y + (ICON_SIZE / 2 - BIGCHAR_HEIGHT / 2), bg_itemlist[value].pickup_name, fadeColor[0]); trap_R_SetColor(NULL); } } return y; } /* ================= CG_DrawMessageQueue ================= */ static float CG_DrawMessageQueue(float y) { int w, h; int i, len; vec4_t hcolor; int chatHeight; int maxtime = 0; float div; #define CHATLOC_Y y #define CHATLOC_X cgs.screenXMin if (!cg_messageQueue.integer || cg_messageQueueTime.integer <= 0) return y; chatHeight = MSGQUEUE_HEIGHT; while (cgs.teamLastChatPos < cgs.teamChatPos && cg.time - cgs.teamChatMsgTimes[cgs.teamLastChatPos % chatHeight] > cg_messageQueueTime.integer) cgs.teamLastChatPos++; if (cgs.teamLastChatPos == cgs.teamChatPos) return y; y -= 32; h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT; w = 0; div = 1.f / cg_messageQueueTime.integer; for (i = cgs.teamLastChatPos; i < cgs.teamChatPos; i++) { int index = i % chatHeight; len = CG_DrawStrlen(cgs.teamChatMsgs[index]); if (len > w) w = len; if (cgs.teamChatMsgTimes[index] > maxtime) maxtime = cgs.teamChatMsgTimes[index]; } w *= TINYCHAR_WIDTH; w += TINYCHAR_WIDTH * 2; { float frac = SmoothLerp(Com_Clamp(0.f, 1.f, 8.f * (1.f - (cg.time - maxtime) * div))); const int expand = 8; CG_DrawFuzzyShadow(CHATLOC_X - expand, CHATLOC_Y - h + TINYCHAR_HEIGHT - expand, w + expand + expand, h + expand + expand, 12.f, 0.5f * frac); } hcolor[0] = hcolor[1] = hcolor[2] = 1.0f; hcolor[3] = 1.0f; for (i = cgs.teamChatPos - 1; i >= cgs.teamLastChatPos; i--) { int index = i % chatHeight; float frac = SmoothLerp(Com_Clamp(0.f, 1.f, 8.f * (1.f - (cg.time - cgs.teamChatMsgTimes[index]) * div))); hcolor[3] = frac; CG_DrawStringExt(CHATLOC_X + TINYCHAR_WIDTH, CHATLOC_Y, cgs.teamChatMsgs[index], hcolor, qfalse, qtrue, TINYCHAR_WIDTH, TINYCHAR_HEIGHT, 0); y -= TINYCHAR_HEIGHT; } return y; } /* ===================== CG_DrawLowerLeft ===================== */ static void CG_DrawLowerLeft(void) { float y; y = 480 - ICON_SIZE; if (cgs.gametype >= GT_TEAM && cg_drawTeamOverlay.integer == 3) { y = CG_DrawTeamOverlay(y, qfalse, qfalse); } y = CG_DrawMessageQueue(y); y = CG_DrawPickupItem(y); } //=========================================================================================== /* =================== CG_DrawHoldableItem =================== */ static void CG_DrawHoldableItem(void) { int value, item; item = 0; // JBravo: new items code. The order here is the same as in g_active so you // drop the same item that you see on your HUD. if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_BANDOLIER)) item = HI_BANDOLIER; else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_SLIPPERS)) item = HI_SLIPPERS; else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_SILENCER)) item = HI_SILENCER; else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_HELMET)) item = HI_HELMET; else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_LASER)) item = HI_LASER; else if (cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_KEVLAR)) item = HI_KEVLAR; if (item) { value = BG_FindItemForHoldable(item) - bg_itemlist; CG_RegisterItemVisuals(value); CG_DrawPic(cgs.screenXMax - SMICON_SIZE - 8, 440, SMICON_SIZE, SMICON_SIZE, cg_items[value].icon); } } /* =================== CG_DrawReward =================== */ static void CG_DrawReward(void) { float *color; int i, count; float x, y; char buf[32]; if (!cg_drawRewards.integer) { return; } color = CG_FadeColor(cg.rewardTime, REWARD_TIME); if (!color) { if (cg.rewardStack > 0) { for (i = 0; i < cg.rewardStack; i++) { cg.rewardSound[i] = cg.rewardSound[i + 1]; cg.rewardShader[i] = cg.rewardShader[i + 1]; cg.rewardCount[i] = cg.rewardCount[i + 1]; } cg.rewardTime = cg.time; cg.rewardStack--; color = CG_FadeColor(cg.rewardTime, REWARD_TIME); trap_S_StartLocalSound(cg.rewardSound[0], CHAN_ANNOUNCER); } else { return; } } trap_R_SetColor(color); /* count = cg.rewardCount[0]/10; // number of big rewards to draw if (count) { y = 4; x = 320 - count * ICON_SIZE; for ( i = 0 ; i < count ; i++ ) { CG_DrawPic( x, y, (ICON_SIZE*2)-4, (ICON_SIZE*2)-4, cg.rewardShader[0] ); x += (ICON_SIZE*2); } } count = cg.rewardCount[0] - count*10; // number of small rewards to draw */ if (cg.rewardCount[0] >= 10) { y = 56; x = 320 - ICON_SIZE / 2; CG_DrawPic(x, y, ICON_SIZE - 4, ICON_SIZE - 4, cg.rewardShader[0]); Com_sprintf(buf, sizeof(buf), "%d", cg.rewardCount[0]); x = (SCREEN_WIDTH - SMALLCHAR_WIDTH * CG_DrawStrlen(buf)) / 2; CG_DrawStringExt(x, y + ICON_SIZE, buf, color, qfalse, qtrue, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 0); } else { count = cg.rewardCount[0]; y = 56; x = 320 - count * ICON_SIZE / 2; for (i = 0; i < count; i++) { CG_DrawPic(x, y, ICON_SIZE - 4, ICON_SIZE - 4, cg.rewardShader[0]); x += ICON_SIZE; } } trap_R_SetColor(NULL); } /* =============================================================================== LAGOMETER =============================================================================== */ /* ============== CG_AddLagometerFrameInfo Adds the current interpolate / extrapolate bar for this frame ============== */ void CG_AddLagometerFrameInfo(void) { int offset; offset = cg.time - cg.latestSnapshotTime; lagometer.frameSamples[lagometer.frameCount & (LAG_SAMPLES - 1)] = offset; lagometer.frameCount++; } /* ============== CG_AddLagometerSnapshotInfo Each time a snapshot is received, log its ping time and the number of snapshots that were dropped before it. Pass NULL for a dropped packet. ============== */ void CG_AddLagometerSnapshotInfo(snapshot_t * snap) { // dropped packet if (!snap) { lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = -1; lagometer.snapshotCount++; return; } // add this snapshot's info lagometer.snapshotSamples[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->ping; lagometer.snapshotFlags[lagometer.snapshotCount & (LAG_SAMPLES - 1)] = snap->snapFlags; lagometer.snapshotCount++; } /* ============== CG_DrawDisconnect Should we draw something differnet for long lag vs no packets? ============== */ static void CG_DrawDisconnect(void) { float x, y; int cmdNum; usercmd_t cmd; const char *s; int w; // bk010215 - FIXME char message[1024]; // draw the phone jack if we are completely past our buffers cmdNum = trap_GetCurrentCmdNumber() - CMD_BACKUP + 1; trap_GetUserCmd(cmdNum, &cmd); if (cmd.serverTime <= cg.snap->ps.commandTime || cmd.serverTime > cg.time) { // special check for map_restart // bk 0102165 - FIXME return; } // also add text in center of screen s = "Connection Interrupted"; // bk 010215 - FIXME w = CG_DrawStrlen(s) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 100, s, 1.0F); // blink the icon if ((cg.time >> 9) & 1) { return; } //Elder: changed position x = 0; y = 0; //x = 640 - 48; //y = 480 - 48; CG_DrawPic(cgs.screenXMin + x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga")); } #define MAX_LAGOMETER_PING 900 #define MAX_LAGOMETER_RANGE 300 /* ============== CG_DrawLagometer ============== */ static void CG_DrawLagometer(void) { int a, x, y, i; float v; float ax, ay, aw, ah, mid, range; int color; float vscale; // JBravo: unlagged if (!cg_lagometer.integer /*|| cgs.localServer*/) { CG_DrawDisconnect(); return; } // // draw the graph // //Elder: changed position x = 0; y = 0; trap_R_SetColor(NULL); CG_DrawPic(cgs.screenXMin + x, y, 48, 48, cgs.media.lagometerShader); ax = cgs.screenXMin + x; ay = y; aw = 48; ah = 48; CG_AdjustFrom640(&ax, &ay, &aw, &ah); color = -1; range = ah / 3; mid = ay + range; vscale = range / MAX_LAGOMETER_RANGE; // draw the frame interpoalte / extrapolate graph for (a = 0; a < aw; a++) { i = (lagometer.frameCount - 1 - a) & (LAG_SAMPLES - 1); v = lagometer.frameSamples[i]; v *= vscale; if (v > 0) { if (color != 1) { color = 1; trap_R_SetColor(g_color_table[ColorIndex(COLOR_YELLOW)]); } if (v > range) { v = range; } trap_R_DrawStretchPic(ax + aw - a, mid - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader); } else if (v < 0) { if (color != 2) { color = 2; trap_R_SetColor(g_color_table[ColorIndex(COLOR_BLUE)]); } v = -v; if (v > range) { v = range; } trap_R_DrawStretchPic(ax + aw - a, mid, 1, v, 0, 0, 0, 0, cgs.media.whiteShader); } } // draw the snapshot latency / drop graph range = ah / 2; vscale = range / MAX_LAGOMETER_PING; for (a = 0; a < aw; a++) { i = (lagometer.snapshotCount - 1 - a) & (LAG_SAMPLES - 1); v = lagometer.snapshotSamples[i]; if (v > 0) { if (lagometer.snapshotFlags[i] & SNAPFLAG_RATE_DELAYED) { if (color != 5) { color = 5; // YELLOW for rate delay trap_R_SetColor(g_color_table[ColorIndex(COLOR_YELLOW)]); } } else { if (color != 3) { color = 3; trap_R_SetColor(g_color_table[ColorIndex(COLOR_GREEN)]); } } v = v * vscale; if (v > range) { v = range; } trap_R_DrawStretchPic(ax + aw - a, ay + ah - v, 1, v, 0, 0, 0, 0, cgs.media.whiteShader); } else if (v < 0) { if (color != 4) { color = 4; // RED for dropped snapshots trap_R_SetColor(g_color_table[ColorIndex(COLOR_RED)]); } trap_R_DrawStretchPic(ax + aw - a, ay + ah - range, 1, range, 0, 0, 0, 0, cgs.media.whiteShader); } } trap_R_SetColor(NULL); if (cg_nopredict.integer || cg_synchronousClients.integer) { CG_DrawBigString(x, y, "snc", 1.0); } CG_DrawDisconnect(); } /* =============================================================================== CENTER PRINTING =============================================================================== */ /* ============== CG_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void CG_CenterPrint(const char *str, int y, int charWidth) { char *s; int length = 0; Q_strncpyz(cg.centerPrint, str, sizeof(cg.centerPrint)); cg.centerPrintTime = cg.time; cg.centerPrintY = y; cg.centerPrintCharWidth = charWidth; cg.centerPrintMaxLen = 0; // count the number of lines for centering cg.centerPrintLines = 1; s = cg.centerPrint; while (*s) { if (*s == '\n') { cg.centerPrintLines++; if (length > cg.centerPrintMaxLen) cg.centerPrintMaxLen = length; length = 0; } else { length++; } s++; } if (cg.centerPrintMaxLen == 0) cg.centerPrintMaxLen = CG_DrawStrlen(str); // Last character a linefeed if (*(s - 1) == '\n') cg.centerPrintLines--; } /* =================== CG_DrawCenterString =================== */ static void CG_DrawCenterString(void) { char *start; int l; int x, y, w; float *color, color2[4]; int windowHeight; if (!cg.centerPrintTime) { return; } color = CG_FadeColor(cg.centerPrintTime, 1000 * cg_centertime.value); if (!color) { return; } trap_R_SetColor(color); start = cg.centerPrint; windowHeight = cg.centerPrintLines * (int) (cg.centerPrintCharWidth * 1.5); y = cg.centerPrintY - windowHeight / 2; if (cg.centerPrint[0] != '\n') { MAKERGBA(color2, 0.0f, 0.0f, 0.0f, 0.4f * color[3]); CG_FillRect(320 - (cg.centerPrintMaxLen * cg.centerPrintCharWidth) * 0.5f - 3, y - 3, cg.centerPrintCharWidth * cg.centerPrintMaxLen + 6, windowHeight + 6, color2); MAKERGBA(color2, 0.0f, 0.0f, 0.0f, 1.0f * color[3]); CG_DrawCleanRect(320 - (cg.centerPrintMaxLen * cg.centerPrintCharWidth) * 0.5f - 3, y - 3, cg.centerPrintCharWidth * cg.centerPrintMaxLen + 6, windowHeight + 6, 1, color2); } while (1) { char linebuffer[1024]; for (l = 0; l < 50; l++) { if (!start[l] || start[l] == '\n') { break; } linebuffer[l] = start[l]; } linebuffer[l] = 0; w = cg.centerPrintCharWidth * CG_DrawStrlen(linebuffer); x = (SCREEN_WIDTH - w) / 2; CG_DrawStringExt(x, y, linebuffer, color, qfalse, qtrue, cg.centerPrintCharWidth, (int) (cg.centerPrintCharWidth * 1.5), 0); y += cg.centerPrintCharWidth * 1.5; while (*start && (*start != '\n')) { start++; } if (!*start) { break; } start++; } trap_R_SetColor(NULL); } /* ================================================================================ CROSSHAIR ================================================================================ */ static void CG_DrawVignetting(float scale) { if (cgs.media.zoomMask) { float sx = cgs.glconfig.vidWidth / (float) SCREEN_WIDTH; float sy = cgs.glconfig.vidHeight / (float) SCREEN_HEIGHT; float tex[4]; if (sx >= sy) { tex[0] = 0.f; tex[1] = 0.5f * (1.f - sy / sx); } else { tex[0] = 0.5f * (1.f - sx / sy); tex[1] = 0.f; } tex[2] = 1.f - tex[0]; tex[3] = 1.f - tex[1]; tex[0] = (tex[0] - 0.5f) * scale + 0.5f; tex[1] = (tex[1] - 0.5f) * scale + 0.5f; tex[2] = (tex[2] - 0.5f) * scale + 0.5f; tex[3] = (tex[3] - 0.5f) * scale + 0.5f; trap_R_DrawStretchPic(0, 0, cg.refdef.width, cg.refdef.height, tex[0], tex[1], tex[2], tex[3], cgs.media.zoomMask); } } /* ================= CG_DrawCrosshair ================= */ static void CG_DrawCrosshair(void) { float w, h; qhandle_t hShader; float f; float x, y; int ca, i, drawSSG; vec4_t crosshairColor; //Slicer: HOW ABOUT SSG SCOPE !! GAHHH !!!!! - moved below //if (!cg_drawCrosshair.integer) { // return; //} //Slicer: Adding Crosshair to FOLLOW SPECS drawSSG = 0; if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && !(cg.snap->ps.pm_flags & PMF_FOLLOW)) { return; } if (cg.renderingThirdPerson) { return; } //Makro - moved some code below to prevent some variables from getting overwritten by the ssg code //Elder: Sniper crosshairs - lots of hardcoded values :/ //if ( cg.snap->ps.weapon==WP_SSG3000 && cg.zoomLevel > 0 && cg.zoomLevel < 4) { // some pile of crap // using SSG and zoomed in if (cg.snap->ps.weapon == WP_SSG3000 && ((cg.zoomLevel & RQ3_ZOOM_LOW) || (cg.zoomLevel & RQ3_ZOOM_MED))) { if ((cg.zoomFirstReturn == -1 || cg.snap->ps.weaponTime < ZOOM_TIME) && cg.snap->ps.stats[STAT_RELOADTIME] <= 0) { //Makro - wasn't initialized, caused a warning in MSVC int zoomMag = 0; x = SCREEN_WIDTH / 2; y = SCREEN_HEIGHT / 2; //derive zoom level if ((cg.zoomLevel & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW && (cg.zoomLevel & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) { zoomMag = 2; } else if ((cg.zoomLevel & RQ3_ZOOM_LOW) == RQ3_ZOOM_LOW) { zoomMag = 0; } else if ((cg.zoomLevel & RQ3_ZOOM_MED) == RQ3_ZOOM_MED) { zoomMag = 1; } else { //Shouldn't need to be here CG_Error("CG_DrawCrosshair: received no zoom value\n"); } //Elder: Setup crosshair colours crosshairColor[0] = cg_RQ3_ssgColorR.value; crosshairColor[1] = cg_RQ3_ssgColorG.value; crosshairColor[2] = cg_RQ3_ssgColorB.value; crosshairColor[3] = cg_RQ3_ssgColorA.value; //Clamp for (i = 0; i < 4; i++) { if (crosshairColor[i] > 1.0f) crosshairColor[i] = 1.0f; else if (crosshairColor[i] < 0) crosshairColor[i] = 0; } trap_R_SetColor(crosshairColor); //I can probably scale the zoom with the screen width -/+ keys //But I'll do it later. //Makro - h = half width, w = width h = cg_RQ3_ssgCrosshairSize.integer >> 1; w = cg_RQ3_ssgCrosshairSize.integer; if ( (hShader = cgs.media.ssgCrosshair[zoomMag]) ) { float dim = cg_RQ3_ssgCrosshairSize.integer * cg.refdef.height / SCREEN_HEIGHT; trap_R_DrawStretchPic((cg.refdef.width - dim) * 0.5f, (cg.refdef.height - dim) * 0.5f, dim, dim, 0.f, 0.f, 1.f, 1.f, hShader); } trap_R_SetColor(NULL); // vignetting { const float ZMC_NORMAL = 0.f; const float ZMC_MAX = 0.175f; float fov_frac = (cg.refdef.fov_x - 10.f) / (90.f - 10.f); float frac = 1.f - 2.f * (ZMC_NORMAL * fov_frac + ZMC_MAX * (1.f - fov_frac)); CG_DrawVignetting(frac); } drawSSG = 1; } } //Slicer if no crosshair, and not using SSG, dont draw crosshair if(!cg_drawCrosshair.integer) return; //Makro - this was above the SSG code // set color based on health if (cg_crosshairHealth.integer) { vec4_t hcolor; CG_ColorForHealth(hcolor); trap_R_SetColor(hcolor); } else { trap_R_SetColor(NULL); } w = h = cg_crosshairSize.value; // pulse the size of the crosshair when picking up items f = cg.time - cg.itemPickupBlendTime; if (f > 0 && f < ITEM_BLOB_TIME) { f /= ITEM_BLOB_TIME; w *= (1 + f); h *= (1 + f); } if (drawSSG == 0 || (drawSSG == 1 && cg_RQ3_overlaycrosshair.integer == 1)) { float half = w * 0.5f; x = cg_crosshairX.integer + SCREEN_WIDTH / 2; y = cg_crosshairY.integer + SCREEN_HEIGHT / 2; ca = cg_drawCrosshair.integer; if (ca < 0) { ca = 0; } hShader = cgs.media.crosshairShader[ca % NUM_CROSSHAIRS]; crosshairColor[0] = cg_RQ3_crosshairColorR.value; crosshairColor[1] = cg_RQ3_crosshairColorG.value; crosshairColor[2] = cg_RQ3_crosshairColorB.value; crosshairColor[3] = cg_RQ3_crosshairColorA.value; for (i = 0; i < 4; i++) { if (crosshairColor[i] > 1.0f) crosshairColor[i] = 1.0f; else if (crosshairColor[i] < 0) crosshairColor[i] = 0; } trap_R_SetColor(crosshairColor); CG_DrawPic(x - half, y - half, w, h, hShader); trap_R_SetColor(NULL); } } /* ================= CG_ScanForCrosshairEntity ================= */ static void CG_ScanForCrosshairEntity(void) { trace_t trace; vec3_t start, end; int content; VectorCopy(cg.refdef.vieworg, start); VectorMA(start, 131072, cg.refdef.viewaxis[0], end); CG_Trace(&trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, CONTENTS_SOLID | CONTENTS_BODY); if (trace.entityNum >= MAX_CLIENTS) { return; } // if the player is in fog, don't show it //content = trap_CM_PointContents(trace.endpos, 0); content = CG_PointContents( trace.endpos, 0 ); if (content & CONTENTS_FOG) { return; } // update the fade timer cg.crosshairClientNum = trace.entityNum; cg.crosshairClientTime = cg.time; } /* ===================== CG_DrawCrosshairNames ===================== */ static void CG_DrawCrosshairNames(void) { float *color; char *name; float w; //Slicer, on RQ3 it doesnt depend on crosshair /*if (!cg_drawCrosshair.integer) { return; }*/ if (!cg_drawCrosshairNames.integer) { return; } if (cg.renderingThirdPerson) { return; } // JBravo: no names for zcam users. if (cg.snap->ps.stats[STAT_RQ3] & RQ3_ZCAM) { return; } // scan the known entities to see if the crosshair is sighted on one CG_ScanForCrosshairEntity(); // draw the name of the player being looked at color = CG_FadeColor(cg.crosshairClientTime, 1000); if (!color) { trap_R_SetColor(NULL); return; } // JBravo: Lets not show player names of opponents if (cgs.clientinfo[cg.clientNum].team != cgs.clientinfo[cg.crosshairClientNum].team && cgs.gametype >= GT_TEAM) { return; } name = cgs.clientinfo[cg.crosshairClientNum].name; w = CG_DrawStrlen(name) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 170, name, color[3] * 0.5f); trap_R_SetColor(NULL); } //============================================================================== /* ================= CG_DrawSpectator ================= */ static void CG_DrawSpectator(void) { float Color[4]; int team; MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 0.4f); if (cgs.gametype >= GT_TEAM) { team = (cgs.gametype == GT_TEAMPLAY) ? cg.snap->ps.persistant[PERS_SAVEDTEAM] : cg.snap->ps.persistant[PERS_TEAM]; CG_TeamColor(team, Color); Color[0] *= 0.7f; Color[1] *= 0.7f; Color[2] *= 0.7f; Color[3] = 0.3f; } CG_FillRect(cgs.screenXMin, 420, cgs.screenWidth, 60, Color); MAKERGBA(Color, 0.0f, 0.0f, 0.0f, 1.0f); CG_DrawCleanLine(cgs.screenXMin, 420, cgs.screenWidth, 1, Color); if (cgs.gametype == GT_CTF && (cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_RED || cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_BLUE)) CG_DrawBigString(320 - 26 * 8, 425, "Waiting for a Team Respawn", 1.0F); else CG_DrawBigString(320 - 10 * 8, 425, "Spectating", 1.0F); if (cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_RED || cg.snap->ps.persistant[PERS_SAVEDTEAM] == TEAM_BLUE) return; if (cgs.gametype == GT_TOURNAMENT) { CG_DrawBigString(320 - 15 * 8, 455, "Waiting to play...", 1.0F); } else if (cgs.gametype >= GT_TEAM) { CG_DrawBigString(320 - 19 * 8, 455, "Join a team to play", 1.0F); } } /* ================= CG_DrawVote ================= */ static void CG_DrawVote(void) { const char *s; int len; int sec, y = 58; vec4_t color1; float xmin; int lines = 3; int offset = 0; float alpha = 1.f; const int previewWidth = 64; const int previewHeight = 48; int height; if (!cgs.voteTime) { return; } // play a talk beep whenever it is modified if (cgs.voteModified) { cgs.voteModified = qfalse; trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND); } sec = (VOTE_TIME - (cg.time - cgs.voteTime) + 999) / 1000; if (sec < 0) { sec = 0; } # define VOTE_FMT(msg) msg "(%d)" if ((cg.snap && EF_VOTED == (cg.snap->ps.eFlags & EF_VOTED)) || !cgs.voteYesKey[0] || !cgs.voteNoKey[0]) { s = va( "Vote called: %s\n" VOTE_FMT("Yes") " " VOTE_FMT("No") "\n" "%d %s left\n", cgs.voteString, cgs.voteYes, cgs.voteNo, sec, sec == 1 ? "second" : "seconds" ); } else { # define KEY_FMT S_COLOR_CYAN "%s" S_COLOR_RESET s = va( "Vote called: %s\n" KEY_FMT "=" VOTE_FMT("Yes") " " KEY_FMT "=" VOTE_FMT("No") "\n" "%d %s left\n", cgs.voteString, cgs.voteYesKey, cgs.voteYes, cgs.voteNoKey, cgs.voteNo, sec, sec == 1 ? "second" : "seconds" ); # undef KEY_FMT } # undef VOTE_FMT len = CG_DrawStrlen(s) * SMALLCHAR_WIDTH; height = lines * SMALLCHAR_HEIGHT + 4; xmin = cgs.screenXMin + 4; if (cgs.media.voteMapShader) { offset += previewWidth + 4 + 4; if (height < previewHeight + 4 + 4) height = previewHeight + 4; } { int begin = cg.time - cgs.voteTime; int end = VOTE_TIME - begin; int min = begin < end ? begin : end; alpha = SmoothLerp(Com_Clamp(0.f, 1.f, min / (1000.f * 0.125f))); } // slide in // xmin -= (1.f - alpha) * (len + offset); { const int inflatex = 32; const int inflatey = 24; CG_DrawFuzzyShadow(xmin + 1 - inflatex, y - inflatey, len + 4 + offset + inflatex + inflatex, height + inflatey + inflatey, 32, 0.75f * alpha); } MAKERGBA(color1, 1.0f, 1.0f, 1.0f, alpha); if (cgs.media.voteMapShader) { CG_FillRect(xmin + 1, y, previewWidth + 4, previewHeight + 4, color1); trap_R_SetColor(color1); CG_DrawPic(xmin + 3, y + 2, previewWidth, previewHeight, cgs.media.voteMapShader); xmin += offset; } CG_DrawStringExt(xmin + 3, y+2, s, color1, qfalse, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 100); trap_R_SetColor(NULL); } /* ================= CG_DrawTeamVote ================= */ static void CG_DrawTeamVote(void) { char *s, *s2; int sec, cs_offset, y = 100; float Color1[4]; if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) cs_offset = 0; else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) cs_offset = 1; else return; if (!cgs.teamVoteTime[cs_offset]) { return; } // play a talk beep whenever it is modified if (cgs.teamVoteModified[cs_offset]) { cgs.teamVoteModified[cs_offset] = qfalse; trap_S_StartLocalSound(cgs.media.talkSound, CHAN_LOCAL_SOUND); } sec = (VOTE_TIME - (cg.time - cgs.teamVoteTime[cs_offset])) / 1000; if (sec < 0) { sec = 0; } /* s = va("TEAMVOTE(%i):%s yes:%i no:%i", sec, cgs.teamVoteString[cs_offset], cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset]); CG_DrawSmallString(0, 90, s, 1.0F); */ s = va("TeamVote[%i seconds left] Yes[%i] No[%i]", sec, cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[cs_offset]); s2 = va("Vote: %s", cgs.teamVoteString[cs_offset]); MAKERGBA(Color1, 0.0f, 0.0f, 0.0f, 0.4f); CG_FillRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s) * SMALLCHAR_WIDTH + 4, SMALLCHAR_HEIGHT + 4, Color1); CG_DrawCleanRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s) * SMALLCHAR_WIDTH + 4, SMALLCHAR_HEIGHT + 4, 1, colorBlack); CG_DrawStringExt(cgs.screenXMin + 3, y+2, s, colorWhite, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 100); y += SMALLCHAR_HEIGHT + 3; CG_FillRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s2) * SMALLCHAR_WIDTH + 4, SMALLCHAR_HEIGHT + 4, Color1); CG_DrawCleanRect(cgs.screenXMin + 1, y, CG_DrawStrlen(s2) * SMALLCHAR_WIDTH + 4, SMALLCHAR_HEIGHT + 4, 1, colorBlack); CG_DrawStringExt(cgs.screenXMin + 3, y + 2, s2, colorWhite, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, 100); } static qboolean CG_DrawScoreboard( void ) { return CG_DrawOldScoreboard(); } /* ================= CG_DrawIntermission ================= */ static void CG_DrawIntermission(void) { if (cgs.gametype == GT_SINGLE_PLAYER) { CG_DrawCenterString(); return; } cg.scoreFadeTime = cg.time; cg.scoreBoardShowing = CG_DrawScoreboard(); } /* ================= CG_DrawFollow ================= */ static qboolean CG_DrawFollow(void) { //Makro - char size #define DF_WIDTH 8 #define DF_HEIGHT 12 static unsigned int df_effect_time = 0; int time; static qboolean df_showmark = qfalse; float x; vec4_t color; const char *name; int team; char combinedName[512]; if (!(cg.snap->ps.pm_flags & PMF_FOLLOW)) { return qfalse; } color[0] = 0.75f; color[1] = 0.75f; color[2] = 0.75f; color[3] = 1.0f; // JBravo: if gametype >= team, append teamname to his name. if (cgs.gametype >= GT_TEAM) { team = cgs.clientinfo[cg.snap->ps.clientNum].team; //Makro - different look /* if (team == TEAM_RED) { Com_sprintf(combinedName, sizeof(combinedName), "%sFollowing%s %s%s/%s%s", S_COLOR_RED, S_COLOR_WHITE, cgs.clientinfo[cg.snap->ps.clientNum].name, S_COLOR_RED, S_COLOR_MAGENTA, cg_RQ3_team1name.string); } else { Com_sprintf(combinedName, sizeof(combinedName), "%sFollowing%s %s%s/%s%s", S_COLOR_RED, S_COLOR_WHITE, cgs.clientinfo[cg.snap->ps.clientNum].name, S_COLOR_RED, S_COLOR_MAGENTA, cg_RQ3_team2name.string); } x = 0.5 * (640 - BIGCHAR_WIDTH * CG_DrawStrlen(combinedName)); CG_DrawStringExt(x, 372, combinedName, color, qfalse, qtrue, BIGCHAR_WIDTH, BIGCHAR_HEIGHT, 0); */ time = trap_Milliseconds(); if (time > df_effect_time) { df_effect_time = time + 500; df_showmark ^= qtrue; } if (df_showmark) { if (team == TEAM_RED) { Com_sprintf(combinedName, sizeof(combinedName), S_COLOR_YELLOW">"S_COLOR_RESET" Following ^7%s^* (^7%s^*) "S_COLOR_YELLOW"<", cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team1name.string); } else { Com_sprintf(combinedName, sizeof(combinedName), S_COLOR_YELLOW">"S_COLOR_RESET" Following ^7%s^* (^7%s^*) "S_COLOR_YELLOW"<", cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team2name.string); } } else { if (team == TEAM_RED) { Com_sprintf(combinedName, sizeof(combinedName), "Following ^7%s ^*(^7%s^*)", cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team1name.string); } else { Com_sprintf(combinedName, sizeof(combinedName), "Following ^7%s ^*(^7%s^*)", cgs.clientinfo[cg.snap->ps.clientNum].name, cg_RQ3_team2name.string); } } x = 0.5 * (640 - DF_WIDTH * CG_DrawStrlen(combinedName)); CG_DrawStringExt(x, 80, combinedName, color, qfalse, qfalse, DF_WIDTH, DF_HEIGHT, 0); } else { CG_DrawBigString(320 - 9 * 8, 24, "following", 1.0F); name = cgs.clientinfo[cg.snap->ps.clientNum].name; x = 0.5 * (640 - GIANT_WIDTH * CG_DrawStrlen(combinedName)); CG_DrawStringExt(x, 40, name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0); } return qtrue; } /* ================= CG_DrawAmmoWarning ================= */ /* JBravo: Not used static void CG_DrawAmmoWarning(void) { const char *s; // int w; if (cg_drawAmmoWarning.integer == 0) { return; } if (!cg.lowAmmoWarning) { return; } if (cg.lowAmmoWarning == 2) { s = "OUT OF AMMO"; } else { s = "LOW AMMO WARNING"; } CG_DrawStrlen(s) * BIGCHAR_WIDTH; //Elder: commented out for now //CG_DrawBigString(320 - w / 2, 64, s, 1.0F); } */ /* ================= CG_DrawWarmup ================= */ static void CG_DrawWarmup(void) { int w; int sec; int i; // float scale; clientInfo_t *ci1, *ci2; int cw; const char *s; sec = cg.warmup; if (!sec) { return; } if (sec < 0) { s = "Waiting for players"; w = CG_DrawStrlen(s) * BIGCHAR_WIDTH; CG_DrawBigString(320 - w / 2, 24, s, 1.0F); cg.warmupCount = 0; return; } if (cgs.gametype == GT_TOURNAMENT) { // find the two active players ci1 = NULL; ci2 = NULL; for (i = 0; i < cgs.maxclients; i++) { if (cgs.clientinfo[i].infoValid && cgs.clientinfo[i].team == TEAM_FREE) { if (!ci1) { ci1 = &cgs.clientinfo[i]; } else { ci2 = &cgs.clientinfo[i]; } } } if (ci1 && ci2) { s = va("%s vs %s", ci1->name, ci2->name); w = CG_DrawStrlen(s); if (w > 640 / GIANT_WIDTH) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt(320 - w * cw / 2, 20, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.5f), 0); } } else { if (cgs.gametype == GT_FFA) { s = "Free For All"; } else if (cgs.gametype == GT_TEAM) { s = "Team Deathmatch"; // JBravo adding teamplay } else if (cgs.gametype == GT_TEAMPLAY) { s = "RQ3 Teamplay"; // JBravo: Now known as CTB } else if (cgs.gametype == GT_CTF) { s = "Capture the Briefcase"; } else { s = ""; } w = CG_DrawStrlen(s); if (w > 640 / GIANT_WIDTH) { cw = 640 / w; } else { cw = GIANT_WIDTH; } CG_DrawStringExt(320 - w * cw / 2, 25, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.1f), 0); } sec = (sec - cg.time) / 1000; if (sec < 0) { cg.warmup = 0; sec = 0; } s = va("Starts in: %i", sec + 1); if (sec != cg.warmupCount) { cg.warmupCount = sec; switch (sec) { case 0: if (cg_RQ3_anouncer.integer == 1) trap_S_StartLocalSound(cgs.media.count1Sound, CHAN_ANNOUNCER); break; case 1: if (cg_RQ3_anouncer.integer == 1) trap_S_StartLocalSound(cgs.media.count2Sound, CHAN_ANNOUNCER); break; case 2: if (cg_RQ3_anouncer.integer == 1) trap_S_StartLocalSound(cgs.media.count3Sound, CHAN_ANNOUNCER); break; default: break; } } // scale = 0.45f; switch (cg.warmupCount) { case 0: cw = 28; // scale = 0.54f; break; case 1: cw = 24; // scale = 0.51f; break; case 2: cw = 20; // scale = 0.48f; break; default: cw = 16; // scale = 0.45f; break; } w = CG_DrawStrlen(s); CG_DrawStringExt(320 - w * cw / 2, 70, s, colorWhite, qfalse, qtrue, cw, (int) (cw * 1.5), 0); } //================================================================================== /* ================= CG_Draw2D ================= */ static void CG_Draw2D(void) { // if we are taking a levelshot for the menu, don't draw anything if (cg.levelShot) { return; } if (cg_draw2D.integer == 0) { return; } if (cg.snap->ps.pm_type == PM_INTERMISSION) { float y; CG_DrawIntermission(); // Makro - make sure chat messages still get drawn during intermission y = 480 - ICON_SIZE; y = CG_DrawMessageQueue(y); return; } /* if (cg.cameraMode) { return; } */ //Slicer: Adding HUD for follow spectating if ((cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && !(cg.snap->ps.pm_flags & PMF_FOLLOW)) || cg.predictedPlayerState.pm_type == PM_SPECTATOR) { //if (cg.snap->ps.persistant[PERS_SAVEDTEAM] != TEAM_RED && cg.snap->ps.persistant[PERS_SAVEDTEAM] != TEAM_BLUE) // cg.predictedPlayerState.pm_type == PM_SPECTATOR // JBravo: no spectator bar for zcam modes. if (!(cg.snap->ps.stats[STAT_RQ3] & RQ3_ZCAM)) { CG_DrawSpectator(); } CG_DrawCrosshair(); CG_DrawCrosshairNames(); } else { // don't draw any status if dead or the scoreboard is being explicitly shown // if ( !cg.showScores && cg.snap->ps.stats[STAT_HEALTH] > 0 ) { if (cg.snap->ps.stats[STAT_HEALTH] > 0) { CG_DrawCrosshair(); CG_DrawStatusBar(); // CG_DrawAmmoWarning(); CG_DrawCrosshairNames(); CG_DrawWeaponSelect(); CG_DrawHoldableItem(); CG_DrawReward(); } } CG_DrawVote(); CG_DrawTeamVote(); CG_DrawLagometer(); CG_DrawUpperRight(); CG_DrawLowerRight(); CG_DrawLowerLeft(); if (!CG_DrawFollow()) { CG_DrawWarmup(); } // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard(); if (!cg.scoreBoardShowing) { CG_DrawCenterString(); } if (cg.showWStats) CG_DrawWeaponStats(); } static void CG_DrawTourneyScoreboard( void ) { CG_DrawOldTourneyScoreboard(); } /* ===================== CG_DrawDamageBlend Elder: Does a fullscreen alpha blend like Quake 2 when hurt Makro - changed to 0..1 instead of 0/1 ===================== */ #define MAX_DAMAGE_ALPHA 0.75 #define MAX_BLEND_TIME 1500 static float CG_GetDamageBlendAlpha( void ) { float dmg, blend = Com_Clamp(0, 1, cg_RQ3_painblend.value); if (cg.rq3_blendTime <= 0.f) return 0.f; //Clamp blend time if (cg.rq3_blendTime > MAX_BLEND_TIME) cg.rq3_blendTime = MAX_BLEND_TIME; dmg = cg.rq3_trueDamage; dmg = blend * MAX_DAMAGE_ALPHA * (dmg / 100.0) * (1.0 - (cg.time - cg.damageTime) / cg.rq3_blendTime); dmg = Com_Clamp(0.f, MAX_DAMAGE_ALPHA, dmg); return dmg; } static void CG_DrawDamageBlend( void ) { float dmg, blend = Com_Clamp(0, 1, cg_RQ3_painblend.value); vec4_t damageColor; //CG_Printf("CG_DrawDamageBlend: trueDamage (%i)\n", cg.rq3_trueDamage); //Leave if no true damage, disabled, or ragepro if (!blend) return; if (!cg.rq3_trueDamage || cgs.glconfig.hardwareType == GLHW_RAGEPRO) return; //Clamp blend time if (cg.rq3_blendTime > MAX_BLEND_TIME) cg.rq3_blendTime = MAX_BLEND_TIME; //Reset if we've gone past our blendTime if (cg.time - cg.damageTime > cg.rq3_blendTime) { //cg.rq3_trueDamage = 0; cg.rq3_blendTime = 0; return; } VectorCopy(colorRed, damageColor); dmg = cg.rq3_trueDamage; //clamp at 100 health if (dmg > 100) dmg = 100; damageColor[3] = blend * MAX_DAMAGE_ALPHA * (dmg / 100.0) * (1.0 - (cg.time - cg.damageTime) / cg.rq3_blendTime); if (damageColor[3] > MAX_DAMAGE_ALPHA) damageColor[3] = MAX_DAMAGE_ALPHA; else if (damageColor[3] < 0) damageColor[3] = 0; CG_FillRect(cgs.screenXMin, 0, cgs.screenWidth, SCREEN_HEIGHT, damageColor); } /* ===================== CG_DrawDeathBlend Makro: fade to black after death ===================== */ static void CG_DrawDeathBlend( void ) { const float MAX_ALPHA = 0.875f; float delta; vec4_t color; if (!cg.snap || cg.snap->ps.stats[STAT_HEALTH] > 0) return; delta = (cg.time - cg.timeOfDeath) / 1000.f; VectorCopy(colorBlack, color); color[3] = MAX_ALPHA * (1.f - 1.f / (delta + 1.f)); trap_R_SetColor(color); trap_R_DrawStretchPic(0.f, 0.f, cg.refdef.width, cg.refdef.height, 0.f, 0.f, 1.f, 1.f, cgs.media.whiteShader); trap_R_SetColor(NULL); } /* ===================== CG_DrawIRBlend Elder: Small red tint Note: This sucks - causes 10fps drop on my system so don't use it ===================== // JBravo: apparently not used. */ /* static void CG_DrawIRBlend() { vec4_t irColor; if (bg_itemlist[cg.snap->ps.stats[STAT_HOLDABLE_ITEM]].giTag == HI_BANDOLIER && cg.rq3_irvision) { irColor[0] = 0; irColor[1] = 1.0f; irColor[2] = 0; irColor[3] = 0.1f; CG_FillRect(0,0,SCREEN_WIDTH, SCREEN_HEIGHT, irColor); } } */ // If zbuffer is disabled, this should make it impossible to see? // By NiceAss (which means it probably doesn't work) void CG_DrawBigPolygon(void) { vec3_t end, forward, right, up, fogColor = {0, 0, 0}; polyVert_t Corners[4]; float Dist = 2048;//12288; //Makro - added const char *info; int i; AngleVectors(cg.refdefViewAngles, forward, right, up); VectorMA(cg.refdef.vieworg, Dist, forward, end); VectorMA(end, -Dist, right, Corners[0].xyz); VectorMA(Corners[0].xyz, -Dist, up, Corners[0].xyz); Corners[0].st[0] = 0; Corners[0].st[1] = 0; VectorMA(end, Dist, right, Corners[1].xyz); VectorMA(Corners[1].xyz, -Dist, up, Corners[1].xyz); Corners[1].st[0] = 0; Corners[1].st[1] = 1; VectorMA(end, Dist, right, Corners[2].xyz); VectorMA(Corners[2].xyz, Dist, up, Corners[2].xyz); Corners[2].st[0] = 1; Corners[2].st[1] = 1; VectorMA(end, -Dist, right, Corners[3].xyz); VectorMA(Corners[3].xyz, Dist, up, Corners[3].xyz); Corners[3].st[0] = 1; Corners[3].st[1] = 0; //Makro info = CG_ConfigString(CS_FOGHULL); if (info) { if (info[0]) { fogColor[0] = atof(Info_ValueForKey(info, "r")); fogColor[1] = atof(Info_ValueForKey(info, "g")); fogColor[2] = atof(Info_ValueForKey(info, "b")); //CG_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, fogcolor); //Com_Printf("Fog color: %f %f %f\n", fogcolor[0], fogcolor[1], fogcolor[2]); } } for (i = 0; i < 4; i++) { Corners[i].modulate[0] = 255 * fogColor[0]; Corners[i].modulate[1] = 255 * fogColor[1]; Corners[i].modulate[2] = 255 * fogColor[2]; Corners[i].modulate[3] = 255; } // CS_FOGHULL trap_R_AddPolyToScene(cgs.media.blackHackShader, 4, Corners); } // Makro - this needs to be called after cg.waterTransitionTime and cg.inWaterLastFrame have been updated static float CG_GetUnderWaterFraction( void ) { const int WATER_ANIM_TIME = 500; // msec const int delta = cg.time - cg.waterTransitionTime; float frac = Com_Clamp(0.f, 1.f, delta / (float)WATER_ANIM_TIME); if (!cg.inWaterLastFrame) frac = 1.f - frac; return frac; } static qboolean CG_IsDead( void ) { return cg.snap && cg.snap->ps.stats[STAT_HEALTH] <= 0; } static qboolean CG_UsesIRVision( void ) { return cg.rq3_irvision && cg.snap && cg.snap->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_BANDOLIER); } static void CG_DrawIRVisionBlend( void ) { float xoffset, yoffset, xscale, yscale; if (!CG_UsesIRVision() || !cgs.media.irvision_overlay) return; xscale = cg.refdef.width / 256.f; yscale = cg.refdef.height / 256.f; xoffset = random() * xscale; yoffset = random() * yscale; CG_DrawVignetting(0.8125f); trap_R_SetColor(NULL); trap_R_DrawStretchPic(0.f, 0.f, cg.refdef.width, cg.refdef.height, xoffset, yoffset, xscale+xoffset, yscale+yoffset, cgs.media.irvision_overlay); } static void CG_SetupPostProcess( void ) { cg.refdefex.blurFactor = CG_GetDamageBlendAlpha(); cg.refdefex.blurFactor += CG_GetUnderWaterFraction(); if (CG_IsDead()) cg.refdefex.blurFactor += 1.f; if (CG_UsesIRVision()) cg.refdefex.blurFactor += 0.75f; if (trap_Key_GetCatcher() & KEYCATCH_UI) cg.refdefex.blurFactor += 1.f; cg.refdefex.blurFactor = Com_Clamp(0.f, 1.f, cg.refdefex.blurFactor); if (cg.refdefex.blurFactor > 0.f) { cg.refdef.rdflags |= RDF_EXTRA; } } /* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive(stereoFrame_t stereoView) { // optionally draw the info screen instead if (!cg.snap) { CG_DrawInformation(); return; } // optionally draw the tournement scoreboard instead if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR && (cg.snap->ps.pm_flags & PMF_SCOREBOARD)) { CG_DrawTourneyScoreboard(); return; } // clear around the rendered view if sized down CG_TileClear(); // NiceAss: Wallhack protection //if (cg.snap->ps.pm_type == PM_NORMAL) // CG_DrawBigPolygon(); //Makro - sun flare // CG_AddLensFlare(qtrue); CG_SetupPostProcess(); // draw 3D view trap_R_RenderScene(&cg.refdef); // restore original viewpoint if running stereo // if (separation != 0) { // VectorCopy(baseOrg, cg.refdef.vieworg); // } // Elder: draw damage blend CG_DrawDamageBlend(); //CG_DrawIRBlend(); CG_DrawDeathBlend(); CG_DrawIRVisionBlend(); // draw status bar and other floating elements CG_Draw2D(); //Makro - reflection particles // CG_AddLensFlare(qfalse); }