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
27 #define STAT_MINUS 10 // num frame for '-' stats digit
28 cachepic_t *sb_nums[2][11];
29 cachepic_t *sb_colon, *sb_slash;
32 cachepic_t *sb_scorebar;
33 // AK only used by NEX
34 cachepic_t *sb_sbar_minimal;
35 cachepic_t *sb_sbar_overlay;
37 // AK changed the bound to 9
38 cachepic_t *sb_weapons[7][9]; // 0 is active, 1 is owned, 2-5 are flashes
39 cachepic_t *sb_ammo[4];
40 cachepic_t *sb_sigil[4];
41 cachepic_t *sb_armor[3];
42 cachepic_t *sb_items[32];
44 // 0-4 are based on health (in 20 increments)
45 // 0 is static, 1 is temporary animation
46 cachepic_t *sb_faces[5][2];
47 cachepic_t *sb_health; // GAME_NEXUIZ
49 cachepic_t *sb_face_invis;
50 cachepic_t *sb_face_quad;
51 cachepic_t *sb_face_invuln;
52 cachepic_t *sb_face_invis_invuln;
54 qboolean sb_showscores;
56 int sb_lines; // scan lines to draw
58 cachepic_t *rsb_invbar[2];
59 cachepic_t *rsb_weapons[5];
60 cachepic_t *rsb_items[2];
61 cachepic_t *rsb_ammo[3];
62 cachepic_t *rsb_teambord; // PGM 01/19/97 - team color border
64 //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
65 cachepic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes
66 //MED 01/04/97 added array to simplify weapon parsing
67 int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
68 //MED 01/04/97 added hipnotic items array
69 cachepic_t *hsb_items[2];
72 cachepic_t *somsb_health;
73 cachepic_t *somsb_ammo[4];
74 cachepic_t *somsb_armor[3];
76 cachepic_t *zymsb_crosshair_center;
77 cachepic_t *zymsb_crosshair_line;
78 cachepic_t *zymsb_crosshair_health;
79 cachepic_t *zymsb_crosshair_ammo;
80 cachepic_t *zymsb_crosshair_clip;
81 cachepic_t *zymsb_crosshair_background;
82 cachepic_t *zymsb_crosshair_left1;
83 cachepic_t *zymsb_crosshair_left2;
84 cachepic_t *zymsb_crosshair_right;
86 cachepic_t *sb_ranking;
87 cachepic_t *sb_complete;
89 cachepic_t *sb_finale;
91 cvar_t showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
92 cvar_t showsound = {CVAR_SAVE, "showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
93 cvar_t showspeed = {CVAR_SAVE, "showspeed", "0", "shows your current speed (qu per second); number selects unit: 1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots"};
94 cvar_t showtopspeed = {CVAR_SAVE, "showtopspeed", "0", "shows your top speed (kept on screen for max 3 seconds); value -1 takes over the unit from showspeed, otherwise it's an unit number just like in showspeed"};
95 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
96 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
97 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
98 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
99 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
100 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
101 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)"};
102 cvar_t sbar_scorerank = {CVAR_SAVE, "sbar_scorerank", "1", "shows an overlay for your score (or team score) and rank in the scoreboard"};
103 cvar_t sbar_gametime = {CVAR_SAVE, "sbar_gametime", "1", "shows an overlay for the time left in the current match/level (or current game time if there is no timelimit set)"};
104 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"};
105 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
106 cvar_t sbar_flagstatus_pos = {CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
108 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
110 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
111 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
112 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
113 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
114 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
116 void Sbar_MiniDeathmatchOverlay (int x, int y);
117 void Sbar_DeathmatchOverlay (void);
118 void Sbar_IntermissionOverlay (void);
119 void Sbar_FinaleOverlay (void);
121 void CL_VM_UpdateShowingScoresState (int showingscores);
131 void Sbar_ShowScores (void)
135 sb_showscores = true;
136 CL_VM_UpdateShowingScoresState(sb_showscores);
146 void Sbar_DontShowScores (void)
148 sb_showscores = false;
149 CL_VM_UpdateShowingScoresState(sb_showscores);
152 void sbar_start(void)
156 if (gamemode == GAME_DELUXEQUAKE)
159 else if (gamemode == GAME_SOM)
161 sb_disc = Draw_CachePic ("gfx/disc");
163 for (i = 0;i < 10;i++)
164 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
166 somsb_health = Draw_CachePic ("gfx/hud_health");
167 somsb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
168 somsb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
169 somsb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
170 somsb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
171 somsb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
172 somsb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
173 somsb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
175 else if (gamemode == GAME_NEXUIZ)
177 for (i = 0;i < 10;i++)
178 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
179 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
180 sb_colon = Draw_CachePic ("gfx/num_colon");
182 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
183 sb_ammo[1] = Draw_CachePic ("gfx/sb_bullets");
184 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
185 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
187 sb_armor[0] = Draw_CachePic ("gfx/sb_armor");
191 sb_health = Draw_CachePic ("gfx/sb_health");
193 sb_items[2] = Draw_CachePic ("gfx/sb_slowmo");
194 sb_items[3] = Draw_CachePic ("gfx/sb_invinc");
195 sb_items[4] = Draw_CachePic ("gfx/sb_energy");
196 sb_items[5] = Draw_CachePic ("gfx/sb_str");
198 sb_items[11] = Draw_CachePic ("gfx/sb_flag_red_taken");
199 sb_items[12] = Draw_CachePic ("gfx/sb_flag_red_lost");
200 sb_items[13] = Draw_CachePic ("gfx/sb_flag_red_carrying");
201 sb_items[14] = Draw_CachePic ("gfx/sb_key_carrying");
202 sb_items[15] = Draw_CachePic ("gfx/sb_flag_blue_taken");
203 sb_items[16] = Draw_CachePic ("gfx/sb_flag_blue_lost");
204 sb_items[17] = Draw_CachePic ("gfx/sb_flag_blue_carrying");
206 sb_sbar = Draw_CachePic ("gfx/sbar");
207 sb_sbar_minimal = Draw_CachePic ("gfx/sbar_minimal");
208 sb_sbar_overlay = Draw_CachePic ("gfx/sbar_overlay");
210 for(i = 0; i < 9;i++)
211 sb_weapons[0][i] = Draw_CachePic (va("gfx/inv_weapon%i",i));
213 else if (gamemode == GAME_ZYMOTIC)
215 zymsb_crosshair_center = Draw_CachePic ("gfx/hud/crosshair_center");
216 zymsb_crosshair_line = Draw_CachePic ("gfx/hud/crosshair_line");
217 zymsb_crosshair_health = Draw_CachePic ("gfx/hud/crosshair_health");
218 zymsb_crosshair_clip = Draw_CachePic ("gfx/hud/crosshair_clip");
219 zymsb_crosshair_ammo = Draw_CachePic ("gfx/hud/crosshair_ammo");
220 zymsb_crosshair_background = Draw_CachePic ("gfx/hud/crosshair_background");
221 zymsb_crosshair_left1 = Draw_CachePic ("gfx/hud/crosshair_left1");
222 zymsb_crosshair_left2 = Draw_CachePic ("gfx/hud/crosshair_left2");
223 zymsb_crosshair_right = Draw_CachePic ("gfx/hud/crosshair_right");
227 sb_disc = Draw_CachePic ("gfx/disc");
229 for (i = 0;i < 10;i++)
231 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
232 sb_nums[1][i] = Draw_CachePic (va("gfx/anum_%i",i));
235 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
236 sb_nums[1][10] = Draw_CachePic ("gfx/anum_minus");
238 sb_colon = Draw_CachePic ("gfx/num_colon");
239 sb_slash = Draw_CachePic ("gfx/num_slash");
241 sb_weapons[0][0] = Draw_CachePic ("gfx/inv_shotgun");
242 sb_weapons[0][1] = Draw_CachePic ("gfx/inv_sshotgun");
243 sb_weapons[0][2] = Draw_CachePic ("gfx/inv_nailgun");
244 sb_weapons[0][3] = Draw_CachePic ("gfx/inv_snailgun");
245 sb_weapons[0][4] = Draw_CachePic ("gfx/inv_rlaunch");
246 sb_weapons[0][5] = Draw_CachePic ("gfx/inv_srlaunch");
247 sb_weapons[0][6] = Draw_CachePic ("gfx/inv_lightng");
249 sb_weapons[1][0] = Draw_CachePic ("gfx/inv2_shotgun");
250 sb_weapons[1][1] = Draw_CachePic ("gfx/inv2_sshotgun");
251 sb_weapons[1][2] = Draw_CachePic ("gfx/inv2_nailgun");
252 sb_weapons[1][3] = Draw_CachePic ("gfx/inv2_snailgun");
253 sb_weapons[1][4] = Draw_CachePic ("gfx/inv2_rlaunch");
254 sb_weapons[1][5] = Draw_CachePic ("gfx/inv2_srlaunch");
255 sb_weapons[1][6] = Draw_CachePic ("gfx/inv2_lightng");
257 for (i = 0;i < 5;i++)
259 sb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_shotgun",i+1));
260 sb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_sshotgun",i+1));
261 sb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_nailgun",i+1));
262 sb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_snailgun",i+1));
263 sb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_rlaunch",i+1));
264 sb_weapons[2+i][5] = Draw_CachePic (va("gfx/inva%i_srlaunch",i+1));
265 sb_weapons[2+i][6] = Draw_CachePic (va("gfx/inva%i_lightng",i+1));
268 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
269 sb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
270 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
271 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
273 sb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
274 sb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
275 sb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
277 sb_items[0] = Draw_CachePic ("gfx/sb_key1");
278 sb_items[1] = Draw_CachePic ("gfx/sb_key2");
279 sb_items[2] = Draw_CachePic ("gfx/sb_invis");
280 sb_items[3] = Draw_CachePic ("gfx/sb_invuln");
281 sb_items[4] = Draw_CachePic ("gfx/sb_suit");
282 sb_items[5] = Draw_CachePic ("gfx/sb_quad");
284 sb_sigil[0] = Draw_CachePic ("gfx/sb_sigil1");
285 sb_sigil[1] = Draw_CachePic ("gfx/sb_sigil2");
286 sb_sigil[2] = Draw_CachePic ("gfx/sb_sigil3");
287 sb_sigil[3] = Draw_CachePic ("gfx/sb_sigil4");
289 sb_faces[4][0] = Draw_CachePic ("gfx/face1");
290 sb_faces[4][1] = Draw_CachePic ("gfx/face_p1");
291 sb_faces[3][0] = Draw_CachePic ("gfx/face2");
292 sb_faces[3][1] = Draw_CachePic ("gfx/face_p2");
293 sb_faces[2][0] = Draw_CachePic ("gfx/face3");
294 sb_faces[2][1] = Draw_CachePic ("gfx/face_p3");
295 sb_faces[1][0] = Draw_CachePic ("gfx/face4");
296 sb_faces[1][1] = Draw_CachePic ("gfx/face_p4");
297 sb_faces[0][0] = Draw_CachePic ("gfx/face5");
298 sb_faces[0][1] = Draw_CachePic ("gfx/face_p5");
300 sb_face_invis = Draw_CachePic ("gfx/face_invis");
301 sb_face_invuln = Draw_CachePic ("gfx/face_invul2");
302 sb_face_invis_invuln = Draw_CachePic ("gfx/face_inv2");
303 sb_face_quad = Draw_CachePic ("gfx/face_quad");
305 sb_sbar = Draw_CachePic ("gfx/sbar");
306 sb_ibar = Draw_CachePic ("gfx/ibar");
307 sb_scorebar = Draw_CachePic ("gfx/scorebar");
309 //MED 01/04/97 added new hipnotic weapons
310 if (gamemode == GAME_HIPNOTIC)
312 hsb_weapons[0][0] = Draw_CachePic ("gfx/inv_laser");
313 hsb_weapons[0][1] = Draw_CachePic ("gfx/inv_mjolnir");
314 hsb_weapons[0][2] = Draw_CachePic ("gfx/inv_gren_prox");
315 hsb_weapons[0][3] = Draw_CachePic ("gfx/inv_prox_gren");
316 hsb_weapons[0][4] = Draw_CachePic ("gfx/inv_prox");
318 hsb_weapons[1][0] = Draw_CachePic ("gfx/inv2_laser");
319 hsb_weapons[1][1] = Draw_CachePic ("gfx/inv2_mjolnir");
320 hsb_weapons[1][2] = Draw_CachePic ("gfx/inv2_gren_prox");
321 hsb_weapons[1][3] = Draw_CachePic ("gfx/inv2_prox_gren");
322 hsb_weapons[1][4] = Draw_CachePic ("gfx/inv2_prox");
324 for (i = 0;i < 5;i++)
326 hsb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_laser",i+1));
327 hsb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_mjolnir",i+1));
328 hsb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_gren_prox",i+1));
329 hsb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_prox_gren",i+1));
330 hsb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_prox",i+1));
333 hsb_items[0] = Draw_CachePic ("gfx/sb_wsuit");
334 hsb_items[1] = Draw_CachePic ("gfx/sb_eshld");
336 else if (gamemode == GAME_ROGUE)
338 rsb_invbar[0] = Draw_CachePic ("gfx/r_invbar1");
339 rsb_invbar[1] = Draw_CachePic ("gfx/r_invbar2");
341 rsb_weapons[0] = Draw_CachePic ("gfx/r_lava");
342 rsb_weapons[1] = Draw_CachePic ("gfx/r_superlava");
343 rsb_weapons[2] = Draw_CachePic ("gfx/r_gren");
344 rsb_weapons[3] = Draw_CachePic ("gfx/r_multirock");
345 rsb_weapons[4] = Draw_CachePic ("gfx/r_plasma");
347 rsb_items[0] = Draw_CachePic ("gfx/r_shield1");
348 rsb_items[1] = Draw_CachePic ("gfx/r_agrav1");
350 // PGM 01/19/97 - team color border
351 rsb_teambord = Draw_CachePic ("gfx/r_teambord");
352 // PGM 01/19/97 - team color border
354 rsb_ammo[0] = Draw_CachePic ("gfx/r_ammolava");
355 rsb_ammo[1] = Draw_CachePic ("gfx/r_ammomulti");
356 rsb_ammo[2] = Draw_CachePic ("gfx/r_ammoplasma");
360 sb_ranking = Draw_CachePic ("gfx/ranking");
361 sb_complete = Draw_CachePic ("gfx/complete");
362 sb_inter = Draw_CachePic ("gfx/inter");
363 sb_finale = Draw_CachePic ("gfx/finale");
366 void sbar_shutdown(void)
370 void sbar_newmap(void)
374 void Sbar_Init (void)
376 Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
377 Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
378 Cvar_RegisterVariable(&showfps);
379 Cvar_RegisterVariable(&showsound);
380 Cvar_RegisterVariable(&showspeed);
381 Cvar_RegisterVariable(&showtopspeed);
382 Cvar_RegisterVariable(&showtime);
383 Cvar_RegisterVariable(&showtime_format);
384 Cvar_RegisterVariable(&showdate);
385 Cvar_RegisterVariable(&showdate_format);
386 Cvar_RegisterVariable(&sbar_alpha_bg);
387 Cvar_RegisterVariable(&sbar_alpha_fg);
388 Cvar_RegisterVariable(&sbar_hudselector);
389 Cvar_RegisterVariable(&sbar_scorerank);
390 Cvar_RegisterVariable(&sbar_gametime);
391 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
392 Cvar_RegisterVariable(&cl_deathscoreboard);
394 Cvar_RegisterVariable(&crosshair_color_red);
395 Cvar_RegisterVariable(&crosshair_color_green);
396 Cvar_RegisterVariable(&crosshair_color_blue);
397 Cvar_RegisterVariable(&crosshair_color_alpha);
398 Cvar_RegisterVariable(&crosshair_size);
400 if(gamemode == GAME_NEXUIZ)
402 Cvar_RegisterVariable(&sbar_flagstatus_right); // this cvar makes no sense in other games
403 Cvar_RegisterVariable(&sbar_flagstatus_pos); // this cvar makes no sense in other games
406 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap);
410 //=============================================================================
412 // drawing routines are relative to the status bar location
421 void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
423 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
426 void Sbar_DrawPic (int x, int y, cachepic_t *pic)
428 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
431 void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
433 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
440 Draws one solid graphics character
443 void Sbar_DrawCharacter (int x, int y, int num)
445 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);
453 void Sbar_DrawString (int x, int y, char *str)
455 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);
463 void Sbar_DrawNum (int x, int y, int num, int digits, int color)
468 l = dpsnprintf(str, sizeof(str), "%i", num);
482 Sbar_DrawPic (x, y, sb_nums[color][frame]);
495 void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
503 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
506 l = dpsnprintf(str, sizeof(str), "%i", num);
511 x += (digits-l) * lettersize;
520 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
527 //=============================================================================
530 int Sbar_IsTeammatch()
532 // currently only nexuiz uses the team score board
533 return ((gamemode == GAME_NEXUIZ)
534 && (teamplay.integer > 0));
542 static int fragsort[MAX_SCOREBOARD];
543 static int scoreboardlines;
545 int Sbar_GetSortedPlayerIndex (int index)
547 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
550 static scoreboard_t teams[MAX_SCOREBOARD];
551 static int teamsort[MAX_SCOREBOARD];
552 static int teamlines;
553 void Sbar_SortFrags (void)
559 for (i=0 ; i<cl.maxclients ; i++)
561 if (cl.scores[i].name[0])
563 fragsort[scoreboardlines] = i;
568 for (i=0 ; i<scoreboardlines ; i++)
569 for (j=0 ; j<scoreboardlines-1-i ; j++)
570 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
573 fragsort[j] = fragsort[j+1];
578 if (Sbar_IsTeammatch ())
580 // now sort players by teams.
581 for (i=0 ; i<scoreboardlines ; i++)
583 for (j=0 ; j<scoreboardlines-1-i ; j++)
585 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
588 fragsort[j] = fragsort[j+1];
594 // calculate team scores
596 for (i=0 ; i<scoreboardlines ; i++)
598 if (color != (cl.scores[fragsort[i]].colors & 15))
600 const char* teamname;
602 color = cl.scores[fragsort[i]].colors & 15;
608 teamname = "^1Red Team";
611 teamname = "^4Blue Team";
614 teamname = "^6Pink Team";
617 teamname = "^3Yellow Team";
620 teamname = "Total Team Score";
623 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
625 teams[teamlines-1].frags = 0;
626 teams[teamlines-1].colors = color + 16 * color;
629 if (cl.scores[fragsort[i]].frags != -666)
631 // do not add spedcators
632 // (ugly hack for nexuiz)
633 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
637 // now sort teams by scores.
638 for (i=0 ; i<teamlines ; i++)
640 for (i=0 ; i<teamlines ; i++)
642 for (j=0 ; j<teamlines-1-i ; j++)
644 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
647 teamsort[j] = teamsort[j+1];
660 void Sbar_SoloScoreboard (void)
663 char str[80], timestr[40];
665 int minutes, seconds;
668 t = (cl.intermission ? cl.completed_time : cl.time);
669 minutes = (int)(t / 60);
670 seconds = (int)(t - 60*floor(t/60));
672 // monsters and secrets are now both on the top row
673 if (cl.stats[STAT_TOTALMONSTERS])
674 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
675 if (cl.stats[STAT_TOTALSECRETS])
676 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
678 // figure out the map's filename without path or extension
679 strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
680 if (strrchr(str, '.'))
681 *(strrchr(str, '.')) = 0;
683 // append a : separator and then the full title
684 strlcat(str, ":", sizeof(str));
685 strlcat(str, cl.levelname, sizeof(str));
687 // if there's a newline character, terminate the string there
688 if (strchr(str, '\n'))
689 *(strchr(str, '\n')) = 0;
691 // make the time string
692 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
694 // truncate the level name if necessary to make room for time
696 if ((int)strlen(str) > max)
699 // print the filename and message
700 Sbar_DrawString(8, 12, str);
703 Sbar_DrawString(8 + max*8, 12, timestr);
707 int minutes, seconds, tens, units;
710 if (gamemode != GAME_NEXUIZ) {
711 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
712 Sbar_DrawString (8, 4, str);
714 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
715 Sbar_DrawString (8, 12, str);
719 minutes = (int)(cl.time / 60);
720 seconds = (int)(cl.time - 60*minutes);
722 units = seconds - 10*tens;
723 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
724 Sbar_DrawString (184, 4, str);
727 if (gamemode == GAME_NEXUIZ) {
728 l = (int) strlen (cl.worldmodel->name);
729 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
731 l = (int) strlen (cl.levelname);
732 Sbar_DrawString (232 - l*4, 12, cl.levelname);
742 void Sbar_DrawScoreboard (void)
744 Sbar_SoloScoreboard ();
745 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
746 //if (cl.gametype == GAME_DEATHMATCH)
748 Sbar_DeathmatchOverlay ();
751 //=============================================================================
753 // AK to make DrawInventory smaller
754 static void Sbar_DrawWeapon(int nr, float fade, int active)
756 if (sbar_hudselector.integer == 1)
758 // width = 300, height = 100
759 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
761 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);
763 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);
767 // width = 300, height = 100
768 const int w_width = 300, w_height = 100, w_space = 10;
769 const float w_scale = 0.4;
771 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);
772 //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);
781 void Sbar_DrawInventory (void)
788 if (gamemode == GAME_ROGUE)
790 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
791 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
793 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
796 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
799 for (i=0 ; i<7 ; i++)
801 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
803 time = cl.item_gettime[i];
804 flashon = (int)(max(0, cl.time - time)*10);
807 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
813 flashon = (flashon%5) + 2;
815 Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
821 if (gamemode == GAME_HIPNOTIC)
823 int grenadeflashing=0;
824 for (i=0 ; i<4 ; i++)
826 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
828 time = max(0, cl.item_gettime[hipweapons[i]]);
829 flashon = (int)((cl.time - time)*10);
832 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
838 flashon = (flashon%5) + 2;
840 // check grenade launcher
843 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
848 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
854 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
856 if (!grenadeflashing)
857 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
860 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
863 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
868 if (gamemode == GAME_ROGUE)
870 // check for powered up weapon.
871 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
873 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
874 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
878 for (i=0 ; i<4 ; i++)
880 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
882 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
884 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
886 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
888 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
892 for (i=0 ; i<6 ; i++)
893 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
895 //MED 01/04/97 changed keys
896 if (gamemode != GAME_HIPNOTIC || (i>1))
897 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
900 //MED 01/04/97 added hipnotic items
902 if (gamemode == GAME_HIPNOTIC)
904 for (i=0 ; i<2 ; i++)
905 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
906 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
909 if (gamemode == GAME_ROGUE)
912 for (i=0 ; i<2 ; i++)
913 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
914 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
919 for (i=0 ; i<4 ; i++)
920 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
921 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
925 //=============================================================================
932 void Sbar_DrawFrags (void)
942 l = min(scoreboardlines, 4);
946 for (i = 0;i < l;i++)
952 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
953 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);
954 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
955 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);
959 dpsnprintf (num, sizeof(num), "%3i",f);
961 if (k == cl.viewentity - 1)
963 Sbar_DrawCharacter ( x + 2, -24, 16);
964 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
966 Sbar_DrawCharacter (x + 8, -24, num[0]);
967 Sbar_DrawCharacter (x + 16, -24, num[1]);
968 Sbar_DrawCharacter (x + 24, -24, num[2]);
973 //=============================================================================
981 void Sbar_DrawFace (void)
985 // PGM 01/19/97 - team color drawing
986 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
987 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
993 s = &cl.scores[cl.viewentity - 1];
995 Sbar_DrawPic (112, 0, rsb_teambord);
996 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
997 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);
998 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
999 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);
1003 dpsnprintf (num, sizeof(num), "%3i",f);
1005 if ((s->colors & 0xf0)==0)
1008 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1010 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1012 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1016 Sbar_DrawCharacter ( 109, 3, num[0]);
1017 Sbar_DrawCharacter ( 116, 3, num[1]);
1018 Sbar_DrawCharacter ( 123, 3, num[2]);
1023 // PGM 01/19/97 - team color drawing
1025 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1026 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1027 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1028 Sbar_DrawPic (112, 0, sb_face_quad );
1029 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1030 Sbar_DrawPic (112, 0, sb_face_invis );
1031 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1032 Sbar_DrawPic (112, 0, sb_face_invuln);
1035 f = cl.stats[STAT_HEALTH] / 20;
1037 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1040 double topspeed = 0;
1041 double topspeedxy = 0;
1042 time_t current_time = 3;
1043 time_t top_time = 0;
1044 time_t topxy_time = 0;
1046 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1049 unitnumber = showspeed.integer;
1054 if(gamemode == GAME_NEXUIZ)
1058 *conversion_factor = 1.0;
1062 *conversion_factor = 0.0254;
1063 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1064 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1068 *conversion_factor = 0.0254 * 3.6;
1069 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1073 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1074 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1078 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1079 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1084 void Sbar_ShowFPS(void)
1086 float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1087 char soundstring[32];
1089 char timestring[32];
1090 char datestring[32];
1091 char speedstring[32];
1092 char topspeedstring[48];
1093 qboolean red = false;
1099 topspeedstring[0] = 0;
1100 if (showfps.integer)
1103 static double nexttime = 0, lasttime = 0;
1104 static double framerate = 0;
1105 static int framecount = 0;
1106 double interval = 0.25;
1109 if (newtime >= nexttime)
1111 framerate = framecount / (newtime - lasttime);
1112 if (nexttime < newtime - interval * 1.5)
1115 nexttime += interval;
1121 if ((red = (calc < 1.0f)))
1122 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1124 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1126 if (showtime.integer)
1127 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1128 if (showdate.integer)
1129 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1130 if (showsound.integer)
1131 dpsnprintf(soundstring, sizeof(soundstring), "%4i/%4i at %3ims\n", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1132 if (showspeed.integer || showtopspeed.integer)
1134 double speed, speedxy, f;
1136 speed = VectorLength(cl.movement_velocity);
1137 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1138 if (showspeed.integer)
1140 get_showspeed_unit(showspeed.integer, &f, &unit);
1141 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1143 if (showtopspeed.integer)
1145 qboolean topspeed_latched = false, topspeedxy_latched = false;
1146 get_showspeed_unit(showtopspeed.integer, &f, &unit);
1147 if (speed >= topspeed || current_time - top_time > 3)
1153 topspeed_latched = true;
1154 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1156 topspeedxy = speedxy;
1160 topspeedxy_latched = true;
1161 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1162 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1163 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1165 time(¤t_time);
1168 if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0] || topspeedstring[0])
1172 fps_height = fps_scaley * ((soundstring[0] != 0) + (fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0) + (topspeedstring[0] != 0));
1173 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1174 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1175 fps_y = vid_conheight.integer - fps_height;
1178 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(soundstring, 0, true, FONT_INFOBAR) * fps_scalex;
1179 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1180 DrawQ_String_Font(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1181 fps_y += fps_scaley;
1185 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1186 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1188 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1190 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1191 fps_y += fps_scaley;
1195 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1196 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1197 DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1198 fps_y += fps_scaley;
1202 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1203 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1204 DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1205 fps_y += fps_scaley;
1209 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1210 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1211 DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1212 fps_y += fps_scaley;
1214 if (topspeedstring[0])
1216 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(topspeedstring, 0, false, FONT_INFOBAR) * fps_scalex;
1217 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1218 DrawQ_String_Font(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1219 fps_y += fps_scaley;
1224 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)
1227 c2 = bound(0, c2, 1);
1228 c1 = bound(0, c1, 1 - c2);
1230 r[1] = rangey + rangeheight * (c2 + c1);
1231 r[2] = rangey + rangeheight * (c2);
1235 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);
1237 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);
1239 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);
1241 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);
1249 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1250 extern cvar_t v_kicktime;
1251 void Sbar_Score (int margin);
1252 void Sbar_Draw (void)
1256 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1259 Sbar_DrawScoreboard ();
1260 else if (cl.intermission == 1)
1262 if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1264 Sbar_DrawScoreboard();
1267 Sbar_IntermissionOverlay();
1269 else if (cl.intermission == 2)
1270 Sbar_FinaleOverlay();
1271 else if (gamemode == GAME_DELUXEQUAKE)
1274 else if (gamemode == GAME_SOM)
1276 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1277 Sbar_DrawScoreboard ();
1280 // this is the top left of the sbar area
1282 sbar_y = vid_conheight.integer - 24*3;
1285 if (cl.stats[STAT_ARMOR])
1287 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1288 Sbar_DrawPic(0, 0, somsb_armor[2]);
1289 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1290 Sbar_DrawPic(0, 0, somsb_armor[1]);
1291 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1292 Sbar_DrawPic(0, 0, somsb_armor[0]);
1293 Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1297 Sbar_DrawPic(0, 24, somsb_health);
1298 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1301 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1302 Sbar_DrawPic(0, 48, somsb_ammo[0]);
1303 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1304 Sbar_DrawPic(0, 48, somsb_ammo[1]);
1305 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1306 Sbar_DrawPic(0, 48, somsb_ammo[2]);
1307 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1308 Sbar_DrawPic(0, 48, somsb_ammo[3]);
1309 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1310 if (cl.stats[STAT_SHELLS])
1311 Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1314 else if (gamemode == GAME_NEXUIZ)
1316 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1318 sbar_x = (vid_conwidth.integer - 640)/2;
1319 sbar_y = vid_conheight.integer - 47;
1320 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1321 Sbar_DrawScoreboard ();
1323 else if (sb_lines && sbar_hudselector.integer == 1)
1327 int redflag, blueflag;
1330 sbar_x = (vid_conwidth.integer - 320)/2;
1331 sbar_y = vid_conheight.integer - 24 - 16;
1333 // calculate intensity to draw weapons bar at
1334 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1335 fade = bound(0.7, fade, 1);
1336 for (i = 0; i < 8;i++)
1337 if (cl.stats[STAT_ITEMS] & (1 << i))
1338 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1339 if((cl.stats[STAT_ITEMS] & (1<<12)))
1340 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1343 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1344 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1345 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1346 if (redflag == 3 && blueflag == 3)
1348 // The Impossible Combination[tm]
1349 // Can only happen in Key Hunt mode...
1350 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1355 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1357 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1361 if (cl.stats[STAT_ARMOR] > 0)
1363 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1364 if(cl.stats[STAT_ARMOR] > 200)
1365 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1366 else if(cl.stats[STAT_ARMOR] > 100)
1367 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1368 else if(cl.stats[STAT_ARMOR] > 50)
1369 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1370 else if(cl.stats[STAT_ARMOR] > 25)
1371 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1373 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1377 if (cl.stats[STAT_HEALTH] != 0)
1379 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1380 if(cl.stats[STAT_HEALTH] > 200)
1381 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1382 else if(cl.stats[STAT_HEALTH] > 100)
1383 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1384 else if(cl.stats[STAT_HEALTH] > 50)
1385 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1386 else if(cl.stats[STAT_HEALTH] > 25)
1387 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1389 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1393 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1395 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1396 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1397 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1398 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1399 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1400 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1401 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1402 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1403 if(cl.stats[STAT_AMMO] > 10)
1404 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1406 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1409 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1410 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1413 // The margin can be at most 8 to support 640x480 console size:
1414 // 320 + 2 * (144 + 16) = 640
1420 int redflag, blueflag;
1423 sbar_x = (vid_conwidth.integer - 640)/2;
1424 sbar_y = vid_conheight.integer - 47;
1426 // calculate intensity to draw weapons bar at
1427 fade = 3 - 2 * (cl.time - cl.weapontime);
1430 fade = min(fade, 1);
1431 for (i = 0; i < 8;i++)
1432 if (cl.stats[STAT_ITEMS] & (1 << i))
1433 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1435 if((cl.stats[STAT_ITEMS] & (1<<12)))
1436 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1439 //if (!cl.islocalgame)
1440 // Sbar_DrawFrags ();
1443 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1445 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1448 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1449 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1450 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1451 if (redflag == 3 && blueflag == 3)
1453 // The Impossible Combination[tm]
1454 // Can only happen in Key Hunt mode...
1455 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1460 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1462 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1466 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1469 if(cl.stats[STAT_HEALTH] > 100)
1470 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1471 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1472 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1474 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1476 // AK dont draw ammo for the laser
1477 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1479 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1480 Sbar_DrawPic (519, 0, sb_ammo[0]);
1481 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1482 Sbar_DrawPic (519, 0, sb_ammo[1]);
1483 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1484 Sbar_DrawPic (519, 0, sb_ammo[2]);
1485 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1486 Sbar_DrawPic (519, 0, sb_ammo[3]);
1488 if(cl.stats[STAT_AMMO] <= 10)
1489 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1491 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1496 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1498 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1499 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1504 // Mini scoreboard uses 12*4 per other team, that is, 144
1505 // pixels when there are four teams...
1506 // Nexuiz by default sets vid_conwidth to 800... makes
1508 // so we need to shift it by 64 pixels to the right to fit
1509 // BUT: then it overlaps with the image that gets drawn
1510 // for viewsize 100! Therefore, just account for 3 teams,
1511 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1515 else if (gamemode == GAME_ZYMOTIC)
1518 float scale = 64.0f / 256.0f;
1519 float kickoffset[3];
1520 VectorClear(kickoffset);
1523 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1524 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1526 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1527 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1528 // left1 16, 48 : 126 -66
1529 // left2 16, 128 : 196 -66
1530 // right 176, 48 : 196 -136
1531 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);
1532 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);
1533 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);
1534 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1536 float scale = 128.0f / 256.0f;
1537 float healthstart, healthheight, healthstarttc, healthendtc;
1538 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1539 float ammostart, ammoheight, ammostarttc, ammoendtc;
1540 float clipstart, clipheight, clipstarttc, clipendtc;
1541 float kickoffset[3], offset;
1542 VectorClear(kickoffset);
1545 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1546 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1548 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1549 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1550 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1551 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);
1552 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);
1553 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);
1554 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);
1555 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1556 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1557 healthstart = 204 - healthheight;
1558 shieldstart = healthstart - shieldheight;
1559 healthstarttc = healthstart * (1.0f / 256.0f);
1560 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1561 shieldstarttc = shieldstart * (1.0f / 256.0f);
1562 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1563 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1564 ammostart = 114 - ammoheight;
1565 ammostarttc = ammostart * (1.0f / 256.0f);
1566 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1567 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1568 clipstart = 190 - clipheight;
1569 clipstarttc = clipstart * (1.0f / 256.0f);
1570 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1571 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);
1572 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);
1573 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);
1574 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);
1575 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1576 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1579 else // Quake and others
1581 sbar_x = (vid_conwidth.integer - 320)/2;
1582 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1583 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1584 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1588 if (gamemode != GAME_GOODVSBAD2)
1589 Sbar_DrawInventory ();
1590 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1594 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1596 if (gamemode != GAME_GOODVSBAD2)
1597 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1598 Sbar_DrawScoreboard ();
1602 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1604 // keys (hipnotic only)
1605 //MED 01/04/97 moved keys here so they would not be overwritten
1606 if (gamemode == GAME_HIPNOTIC)
1608 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1609 Sbar_DrawPic (209, 3, sb_items[0]);
1610 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1611 Sbar_DrawPic (209, 12, sb_items[1]);
1614 if (gamemode != GAME_GOODVSBAD2)
1616 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1618 Sbar_DrawNum (24, 0, 666, 3, 1);
1619 Sbar_DrawPic (0, 0, sb_disc);
1623 if (gamemode == GAME_ROGUE)
1625 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1626 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1627 Sbar_DrawPic (0, 0, sb_armor[2]);
1628 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1629 Sbar_DrawPic (0, 0, sb_armor[1]);
1630 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1631 Sbar_DrawPic (0, 0, sb_armor[0]);
1635 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1636 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1637 Sbar_DrawPic (0, 0, sb_armor[2]);
1638 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1639 Sbar_DrawPic (0, 0, sb_armor[1]);
1640 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1641 Sbar_DrawPic (0, 0, sb_armor[0]);
1650 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1653 if (gamemode == GAME_ROGUE)
1655 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1656 Sbar_DrawPic (224, 0, sb_ammo[0]);
1657 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1658 Sbar_DrawPic (224, 0, sb_ammo[1]);
1659 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1660 Sbar_DrawPic (224, 0, sb_ammo[2]);
1661 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1662 Sbar_DrawPic (224, 0, sb_ammo[3]);
1663 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1664 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1665 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1666 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1667 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1668 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1672 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1673 Sbar_DrawPic (224, 0, sb_ammo[0]);
1674 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1675 Sbar_DrawPic (224, 0, sb_ammo[1]);
1676 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1677 Sbar_DrawPic (224, 0, sb_ammo[2]);
1678 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1679 Sbar_DrawPic (224, 0, sb_ammo[3]);
1682 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1684 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1685 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1687 if (gamemode == GAME_TRANSFUSION)
1688 Sbar_MiniDeathmatchOverlay (0, 0);
1690 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1699 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1701 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1702 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);
1705 if (cl_prydoncursor.integer)
1706 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)), 0, 0, 1, 1, 1, 1, 0);
1709 //=============================================================================
1713 Sbar_DeathmatchOverlay
1717 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1720 qboolean myself = false;
1722 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1724 if((s - cl.scores) == cl.playerentity - 1)
1726 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1727 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1730 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1732 if (s->qw_spectator)
1734 if (s->qw_ping || s->qw_packetloss)
1735 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 );
1737 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 );
1741 // draw colors behind score
1747 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1748 DrawQ_Fill(x + 14*8*FONT_SBAR->maxwidth, y+1, 40*FONT_SBAR->maxwidth, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1749 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1750 DrawQ_Fill(x + 14*8*FONT_SBAR->maxwidth, y+4, 40*FONT_SBAR->maxwidth, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1752 //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);
1753 if (s->qw_ping || s->qw_packetloss)
1754 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 );
1756 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 );
1761 if (s->qw_spectator)
1763 if (s->qw_ping || s->qw_packetloss)
1764 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 );
1766 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 );
1770 // draw colors behind score
1771 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1772 DrawQ_Fill(x + 9*8*FONT_SBAR->maxwidth, y+1, 40*FONT_SBAR->maxwidth, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1773 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1774 DrawQ_Fill(x + 9*8*FONT_SBAR->maxwidth, y+4, 40*FONT_SBAR->maxwidth, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1776 //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);
1777 if (s->qw_ping || s->qw_packetloss)
1778 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 );
1780 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 );
1786 void Sbar_DeathmatchOverlay (void)
1788 int i, y, xmin, xmax, ymin, ymax;
1790 // request new ping times every two second
1791 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1793 cl.last_ping_request = realtime;
1794 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1796 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1797 MSG_WriteString(&cls.netcon->message, "pings");
1799 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*/)
1801 // 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
1802 static int ping_anyway_counter = 0;
1803 if(cl.parsingtextexpectingpingforscores == 1)
1805 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1806 if(++ping_anyway_counter >= 5)
1807 cl.parsingtextexpectingpingforscores = 0;
1809 if(cl.parsingtextexpectingpingforscores != 1)
1811 ping_anyway_counter = 0;
1812 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1813 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1814 MSG_WriteString(&cls.netcon->message, "ping");
1819 // 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
1820 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1821 MSG_WriteString(&cls.netcon->message, "pings");
1829 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1831 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1832 xmin = (int) (vid_conwidth.integer - (26 + 15) * 8 * FONT_SBAR->maxwidth) / 2; // 26 characters until name, then we assume 15 character names (they can be longer but usually aren't)
1834 xmin = (int) (vid_conwidth.integer - (16 + 25) * 8 * FONT_SBAR->maxwidth) / 2; // 16 characters until name, then we assume 25 character names (they can be longer but usually aren't)
1835 xmax = vid_conwidth.integer - xmin;
1837 if(gamemode == GAME_NEXUIZ)
1838 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);
1840 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1844 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1846 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 );
1850 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 );
1854 if (Sbar_IsTeammatch ())
1856 // show team scores first
1857 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1858 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1862 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1863 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1868 Sbar_MiniDeathmatchOverlay
1872 void Sbar_MiniDeathmatchOverlay (int x, int y)
1874 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1876 // do not draw this if sbar_miniscoreboard_size is zero
1877 if(sbar_miniscoreboard_size.value == 0)
1879 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1880 if(sbar_miniscoreboard_size.value > 0)
1881 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1886 // decide where to print
1887 if (gamemode == GAME_TRANSFUSION)
1888 numlines = (vid_conwidth.integer - x + 127) / 128;
1890 numlines = (vid_conheight.integer - y + 7) / 8;
1892 // give up if there isn't room
1893 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1897 for (i = 0; i < scoreboardlines; i++)
1898 if (fragsort[i] == cl.playerentity - 1)
1902 range_end = scoreboardlines;
1905 if (gamemode != GAME_TRANSFUSION)
1906 if (Sbar_IsTeammatch ())
1908 // reserve space for the team scores
1909 numlines -= teamlines;
1911 // find first and last player of my team (only draw the team totals and my own team)
1912 range_begin = range_end = i;
1913 myteam = cl.scores[fragsort[i]].colors & 15;
1914 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1916 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1919 // looks better than two players
1929 i = min(i, range_end - numlines);
1930 i = max(i, range_begin);
1932 if (gamemode == GAME_TRANSFUSION)
1934 for (;i < range_end && x < vid_conwidth.integer;i++)
1935 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1939 if(range_end - i < numlines) // won't draw to bottom?
1940 y += 8 * (numlines - (range_end - i)); // bottom align
1941 // show team scores first
1942 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1943 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1945 for (;i < range_end && y < vid_conheight.integer;i++)
1946 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1950 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1952 static int const sortorder[16] =
1971 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1972 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1973 int tc1 = sortorder[t1->colors & 15];
1974 int tc2 = sortorder[t2->colors & 15];
1978 void Sbar_Score (int margin)
1980 int i, me, score, otherleader, place, distribution, minutes, seconds;
1982 int sbar_x_save = sbar_x;
1983 int sbar_y_save = sbar_y;
1986 sbar_y = (int) (vid_conheight.value - (32+12));
1989 me = cl.playerentity - 1;
1990 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
1992 if(Sbar_IsTeammatch())
1996 // team1 team3 team4
2000 scoreboard_t *teamcolorsort[16];
2003 for(i = 0; i < teamlines; ++i)
2004 teamcolorsort[i] = &(teams[i]);
2006 // Now sort them by color
2007 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2010 // -12*4: four digits space
2011 place = (teamlines - 1) * (-12 * 4);
2013 for(i = 0; i < teamlines; ++i)
2015 int cindex = teamcolorsort[i]->colors & 15;
2016 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2017 float cm = max(max(c[0], c[1]), c[2]);
2018 float cr = c[0] / cm;
2019 float cg = c[1] / cm;
2020 float cb = c[2] / cm;
2021 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2023 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2027 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2040 // find leading score other than ourselves, to calculate distribution
2041 // find our place in the scoreboard
2042 score = cl.scores[me].frags;
2043 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2045 if (cl.scores[i].name[0] && i != me)
2047 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2049 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2053 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2055 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2056 else if (place == 2)
2057 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2059 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2060 if (otherleader < 0)
2061 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2062 if (distribution >= 0)
2064 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2065 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2067 else if (distribution >= -5)
2069 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2070 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2074 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2075 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2080 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2082 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2083 minutes = (int)floor(timeleft / 60);
2084 seconds = (int)(floor(timeleft) - minutes * 60);
2087 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2088 if(sb_colon && sb_colon->tex != r_texture_notexture)
2089 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2090 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2092 else if (minutes >= 1)
2094 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2095 if(sb_colon && sb_colon->tex != r_texture_notexture)
2096 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2097 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2099 else if ((int)(timeleft * 4) & 1)
2100 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2102 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2104 else if (sbar_gametime.integer)
2106 minutes = (int)floor(cl.time / 60);
2107 seconds = (int)(floor(cl.time) - minutes * 60);
2108 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2109 if(sb_colon && sb_colon->tex != r_texture_notexture)
2110 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2111 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2114 sbar_x = sbar_x_save;
2115 sbar_y = sbar_y_save;
2120 Sbar_IntermissionOverlay
2124 void Sbar_IntermissionOverlay (void)
2129 if (cl.gametype == GAME_DEATHMATCH)
2131 Sbar_DeathmatchOverlay ();
2135 sbar_x = (vid_conwidth.integer - 320) >> 1;
2136 sbar_y = (vid_conheight.integer - 200) >> 1;
2138 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2139 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2142 dig = (int)cl.completed_time / 60;
2143 Sbar_DrawNum (160, 64, dig, 3, 0);
2144 num = (int)cl.completed_time - dig*60;
2145 Sbar_DrawPic (234,64,sb_colon);
2146 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2147 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2149 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2150 if (gamemode != GAME_NEXUIZ)
2151 Sbar_DrawPic (232, 104, sb_slash);
2152 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2154 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2155 if (gamemode != GAME_NEXUIZ)
2156 Sbar_DrawPic (232, 144, sb_slash);
2157 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2168 void Sbar_FinaleOverlay (void)
2170 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);