2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // sbar.c -- status bar code
26 #define STAT_MINUS 10 // num frame for '-' stats digit
27 cachepic_t *sb_nums[2][11];
28 cachepic_t *sb_colon, *sb_slash;
31 cachepic_t *sb_scorebar;
32 // AK only used by NEX
33 cachepic_t *sb_sbar_minimal;
34 cachepic_t *sb_sbar_overlay;
36 // AK changed the bound to 9
37 cachepic_t *sb_weapons[7][9]; // 0 is active, 1 is owned, 2-5 are flashes
38 cachepic_t *sb_ammo[4];
39 cachepic_t *sb_sigil[4];
40 cachepic_t *sb_armor[3];
41 cachepic_t *sb_items[32];
43 // 0-4 are based on health (in 20 increments)
44 // 0 is static, 1 is temporary animation
45 cachepic_t *sb_faces[5][2];
46 cachepic_t *sb_health; // GAME_NEXUIZ
48 cachepic_t *sb_face_invis;
49 cachepic_t *sb_face_quad;
50 cachepic_t *sb_face_invuln;
51 cachepic_t *sb_face_invis_invuln;
53 qboolean sb_showscores;
55 int sb_lines; // scan lines to draw
57 cachepic_t *rsb_invbar[2];
58 cachepic_t *rsb_weapons[5];
59 cachepic_t *rsb_items[2];
60 cachepic_t *rsb_ammo[3];
61 cachepic_t *rsb_teambord; // PGM 01/19/97 - team color border
63 //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
64 cachepic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes
65 //MED 01/04/97 added array to simplify weapon parsing
66 int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
67 //MED 01/04/97 added hipnotic items array
68 cachepic_t *hsb_items[2];
71 cachepic_t *somsb_health;
72 cachepic_t *somsb_ammo[4];
73 cachepic_t *somsb_armor[3];
75 cachepic_t *zymsb_crosshair_center;
76 cachepic_t *zymsb_crosshair_line;
77 cachepic_t *zymsb_crosshair_health;
78 cachepic_t *zymsb_crosshair_ammo;
79 cachepic_t *zymsb_crosshair_clip;
80 cachepic_t *zymsb_crosshair_background;
81 cachepic_t *zymsb_crosshair_left1;
82 cachepic_t *zymsb_crosshair_left2;
83 cachepic_t *zymsb_crosshair_right;
85 cachepic_t *sb_ranking;
86 cachepic_t *sb_complete;
88 cachepic_t *sb_finale;
90 cvar_t showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
91 cvar_t showspeed = {CVAR_SAVE, "showspeed", "0", "shows your current speed (qu per second); number selects unit: 1 = qups, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots"};
92 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
93 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
94 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
95 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
96 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
97 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
98 cvar_t sbar_hudselector = {CVAR_SAVE, "sbar_hudselector", "0", "selects which of the builtin hud layouts to use (meaning is somewhat dependent on gamemode, so nexuiz has a very different set of hud layouts than quake for example)"};
99 cvar_t sbar_miniscoreboard_size = {CVAR_SAVE, "sbar_miniscoreboard_size", "-1", "sets the size of the mini deathmatch overlay in items, or disables it when set to 0, or sets it to a sane default when set to -1"};
100 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
102 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
104 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
105 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
106 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
107 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
108 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
110 void Sbar_MiniDeathmatchOverlay (int x, int y);
111 void Sbar_DeathmatchOverlay (void);
112 void Sbar_IntermissionOverlay (void);
113 void Sbar_FinaleOverlay (void);
115 void CL_VM_UpdateShowingScoresState (int showingscores);
125 void Sbar_ShowScores (void)
129 sb_showscores = true;
130 CL_VM_UpdateShowingScoresState(sb_showscores);
140 void Sbar_DontShowScores (void)
142 sb_showscores = false;
143 CL_VM_UpdateShowingScoresState(sb_showscores);
146 void sbar_start(void)
150 if (gamemode == GAME_DELUXEQUAKE)
153 else if (gamemode == GAME_SOM)
155 sb_disc = Draw_CachePic("gfx/disc", true);
157 for (i = 0;i < 10;i++)
158 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i), true);
160 somsb_health = Draw_CachePic("gfx/hud_health", true);
161 somsb_ammo[0] = Draw_CachePic("gfx/sb_shells", true);
162 somsb_ammo[1] = Draw_CachePic("gfx/sb_nails", true);
163 somsb_ammo[2] = Draw_CachePic("gfx/sb_rocket", true);
164 somsb_ammo[3] = Draw_CachePic("gfx/sb_cells", true);
165 somsb_armor[0] = Draw_CachePic("gfx/sb_armor1", true);
166 somsb_armor[1] = Draw_CachePic("gfx/sb_armor2", true);
167 somsb_armor[2] = Draw_CachePic("gfx/sb_armor3", true);
169 else if (gamemode == GAME_NEXUIZ)
171 for (i = 0;i < 10;i++)
172 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i), true);
173 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus", true);
174 sb_colon = Draw_CachePic ("gfx/num_colon", true);
176 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells", true);
177 sb_ammo[1] = Draw_CachePic ("gfx/sb_bullets", true);
178 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket", true);
179 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells", true);
181 sb_armor[0] = Draw_CachePic ("gfx/sb_armor", true);
185 sb_health = Draw_CachePic ("gfx/sb_health", true);
187 sb_items[2] = Draw_CachePic ("gfx/sb_slowmo", true);
188 sb_items[3] = Draw_CachePic ("gfx/sb_invinc", true);
189 sb_items[4] = Draw_CachePic ("gfx/sb_energy", true);
190 sb_items[5] = Draw_CachePic ("gfx/sb_str", true);
192 sb_items[11] = Draw_CachePic ("gfx/sb_flag_red_taken", true);
193 sb_items[12] = Draw_CachePic ("gfx/sb_flag_red_lost", true);
194 sb_items[13] = Draw_CachePic ("gfx/sb_flag_red_carrying", true);
195 sb_items[14] = Draw_CachePic ("gfx/sb_key_carrying", true);
196 sb_items[15] = Draw_CachePic ("gfx/sb_flag_blue_taken", true);
197 sb_items[16] = Draw_CachePic ("gfx/sb_flag_blue_lost", true);
198 sb_items[17] = Draw_CachePic ("gfx/sb_flag_blue_carrying", true);
200 sb_sbar = Draw_CachePic("gfx/sbar", true);
201 sb_sbar_minimal = Draw_CachePic("gfx/sbar_minimal", true);
202 sb_sbar_overlay = Draw_CachePic("gfx/sbar_overlay", true);
204 for(i = 0; i < 9;i++)
205 sb_weapons[0][i] = Draw_CachePic(va("gfx/inv_weapon%i",i), true);
207 else if (gamemode == GAME_ZYMOTIC)
209 zymsb_crosshair_center = Draw_CachePic ("gfx/hud/crosshair_center", true);
210 zymsb_crosshair_line = Draw_CachePic ("gfx/hud/crosshair_line", true);
211 zymsb_crosshair_health = Draw_CachePic ("gfx/hud/crosshair_health", true);
212 zymsb_crosshair_clip = Draw_CachePic ("gfx/hud/crosshair_clip", true);
213 zymsb_crosshair_ammo = Draw_CachePic ("gfx/hud/crosshair_ammo", true);
214 zymsb_crosshair_background = Draw_CachePic ("gfx/hud/crosshair_background", true);
215 zymsb_crosshair_left1 = Draw_CachePic ("gfx/hud/crosshair_left1", true);
216 zymsb_crosshair_left2 = Draw_CachePic ("gfx/hud/crosshair_left2", true);
217 zymsb_crosshair_right = Draw_CachePic ("gfx/hud/crosshair_right", true);
221 sb_disc = Draw_CachePic("gfx/disc", true);
223 for (i = 0;i < 10;i++)
225 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i), true);
226 sb_nums[1][i] = Draw_CachePic (va("gfx/anum_%i",i), true);
229 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus", true);
230 sb_nums[1][10] = Draw_CachePic ("gfx/anum_minus", true);
232 sb_colon = Draw_CachePic ("gfx/num_colon", true);
233 sb_slash = Draw_CachePic ("gfx/num_slash", true);
235 sb_weapons[0][0] = Draw_CachePic ("gfx/inv_shotgun", true);
236 sb_weapons[0][1] = Draw_CachePic ("gfx/inv_sshotgun", true);
237 sb_weapons[0][2] = Draw_CachePic ("gfx/inv_nailgun", true);
238 sb_weapons[0][3] = Draw_CachePic ("gfx/inv_snailgun", true);
239 sb_weapons[0][4] = Draw_CachePic ("gfx/inv_rlaunch", true);
240 sb_weapons[0][5] = Draw_CachePic ("gfx/inv_srlaunch", true);
241 sb_weapons[0][6] = Draw_CachePic ("gfx/inv_lightng", true);
243 sb_weapons[1][0] = Draw_CachePic ("gfx/inv2_shotgun", true);
244 sb_weapons[1][1] = Draw_CachePic ("gfx/inv2_sshotgun", true);
245 sb_weapons[1][2] = Draw_CachePic ("gfx/inv2_nailgun", true);
246 sb_weapons[1][3] = Draw_CachePic ("gfx/inv2_snailgun", true);
247 sb_weapons[1][4] = Draw_CachePic ("gfx/inv2_rlaunch", true);
248 sb_weapons[1][5] = Draw_CachePic ("gfx/inv2_srlaunch", true);
249 sb_weapons[1][6] = Draw_CachePic ("gfx/inv2_lightng", true);
251 for (i = 0;i < 5;i++)
253 sb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_shotgun",i+1), true);
254 sb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_sshotgun",i+1), true);
255 sb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_nailgun",i+1), true);
256 sb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_snailgun",i+1), true);
257 sb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_rlaunch",i+1), true);
258 sb_weapons[2+i][5] = Draw_CachePic (va("gfx/inva%i_srlaunch",i+1), true);
259 sb_weapons[2+i][6] = Draw_CachePic (va("gfx/inva%i_lightng",i+1), true);
262 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells", true);
263 sb_ammo[1] = Draw_CachePic ("gfx/sb_nails", true);
264 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket", true);
265 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells", true);
267 sb_armor[0] = Draw_CachePic ("gfx/sb_armor1", true);
268 sb_armor[1] = Draw_CachePic ("gfx/sb_armor2", true);
269 sb_armor[2] = Draw_CachePic ("gfx/sb_armor3", true);
271 sb_items[0] = Draw_CachePic ("gfx/sb_key1", true);
272 sb_items[1] = Draw_CachePic ("gfx/sb_key2", true);
273 sb_items[2] = Draw_CachePic ("gfx/sb_invis", true);
274 sb_items[3] = Draw_CachePic ("gfx/sb_invuln", true);
275 sb_items[4] = Draw_CachePic ("gfx/sb_suit", true);
276 sb_items[5] = Draw_CachePic ("gfx/sb_quad", true);
278 sb_sigil[0] = Draw_CachePic ("gfx/sb_sigil1", true);
279 sb_sigil[1] = Draw_CachePic ("gfx/sb_sigil2", true);
280 sb_sigil[2] = Draw_CachePic ("gfx/sb_sigil3", true);
281 sb_sigil[3] = Draw_CachePic ("gfx/sb_sigil4", true);
283 sb_faces[4][0] = Draw_CachePic ("gfx/face1", true);
284 sb_faces[4][1] = Draw_CachePic ("gfx/face_p1", true);
285 sb_faces[3][0] = Draw_CachePic ("gfx/face2", true);
286 sb_faces[3][1] = Draw_CachePic ("gfx/face_p2", true);
287 sb_faces[2][0] = Draw_CachePic ("gfx/face3", true);
288 sb_faces[2][1] = Draw_CachePic ("gfx/face_p3", true);
289 sb_faces[1][0] = Draw_CachePic ("gfx/face4", true);
290 sb_faces[1][1] = Draw_CachePic ("gfx/face_p4", true);
291 sb_faces[0][0] = Draw_CachePic ("gfx/face5", true);
292 sb_faces[0][1] = Draw_CachePic ("gfx/face_p5", true);
294 sb_face_invis = Draw_CachePic ("gfx/face_invis", true);
295 sb_face_invuln = Draw_CachePic ("gfx/face_invul2", true);
296 sb_face_invis_invuln = Draw_CachePic ("gfx/face_inv2", true);
297 sb_face_quad = Draw_CachePic ("gfx/face_quad", true);
299 sb_sbar = Draw_CachePic ("gfx/sbar", true);
300 sb_ibar = Draw_CachePic ("gfx/ibar", true);
301 sb_scorebar = Draw_CachePic ("gfx/scorebar", true);
303 //MED 01/04/97 added new hipnotic weapons
304 if (gamemode == GAME_HIPNOTIC)
306 hsb_weapons[0][0] = Draw_CachePic ("gfx/inv_laser", true);
307 hsb_weapons[0][1] = Draw_CachePic ("gfx/inv_mjolnir", true);
308 hsb_weapons[0][2] = Draw_CachePic ("gfx/inv_gren_prox", true);
309 hsb_weapons[0][3] = Draw_CachePic ("gfx/inv_prox_gren", true);
310 hsb_weapons[0][4] = Draw_CachePic ("gfx/inv_prox", true);
312 hsb_weapons[1][0] = Draw_CachePic ("gfx/inv2_laser", true);
313 hsb_weapons[1][1] = Draw_CachePic ("gfx/inv2_mjolnir", true);
314 hsb_weapons[1][2] = Draw_CachePic ("gfx/inv2_gren_prox", true);
315 hsb_weapons[1][3] = Draw_CachePic ("gfx/inv2_prox_gren", true);
316 hsb_weapons[1][4] = Draw_CachePic ("gfx/inv2_prox", true);
318 for (i = 0;i < 5;i++)
320 hsb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_laser",i+1), true);
321 hsb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_mjolnir",i+1), true);
322 hsb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_gren_prox",i+1), true);
323 hsb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_prox_gren",i+1), true);
324 hsb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_prox",i+1), true);
327 hsb_items[0] = Draw_CachePic ("gfx/sb_wsuit", true);
328 hsb_items[1] = Draw_CachePic ("gfx/sb_eshld", true);
330 else if (gamemode == GAME_ROGUE)
332 rsb_invbar[0] = Draw_CachePic ("gfx/r_invbar1", true);
333 rsb_invbar[1] = Draw_CachePic ("gfx/r_invbar2", true);
335 rsb_weapons[0] = Draw_CachePic ("gfx/r_lava", true);
336 rsb_weapons[1] = Draw_CachePic ("gfx/r_superlava", true);
337 rsb_weapons[2] = Draw_CachePic ("gfx/r_gren", true);
338 rsb_weapons[3] = Draw_CachePic ("gfx/r_multirock", true);
339 rsb_weapons[4] = Draw_CachePic ("gfx/r_plasma", true);
341 rsb_items[0] = Draw_CachePic ("gfx/r_shield1", true);
342 rsb_items[1] = Draw_CachePic ("gfx/r_agrav1", true);
344 // PGM 01/19/97 - team color border
345 rsb_teambord = Draw_CachePic ("gfx/r_teambord", true);
346 // PGM 01/19/97 - team color border
348 rsb_ammo[0] = Draw_CachePic ("gfx/r_ammolava", true);
349 rsb_ammo[1] = Draw_CachePic ("gfx/r_ammomulti", true);
350 rsb_ammo[2] = Draw_CachePic ("gfx/r_ammoplasma", true);
354 sb_ranking = Draw_CachePic ("gfx/ranking", true);
355 sb_complete = Draw_CachePic ("gfx/complete", true);
356 sb_inter = Draw_CachePic ("gfx/inter", true);
357 sb_finale = Draw_CachePic ("gfx/finale", true);
360 void sbar_shutdown(void)
364 void sbar_newmap(void)
368 void Sbar_Init (void)
370 Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
371 Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
372 Cvar_RegisterVariable(&showfps);
373 Cvar_RegisterVariable(&showspeed);
374 Cvar_RegisterVariable(&showtime);
375 Cvar_RegisterVariable(&showtime_format);
376 Cvar_RegisterVariable(&showdate);
377 Cvar_RegisterVariable(&showdate_format);
378 Cvar_RegisterVariable(&sbar_alpha_bg);
379 Cvar_RegisterVariable(&sbar_alpha_fg);
380 Cvar_RegisterVariable(&sbar_hudselector);
381 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
382 Cvar_RegisterVariable(&cl_deathscoreboard);
384 Cvar_RegisterVariable(&crosshair_color_red);
385 Cvar_RegisterVariable(&crosshair_color_green);
386 Cvar_RegisterVariable(&crosshair_color_blue);
387 Cvar_RegisterVariable(&crosshair_color_alpha);
388 Cvar_RegisterVariable(&crosshair_size);
390 if(gamemode == GAME_NEXUIZ)
391 Cvar_RegisterVariable(&sbar_flagstatus_right); // this cvar makes no sense in other games
393 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap);
397 //=============================================================================
399 // drawing routines are relative to the status bar location
408 void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
410 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
413 void Sbar_DrawPic (int x, int y, cachepic_t *pic)
415 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
418 void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
420 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
427 Draws one solid graphics character
430 void Sbar_DrawCharacter (int x, int y, int num)
432 DrawQ_String_Font (sbar_x + x + 4 , sbar_y + y, va("%c", num), 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, true, FONT_SBAR);
440 void Sbar_DrawString (int x, int y, char *str)
442 DrawQ_String_Font (sbar_x + x, sbar_y + y, str, 0, 8, 8, 1, 1, 1, sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR);
450 void Sbar_DrawNum (int x, int y, int num, int digits, int color)
455 l = sprintf(str, "%i", num);
469 Sbar_DrawPic (x, y, sb_nums[color][frame]);
482 void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
490 l = sprintf(str, "%0*i", digits, num);
493 l = sprintf(str, "%i", num);
498 x += (digits-l) * lettersize;
507 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
514 //=============================================================================
517 int Sbar_IsTeammatch()
519 // currently only nexuiz uses the team score board
520 return ((gamemode == GAME_NEXUIZ)
521 && (teamplay.integer > 0));
529 static int fragsort[MAX_SCOREBOARD];
530 static int scoreboardlines;
532 //[515]: Sbar_GetPlayer for csqc "getplayerkey" func
533 int Sbar_GetPlayer (int index)
538 if(index >= scoreboardlines)
540 index = fragsort[index];
542 if(index >= scoreboardlines)
547 static scoreboard_t teams[MAX_SCOREBOARD];
548 static int teamsort[MAX_SCOREBOARD];
549 static int teamlines;
550 void Sbar_SortFrags (void)
556 for (i=0 ; i<cl.maxclients ; i++)
558 if (cl.scores[i].name[0])
560 fragsort[scoreboardlines] = i;
565 for (i=0 ; i<scoreboardlines ; i++)
566 for (j=0 ; j<scoreboardlines-1-i ; j++)
567 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
570 fragsort[j] = fragsort[j+1];
575 if (Sbar_IsTeammatch ())
577 // now sort players by teams.
578 for (i=0 ; i<scoreboardlines ; i++)
580 for (j=0 ; j<scoreboardlines-1-i ; j++)
582 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
585 fragsort[j] = fragsort[j+1];
591 // calculate team scores
593 for (i=0 ; i<scoreboardlines ; i++)
595 if (color != (cl.scores[fragsort[i]].colors & 15))
597 const char* teamname;
599 color = cl.scores[fragsort[i]].colors & 15;
605 teamname = "^1Red Team";
608 teamname = "^4Blue Team";
611 teamname = "^6Pink Team";
614 teamname = "^3Yellow Team";
617 teamname = "Total Team Score";
620 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
622 teams[teamlines-1].frags = 0;
623 teams[teamlines-1].colors = color + 16 * color;
626 if (cl.scores[fragsort[i]].frags != -666)
628 // do not add spedcators
629 // (ugly hack for nexuiz)
630 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
634 // now sort teams by scores.
635 for (i=0 ; i<teamlines ; i++)
637 for (i=0 ; i<teamlines ; i++)
639 for (j=0 ; j<teamlines-1-i ; j++)
641 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
644 teamsort[j] = teamsort[j+1];
657 void Sbar_SoloScoreboard (void)
660 char str[80], timestr[40];
662 int minutes, seconds;
665 t = (cl.intermission ? cl.completed_time : cl.time);
666 minutes = (int)(t / 60);
667 seconds = (int)(t - 60*floor(t/60));
669 // monsters and secrets are now both on the top row
670 if (cl.stats[STAT_TOTALMONSTERS])
671 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
672 if (cl.stats[STAT_TOTALSECRETS])
673 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
675 // figure out the map's filename without path or extension
676 strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
677 if (strrchr(str, '.'))
678 *(strrchr(str, '.')) = 0;
680 // append a : separator and then the full title
681 strlcat(str, ":", sizeof(str));
682 strlcat(str, cl.levelname, sizeof(str));
684 // if there's a newline character, terminate the string there
685 if (strchr(str, '\n'))
686 *(strchr(str, '\n')) = 0;
688 // make the time string
689 timelen = sprintf(timestr, " %i:%02i", minutes, seconds);
691 // truncate the level name if necessary to make room for time
693 if ((int)strlen(str) > max)
696 // print the filename and message
697 Sbar_DrawString(8, 12, str);
700 Sbar_DrawString(8 + max*8, 12, timestr);
704 int minutes, seconds, tens, units;
707 if (gamemode != GAME_NEXUIZ) {
708 sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
709 Sbar_DrawString (8, 4, str);
711 sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
712 Sbar_DrawString (8, 12, str);
716 minutes = (int)(cl.time / 60);
717 seconds = (int)(cl.time - 60*minutes);
719 units = seconds - 10*tens;
720 sprintf (str,"Time :%3i:%i%i", minutes, tens, units);
721 Sbar_DrawString (184, 4, str);
724 if (gamemode == GAME_NEXUIZ) {
725 l = (int) strlen (cl.worldmodel->name);
726 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
728 l = (int) strlen (cl.levelname);
729 Sbar_DrawString (232 - l*4, 12, cl.levelname);
739 void Sbar_DrawScoreboard (void)
741 Sbar_SoloScoreboard ();
742 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
743 //if (cl.gametype == GAME_DEATHMATCH)
745 Sbar_DeathmatchOverlay ();
748 //=============================================================================
750 // AK to make DrawInventory smaller
751 static void Sbar_DrawWeapon(int nr, float fade, int active)
753 if (sbar_hudselector.integer == 1)
755 // width = 300, height = 100
756 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
758 DrawQ_Pic((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr, vid_conheight.integer - w_height, sb_weapons[0][nr], w_width, w_height, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 0.6, (active ? 1 : 0.6) * fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
760 DrawQ_String((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr + w_space, vid_conheight.integer - w_height + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 1, 0, sbar_alpha_fg.value, 0, NULL, true);
764 // width = 300, height = 100
765 const int w_width = 300, w_height = 100, w_space = 10;
766 const float w_scale = 0.4;
768 DrawQ_Pic(vid_conwidth.integer - (w_width + w_space) * w_scale, (w_height + w_space) * w_scale * nr + w_space, sb_weapons[0][nr], w_width * w_scale, w_height * w_scale, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 1, fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
769 //DrawQ_String(vid_conwidth.integer - (w_space + font_size ), (w_height + w_space) * w_scale * nr + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 0, 0, fade, 0, NULL, true);
778 void Sbar_DrawInventory (void)
785 if (gamemode == GAME_ROGUE)
787 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
788 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
790 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
793 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
796 for (i=0 ; i<7 ; i++)
798 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
800 time = cl.item_gettime[i];
801 flashon = (int)(max(0, cl.time - time)*10);
804 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
810 flashon = (flashon%5) + 2;
812 Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
818 if (gamemode == GAME_HIPNOTIC)
820 int grenadeflashing=0;
821 for (i=0 ; i<4 ; i++)
823 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
825 time = max(0, cl.item_gettime[hipweapons[i]]);
826 flashon = (int)((cl.time - time)*10);
829 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
835 flashon = (flashon%5) + 2;
837 // check grenade launcher
840 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
845 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
851 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
853 if (!grenadeflashing)
854 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
857 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
860 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
865 if (gamemode == GAME_ROGUE)
867 // check for powered up weapon.
868 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
870 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
871 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
875 for (i=0 ; i<4 ; i++)
877 sprintf (num, "%4i",cl.stats[STAT_SHELLS+i] );
879 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
881 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
883 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
885 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
889 for (i=0 ; i<6 ; i++)
890 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
892 //MED 01/04/97 changed keys
893 if (gamemode != GAME_HIPNOTIC || (i>1))
894 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
897 //MED 01/04/97 added hipnotic items
899 if (gamemode == GAME_HIPNOTIC)
901 for (i=0 ; i<2 ; i++)
902 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
903 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
906 if (gamemode == GAME_ROGUE)
909 for (i=0 ; i<2 ; i++)
910 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
911 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
916 for (i=0 ; i<4 ; i++)
917 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
918 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
922 //=============================================================================
929 void Sbar_DrawFrags (void)
939 l = min(scoreboardlines, 4);
943 for (i = 0;i < l;i++)
949 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
950 DrawQ_Fill (sbar_x + x + 10, sbar_y - 23, 28, 4, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
951 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
952 DrawQ_Fill (sbar_x + x + 10, sbar_y + 4 - 23, 28, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
956 sprintf (num, "%3i",f);
958 if (k == cl.viewentity - 1)
960 Sbar_DrawCharacter ( x + 2, -24, 16);
961 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
963 Sbar_DrawCharacter (x + 8, -24, num[0]);
964 Sbar_DrawCharacter (x + 16, -24, num[1]);
965 Sbar_DrawCharacter (x + 24, -24, num[2]);
970 //=============================================================================
978 void Sbar_DrawFace (void)
982 // PGM 01/19/97 - team color drawing
983 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
984 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
990 s = &cl.scores[cl.viewentity - 1];
992 Sbar_DrawPic (112, 0, rsb_teambord);
993 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
994 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+3, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
995 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
996 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+12, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1000 sprintf (num, "%3i",f);
1002 if ((s->colors & 0xf0)==0)
1005 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1007 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1009 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1013 Sbar_DrawCharacter ( 109, 3, num[0]);
1014 Sbar_DrawCharacter ( 116, 3, num[1]);
1015 Sbar_DrawCharacter ( 123, 3, num[2]);
1020 // PGM 01/19/97 - team color drawing
1022 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1023 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1024 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1025 Sbar_DrawPic (112, 0, sb_face_quad );
1026 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1027 Sbar_DrawPic (112, 0, sb_face_invis );
1028 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1029 Sbar_DrawPic (112, 0, sb_face_invuln);
1032 f = cl.stats[STAT_HEALTH] / 20;
1034 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1038 void Sbar_ShowFPS(void)
1040 float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1042 char timestring[32];
1043 char datestring[32];
1044 char speedstring[32];
1045 qboolean red = false;
1050 if (showfps.integer)
1053 static double nexttime = 0, lasttime = 0;
1054 static double framerate = 0;
1055 static int framecount = 0;
1058 if (newtime >= nexttime)
1060 framerate = framecount / (newtime - lasttime);
1062 nexttime = max(nexttime + 1, lasttime - 1);
1068 if ((red = (calc < 1.0f)))
1069 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1071 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1073 if (showtime.integer)
1074 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1075 if (showdate.integer)
1076 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1077 if (showspeed.integer)
1079 double speed, speedxy, f;
1081 speed = VectorLength(cl.movement_velocity);
1082 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1083 switch(showspeed.integer)
1093 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1094 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1099 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1103 f = 0.0254 * 3.6 * 0.6213711922;
1104 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1108 f = 0.0254 * 1.943844492;
1109 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1112 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1114 if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0])
1118 fps_height = fps_scaley * ((fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0));
1119 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1120 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1121 fps_y = vid_conheight.integer - fps_height;
1124 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1125 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1127 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1129 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1130 fps_y += fps_scaley;
1134 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1135 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1136 DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1137 fps_y += fps_scaley;
1141 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1142 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1143 DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1144 fps_y += fps_scaley;
1148 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1149 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1150 DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1151 fps_y += fps_scaley;
1156 void Sbar_DrawGauge(float x, float y, cachepic_t *pic, float width, float height, float rangey, float rangeheight, float c1, float c2, float c1r, float c1g, float c1b, float c1a, float c2r, float c2g, float c2b, float c2a, float c3r, float c3g, float c3b, float c3a, int drawflags)
1159 c2 = bound(0, c2, 1);
1160 c1 = bound(0, c1, 1 - c2);
1162 r[1] = rangey + rangeheight * (c2 + c1);
1163 r[2] = rangey + rangeheight * (c2);
1167 DrawQ_SuperPic(x, y + r[0], pic, width, (r[1] - r[0]), 0,(r[0] / height), c3r,c3g,c3b,c3a, 1,(r[0] / height), c3r,c3g,c3b,c3a, 0,(r[1] / height), c3r,c3g,c3b,c3a, 1,(r[1] / height), c3r,c3g,c3b,c3a, drawflags);
1169 DrawQ_SuperPic(x, y + r[1], pic, width, (r[2] - r[1]), 0,(r[1] / height), c1r,c1g,c1b,c1a, 1,(r[1] / height), c1r,c1g,c1b,c1a, 0,(r[2] / height), c1r,c1g,c1b,c1a, 1,(r[2] / height), c1r,c1g,c1b,c1a, drawflags);
1171 DrawQ_SuperPic(x, y + r[2], pic, width, (r[3] - r[2]), 0,(r[2] / height), c2r,c2g,c2b,c2a, 1,(r[2] / height), c2r,c2g,c2b,c2a, 0,(r[3] / height), c2r,c2g,c2b,c2a, 1,(r[3] / height), c2r,c2g,c2b,c2a, drawflags);
1173 DrawQ_SuperPic(x, y + r[3], pic, width, (r[4] - r[3]), 0,(r[3] / height), c3r,c3g,c3b,c3a, 1,(r[3] / height), c3r,c3g,c3b,c3a, 0,(r[4] / height), c3r,c3g,c3b,c3a, 1,(r[4] / height), c3r,c3g,c3b,c3a, drawflags);
1181 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1182 extern cvar_t v_kicktime;
1183 void Sbar_Score (int margin);
1184 void Sbar_Draw (void)
1188 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1191 Sbar_DrawScoreboard ();
1192 else if (cl.intermission == 1)
1194 if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1196 Sbar_DrawScoreboard();
1199 Sbar_IntermissionOverlay();
1201 else if (cl.intermission == 2)
1202 Sbar_FinaleOverlay();
1203 else if (gamemode == GAME_DELUXEQUAKE)
1206 else if (gamemode == GAME_SOM)
1208 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1209 Sbar_DrawScoreboard ();
1212 // this is the top left of the sbar area
1214 sbar_y = vid_conheight.integer - 24*3;
1217 if (cl.stats[STAT_ARMOR])
1219 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1220 Sbar_DrawPic(0, 0, somsb_armor[2]);
1221 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1222 Sbar_DrawPic(0, 0, somsb_armor[1]);
1223 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1224 Sbar_DrawPic(0, 0, somsb_armor[0]);
1225 Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1229 Sbar_DrawPic(0, 24, somsb_health);
1230 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1233 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1234 Sbar_DrawPic(0, 48, somsb_ammo[0]);
1235 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1236 Sbar_DrawPic(0, 48, somsb_ammo[1]);
1237 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1238 Sbar_DrawPic(0, 48, somsb_ammo[2]);
1239 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1240 Sbar_DrawPic(0, 48, somsb_ammo[3]);
1241 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1242 if (cl.stats[STAT_SHELLS])
1243 Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1246 else if (gamemode == GAME_NEXUIZ)
1248 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1250 sbar_x = (vid_conwidth.integer - 640)/2;
1251 sbar_y = vid_conheight.integer - 47;
1252 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1253 Sbar_DrawScoreboard ();
1255 else if (sb_lines && sbar_hudselector.integer == 1)
1259 int redflag, blueflag;
1262 sbar_x = (vid_conwidth.integer - 320)/2;
1263 sbar_y = vid_conheight.integer - 24 - 16;
1265 // calculate intensity to draw weapons bar at
1266 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1267 fade = bound(0.7, fade, 1);
1268 for (i = 0; i < 8;i++)
1269 if (cl.stats[STAT_ITEMS] & (1 << i))
1270 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1271 if((cl.stats[STAT_ITEMS] & (1<<12)))
1272 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1275 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1276 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1277 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1278 if (redflag == 3 && blueflag == 3)
1280 // The Impossible Combination[tm]
1281 // Can only happen in Key Hunt mode...
1282 Sbar_DrawPic (x, -179, sb_items[14]);
1287 Sbar_DrawPic (x, -117, sb_items[redflag+10]);
1289 Sbar_DrawPic (x, -177, sb_items[blueflag+14]);
1293 if (cl.stats[STAT_ARMOR] > 0)
1295 Sbar_DrawStretchPic (0, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1296 if(cl.stats[STAT_ARMOR] > 200)
1297 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1298 else if(cl.stats[STAT_ARMOR] > 100)
1299 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1300 else if(cl.stats[STAT_ARMOR] > 50)
1301 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1302 else if(cl.stats[STAT_ARMOR] > 25)
1303 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1305 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1309 if (cl.stats[STAT_HEALTH] != 0)
1311 Sbar_DrawStretchPic (112, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1312 if(cl.stats[STAT_HEALTH] > 200)
1313 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1314 else if(cl.stats[STAT_HEALTH] > 100)
1315 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1316 else if(cl.stats[STAT_HEALTH] > 50)
1317 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1318 else if(cl.stats[STAT_HEALTH] > 25)
1319 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1321 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1325 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1327 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1328 Sbar_DrawStretchPic (224, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1329 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1330 Sbar_DrawStretchPic (224, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1331 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1332 Sbar_DrawStretchPic (224, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1333 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1334 Sbar_DrawStretchPic (224, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1335 if(cl.stats[STAT_AMMO] > 10)
1336 Sbar_DrawXNum(248, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1338 Sbar_DrawXNum(248, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1341 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1342 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1345 // The margin can be at most 8 to support 640x480 console size:
1346 // 320 + 2 * (144 + 16) = 640
1352 int redflag, blueflag;
1355 sbar_x = (vid_conwidth.integer - 640)/2;
1356 sbar_y = vid_conheight.integer - 47;
1358 // calculate intensity to draw weapons bar at
1359 fade = 3 - 2 * (cl.time - cl.weapontime);
1362 fade = min(fade, 1);
1363 for (i = 0; i < 8;i++)
1364 if (cl.stats[STAT_ITEMS] & (1 << i))
1365 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1367 if((cl.stats[STAT_ITEMS] & (1<<12)))
1368 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1371 //if (!cl.islocalgame)
1372 // Sbar_DrawFrags ();
1375 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1377 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1380 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1381 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1382 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1383 if (redflag == 3 && blueflag == 3)
1385 // The Impossible Combination[tm]
1386 // Can only happen in Key Hunt mode...
1387 Sbar_DrawPic (x, -179, sb_items[14]);
1392 Sbar_DrawPic (x, -117, sb_items[redflag+10]);
1394 Sbar_DrawPic (x, -177, sb_items[blueflag+14]);
1398 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1401 if(cl.stats[STAT_HEALTH] > 100)
1402 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1403 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1404 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1406 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1408 // AK dont draw ammo for the laser
1409 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1411 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1412 Sbar_DrawPic (519, 0, sb_ammo[0]);
1413 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1414 Sbar_DrawPic (519, 0, sb_ammo[1]);
1415 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1416 Sbar_DrawPic (519, 0, sb_ammo[2]);
1417 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1418 Sbar_DrawPic (519, 0, sb_ammo[3]);
1420 if(cl.stats[STAT_AMMO] <= 10)
1421 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1423 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1428 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1430 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1431 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1436 // Mini scoreboard uses 12*4 per other team, that is, 144
1437 // pixels when there are four teams...
1438 // Nexuiz by default sets vid_conwidth to 800... makes
1440 // so we need to shift it by 64 pixels to the right to fit
1441 // BUT: then it overlaps with the image that gets drawn
1442 // for viewsize 100! Therefore, just account for 3 teams,
1443 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1447 else if (gamemode == GAME_ZYMOTIC)
1450 float scale = 64.0f / 256.0f;
1451 float kickoffset[3];
1452 VectorClear(kickoffset);
1455 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1456 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1458 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1459 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1460 // left1 16, 48 : 126 -66
1461 // left2 16, 128 : 196 -66
1462 // right 176, 48 : 196 -136
1463 Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 48 * scale, zymsb_crosshair_left1, 64*scale, 80*scale, 78*scale, -66*scale, cl.stats[STAT_AMMO] * (1.0 / 200.0), cl.stats[STAT_SHELLS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1464 Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2, 64*scale, 80*scale, 68*scale, -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1465 Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y + 48 * scale, zymsb_crosshair_right, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR] * (1.0 / 300.0), cl.stats[STAT_HEALTH] * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1466 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1468 float scale = 128.0f / 256.0f;
1469 float healthstart, healthheight, healthstarttc, healthendtc;
1470 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1471 float ammostart, ammoheight, ammostarttc, ammoendtc;
1472 float clipstart, clipheight, clipstarttc, clipendtc;
1473 float kickoffset[3], offset;
1474 VectorClear(kickoffset);
1477 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1478 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1480 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1481 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1482 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1483 DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0);
1484 DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0);
1485 DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0);
1486 DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0);
1487 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1488 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1489 healthstart = 204 - healthheight;
1490 shieldstart = healthstart - shieldheight;
1491 healthstarttc = healthstart * (1.0f / 256.0f);
1492 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1493 shieldstarttc = shieldstart * (1.0f / 256.0f);
1494 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1495 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1496 ammostart = 114 - ammoheight;
1497 ammostarttc = ammostart * (1.0f / 256.0f);
1498 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1499 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1500 clipstart = 190 - clipheight;
1501 clipstarttc = clipstart * (1.0f / 256.0f);
1502 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1503 if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1504 if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL);
1505 if (ammoheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart * scale, zymsb_crosshair_ammo, 256 * scale, ammoheight * scale, 0,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 1,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 0,ammoendtc, 0.8f,0.8f,0.0f,1.0f, 1,ammoendtc, 0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL);
1506 if (clipheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart * scale, zymsb_crosshair_clip, 256 * scale, clipheight * scale, 0,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 1,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 0,clipendtc, 1.0f,1.0f,0.0f,1.0f, 1,clipendtc, 1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1507 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1508 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1511 else // Quake and others
1513 sbar_x = (vid_conwidth.integer - 320)/2;
1514 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1515 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1516 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1520 if (gamemode != GAME_GOODVSBAD2)
1521 Sbar_DrawInventory ();
1522 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1526 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1528 if (gamemode != GAME_GOODVSBAD2)
1529 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1530 Sbar_DrawScoreboard ();
1534 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1536 // keys (hipnotic only)
1537 //MED 01/04/97 moved keys here so they would not be overwritten
1538 if (gamemode == GAME_HIPNOTIC)
1540 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1541 Sbar_DrawPic (209, 3, sb_items[0]);
1542 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1543 Sbar_DrawPic (209, 12, sb_items[1]);
1546 if (gamemode != GAME_GOODVSBAD2)
1548 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1550 Sbar_DrawNum (24, 0, 666, 3, 1);
1551 Sbar_DrawPic (0, 0, sb_disc);
1555 if (gamemode == GAME_ROGUE)
1557 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1558 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1559 Sbar_DrawPic (0, 0, sb_armor[2]);
1560 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1561 Sbar_DrawPic (0, 0, sb_armor[1]);
1562 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1563 Sbar_DrawPic (0, 0, sb_armor[0]);
1567 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1568 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1569 Sbar_DrawPic (0, 0, sb_armor[2]);
1570 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1571 Sbar_DrawPic (0, 0, sb_armor[1]);
1572 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1573 Sbar_DrawPic (0, 0, sb_armor[0]);
1582 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1585 if (gamemode == GAME_ROGUE)
1587 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1588 Sbar_DrawPic (224, 0, sb_ammo[0]);
1589 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1590 Sbar_DrawPic (224, 0, sb_ammo[1]);
1591 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1592 Sbar_DrawPic (224, 0, sb_ammo[2]);
1593 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1594 Sbar_DrawPic (224, 0, sb_ammo[3]);
1595 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1596 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1597 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1598 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1599 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1600 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1604 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1605 Sbar_DrawPic (224, 0, sb_ammo[0]);
1606 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1607 Sbar_DrawPic (224, 0, sb_ammo[1]);
1608 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1609 Sbar_DrawPic (224, 0, sb_ammo[2]);
1610 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1611 Sbar_DrawPic (224, 0, sb_ammo[3]);
1614 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1616 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1617 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1619 if (gamemode == GAME_TRANSFUSION)
1620 Sbar_MiniDeathmatchOverlay (0, 0);
1622 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1631 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1633 pic = Draw_CachePic(va("gfx/crosshair%i", crosshair.integer), true);
1634 DrawQ_Pic((vid_conwidth.integer - pic->width * crosshair_size.value) * 0.5f, (vid_conheight.integer - pic->height * crosshair_size.value) * 0.5f, pic, pic->width * crosshair_size.value, pic->height * crosshair_size.value, crosshair_color_red.value, crosshair_color_green.value, crosshair_color_blue.value, crosshair_color_alpha.value, 0);
1637 if (cl_prydoncursor.integer)
1638 DrawQ_Pic((cl.cmd.cursor_screen[0] + 1) * 0.5 * vid_conwidth.integer, (cl.cmd.cursor_screen[1] + 1) * 0.5 * vid_conheight.integer, Draw_CachePic(va("gfx/prydoncursor%03i", cl_prydoncursor.integer), true), 0, 0, 1, 1, 1, 1, 0);
1641 //=============================================================================
1645 Sbar_DeathmatchOverlay
1649 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1652 qboolean myself = false;
1654 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1656 if((s - cl.scores) == cl.playerentity - 1)
1658 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1659 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1662 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1664 if (s->qw_spectator)
1666 if (s->qw_ping || s->qw_packetloss)
1667 DrawQ_String_Font(x, y, va("%4i %3i %4i spectator %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1669 DrawQ_String_Font(x, y, va(" %4i spectator %c%s", minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1673 // draw colors behind score
1679 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1680 DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1681 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1682 DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1684 //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1685 if (s->qw_ping || s->qw_packetloss)
1686 DrawQ_String_Font(x, y, va("%4i %3i %4i %5i %-4s %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1688 DrawQ_String_Font(x, y, va(" %4i %5i %-4s %c%s", minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1693 if (s->qw_spectator)
1695 if (s->qw_ping || s->qw_packetloss)
1696 DrawQ_String_Font(x, y, va("%4i %3i spect %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1698 DrawQ_String_Font(x, y, va(" spect %c%s", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1702 // draw colors behind score
1703 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1704 DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1705 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1706 DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1708 //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1709 if (s->qw_ping || s->qw_packetloss)
1710 DrawQ_String_Font(x, y, va("%4i %3i %5i %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1712 DrawQ_String_Font(x, y, va(" %5i %c%s", (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1718 void Sbar_DeathmatchOverlay (void)
1720 int i, y, xmin, xmax, ymin, ymax;
1722 // request new ping times every two second
1723 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1725 cl.last_ping_request = realtime;
1726 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1728 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1729 MSG_WriteString(&cls.netcon->message, "pings");
1731 else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5 || cls.protocol == PROTOCOL_DARKPLACES6/* || cls.protocol == PROTOCOL_DARKPLACES7*/)
1733 // these servers usually lack the pings command and so a less efficient "ping" command must be sent, which on modern DP servers will also reply with a pingplreport command after the ping listing
1734 static int ping_anyway_counter = 0;
1735 if(cl.parsingtextexpectingpingforscores == 1)
1737 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1738 if(++ping_anyway_counter >= 5)
1739 cl.parsingtextexpectingpingforscores = 0;
1741 if(cl.parsingtextexpectingpingforscores != 1)
1743 ping_anyway_counter = 0;
1744 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1745 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1746 MSG_WriteString(&cls.netcon->message, "ping");
1751 // newer server definitely has pings command, so use it for more efficiency, avoids ping reports spamming the console if they are misparsed, and saves a little bandwidth
1752 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1753 MSG_WriteString(&cls.netcon->message, "pings");
1761 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1763 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1764 xmin = (vid_conwidth.integer - (26 + 15) * 8 * FONT_SBAR->width_of[0]) / 2; // 26 characters until name, then we assume 15 character names (they can be longer but usually aren't)
1766 xmin = (vid_conwidth.integer - (16 + 25) * 8 * FONT_SBAR->width_of[0]) / 2; // 16 characters until name, then we assume 25 character names (they can be longer but usually aren't)
1767 xmax = vid_conwidth.integer - xmin;
1769 if(gamemode == GAME_NEXUIZ)
1770 DrawQ_Pic (xmin - 8, ymin - 8, 0, xmax-xmin+1 + 2*8, ymax-ymin+1 + 2*8, 0, 0, 0, sbar_alpha_bg.value, 0);
1772 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1776 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1778 DrawQ_String_Font(xmin, y, va("ping pl%% time frags team name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1782 DrawQ_String_Font(xmin, y, va("ping pl%% frags name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1786 if (Sbar_IsTeammatch ())
1788 // show team scores first
1789 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1790 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1794 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1795 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1800 Sbar_MiniDeathmatchOverlay
1804 void Sbar_MiniDeathmatchOverlay (int x, int y)
1806 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1808 // do not draw this if sbar_miniscoreboard_size is zero
1809 if(sbar_miniscoreboard_size.value == 0)
1811 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1812 if(sbar_miniscoreboard_size.value > 0)
1813 y = vid_conheight.integer - sbar_miniscoreboard_size.value * 8;
1818 // decide where to print
1819 if (gamemode == GAME_TRANSFUSION)
1820 numlines = (vid_conwidth.integer - x + 127) / 128;
1822 numlines = (vid_conheight.integer - y + 7) / 8;
1824 // give up if there isn't room
1825 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1829 for (i = 0; i < scoreboardlines; i++)
1830 if (fragsort[i] == cl.playerentity - 1)
1834 range_end = scoreboardlines;
1837 if (gamemode != GAME_TRANSFUSION)
1838 if (Sbar_IsTeammatch ())
1840 // reserve space for the team scores
1841 numlines -= teamlines;
1843 // find first and last player of my team (only draw the team totals and my own team)
1844 range_begin = range_end = i;
1845 myteam = cl.scores[fragsort[i]].colors & 15;
1846 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1848 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1851 // looks better than two players
1861 i = min(i, range_end - numlines);
1862 i = max(i, range_begin);
1864 if (gamemode == GAME_TRANSFUSION)
1866 for (;i < range_end && x < vid_conwidth.integer;i++)
1867 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1871 if(range_end - i < numlines) // won't draw to bottom?
1872 y += 8 * (numlines - (range_end - i)); // bottom align
1873 // show team scores first
1874 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1875 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1877 for (;i < range_end && y < vid_conheight.integer;i++)
1878 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1882 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1884 static int const sortorder[16] =
1903 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1904 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1905 int tc1 = sortorder[t1->colors & 15];
1906 int tc2 = sortorder[t2->colors & 15];
1910 void Sbar_Score (int margin)
1912 int i, me, score, otherleader, place, distribution, minutes, seconds;
1914 int sbar_x_save = sbar_x;
1915 int sbar_y_save = sbar_y;
1917 sbar_y = vid_conheight.value - (32+12);
1920 me = cl.playerentity - 1;
1921 if (me >= 0 && me < cl.maxclients)
1923 if(Sbar_IsTeammatch())
1927 // team1 team3 team4
1931 scoreboard_t *teamcolorsort[16];
1934 for(i = 0; i < teamlines; ++i)
1935 teamcolorsort[i] = &(teams[i]);
1937 // Now sort them by color
1938 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
1941 // -12*4: four digits space
1942 place = (teamlines - 1) * (-12 * 4);
1944 for(i = 0; i < teamlines; ++i)
1946 int cindex = teamcolorsort[i]->colors & 15;
1947 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
1948 float cm = max(max(c[0], c[1]), c[2]);
1949 float cr = c[0] / cm;
1950 float cg = c[1] / cm;
1951 float cb = c[2] / cm;
1952 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
1954 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
1958 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
1971 // find leading score other than ourselves, to calculate distribution
1972 // find our place in the scoreboard
1973 score = cl.scores[me].frags;
1974 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
1976 if (cl.scores[i].name[0] && i != me)
1978 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
1980 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
1984 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
1986 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
1987 else if (place == 2)
1988 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
1990 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
1991 if (otherleader < 0)
1992 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
1993 if (distribution >= 0)
1995 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
1996 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
1998 else if (distribution >= -5)
2000 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2001 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2005 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2006 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2011 if (cl.statsf[STAT_TIMELIMIT])
2013 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2014 minutes = (int)floor(timeleft / 60);
2015 seconds = (int)(floor(timeleft) - minutes * 60);
2018 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2019 if(sb_colon && sb_colon->tex != r_texture_notexture)
2020 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2021 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2023 else if (minutes >= 1)
2025 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2026 if(sb_colon && sb_colon->tex != r_texture_notexture)
2027 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2028 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2030 else if ((int)(timeleft * 4) & 1)
2031 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2033 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2037 minutes = (int)floor(cl.time / 60);
2038 seconds = (int)(floor(cl.time) - minutes * 60);
2039 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2040 if(sb_colon && sb_colon->tex != r_texture_notexture)
2041 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2042 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2045 sbar_x = sbar_x_save;
2046 sbar_y = sbar_y_save;
2051 Sbar_IntermissionOverlay
2055 void Sbar_IntermissionOverlay (void)
2060 if (cl.gametype == GAME_DEATHMATCH)
2062 Sbar_DeathmatchOverlay ();
2066 sbar_x = (vid_conwidth.integer - 320) >> 1;
2067 sbar_y = (vid_conheight.integer - 200) >> 1;
2069 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2070 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2073 dig = (int)cl.completed_time / 60;
2074 Sbar_DrawNum (160, 64, dig, 3, 0);
2075 num = (int)cl.completed_time - dig*60;
2076 Sbar_DrawPic (234,64,sb_colon);
2077 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2078 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2080 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2081 if (gamemode != GAME_NEXUIZ)
2082 Sbar_DrawPic (232, 104, sb_slash);
2083 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2085 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2086 if (gamemode != GAME_NEXUIZ)
2087 Sbar_DrawPic (232, 144, sb_slash);
2088 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2099 void Sbar_FinaleOverlay (void)
2101 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);