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"};
107 cvar_t sbar_info_pos = {CVAR_SAVE, "sbar_info_pos", "0", "pixel position of the info strings (such as showfps), from the bottom"};
109 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
111 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
112 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
113 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
114 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
115 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
117 void Sbar_MiniDeathmatchOverlay (int x, int y);
118 void Sbar_DeathmatchOverlay (void);
119 void Sbar_IntermissionOverlay (void);
120 void Sbar_FinaleOverlay (void);
122 void CL_VM_UpdateShowingScoresState (int showingscores);
132 void Sbar_ShowScores (void)
136 sb_showscores = true;
137 CL_VM_UpdateShowingScoresState(sb_showscores);
147 void Sbar_DontShowScores (void)
149 sb_showscores = false;
150 CL_VM_UpdateShowingScoresState(sb_showscores);
153 void sbar_start(void)
157 if (gamemode == GAME_DELUXEQUAKE)
160 else if (gamemode == GAME_SOM)
162 sb_disc = Draw_CachePic ("gfx/disc");
164 for (i = 0;i < 10;i++)
165 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
167 somsb_health = Draw_CachePic ("gfx/hud_health");
168 somsb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
169 somsb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
170 somsb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
171 somsb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
172 somsb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
173 somsb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
174 somsb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
176 else if (gamemode == GAME_NEXUIZ)
178 for (i = 0;i < 10;i++)
179 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
180 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
181 sb_colon = Draw_CachePic ("gfx/num_colon");
183 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
184 sb_ammo[1] = Draw_CachePic ("gfx/sb_bullets");
185 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
186 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
188 sb_armor[0] = Draw_CachePic ("gfx/sb_armor");
192 sb_health = Draw_CachePic ("gfx/sb_health");
194 sb_items[2] = Draw_CachePic ("gfx/sb_slowmo");
195 sb_items[3] = Draw_CachePic ("gfx/sb_invinc");
196 sb_items[4] = Draw_CachePic ("gfx/sb_energy");
197 sb_items[5] = Draw_CachePic ("gfx/sb_str");
199 sb_items[11] = Draw_CachePic ("gfx/sb_flag_red_taken");
200 sb_items[12] = Draw_CachePic ("gfx/sb_flag_red_lost");
201 sb_items[13] = Draw_CachePic ("gfx/sb_flag_red_carrying");
202 sb_items[14] = Draw_CachePic ("gfx/sb_key_carrying");
203 sb_items[15] = Draw_CachePic ("gfx/sb_flag_blue_taken");
204 sb_items[16] = Draw_CachePic ("gfx/sb_flag_blue_lost");
205 sb_items[17] = Draw_CachePic ("gfx/sb_flag_blue_carrying");
207 sb_sbar = Draw_CachePic ("gfx/sbar");
208 sb_sbar_minimal = Draw_CachePic ("gfx/sbar_minimal");
209 sb_sbar_overlay = Draw_CachePic ("gfx/sbar_overlay");
211 for(i = 0; i < 9;i++)
212 sb_weapons[0][i] = Draw_CachePic (va("gfx/inv_weapon%i",i));
214 else if (gamemode == GAME_ZYMOTIC)
216 zymsb_crosshair_center = Draw_CachePic ("gfx/hud/crosshair_center");
217 zymsb_crosshair_line = Draw_CachePic ("gfx/hud/crosshair_line");
218 zymsb_crosshair_health = Draw_CachePic ("gfx/hud/crosshair_health");
219 zymsb_crosshair_clip = Draw_CachePic ("gfx/hud/crosshair_clip");
220 zymsb_crosshair_ammo = Draw_CachePic ("gfx/hud/crosshair_ammo");
221 zymsb_crosshair_background = Draw_CachePic ("gfx/hud/crosshair_background");
222 zymsb_crosshair_left1 = Draw_CachePic ("gfx/hud/crosshair_left1");
223 zymsb_crosshair_left2 = Draw_CachePic ("gfx/hud/crosshair_left2");
224 zymsb_crosshair_right = Draw_CachePic ("gfx/hud/crosshair_right");
228 sb_disc = Draw_CachePic ("gfx/disc");
230 for (i = 0;i < 10;i++)
232 sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
233 sb_nums[1][i] = Draw_CachePic (va("gfx/anum_%i",i));
236 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
237 sb_nums[1][10] = Draw_CachePic ("gfx/anum_minus");
239 sb_colon = Draw_CachePic ("gfx/num_colon");
240 sb_slash = Draw_CachePic ("gfx/num_slash");
242 sb_weapons[0][0] = Draw_CachePic ("gfx/inv_shotgun");
243 sb_weapons[0][1] = Draw_CachePic ("gfx/inv_sshotgun");
244 sb_weapons[0][2] = Draw_CachePic ("gfx/inv_nailgun");
245 sb_weapons[0][3] = Draw_CachePic ("gfx/inv_snailgun");
246 sb_weapons[0][4] = Draw_CachePic ("gfx/inv_rlaunch");
247 sb_weapons[0][5] = Draw_CachePic ("gfx/inv_srlaunch");
248 sb_weapons[0][6] = Draw_CachePic ("gfx/inv_lightng");
250 sb_weapons[1][0] = Draw_CachePic ("gfx/inv2_shotgun");
251 sb_weapons[1][1] = Draw_CachePic ("gfx/inv2_sshotgun");
252 sb_weapons[1][2] = Draw_CachePic ("gfx/inv2_nailgun");
253 sb_weapons[1][3] = Draw_CachePic ("gfx/inv2_snailgun");
254 sb_weapons[1][4] = Draw_CachePic ("gfx/inv2_rlaunch");
255 sb_weapons[1][5] = Draw_CachePic ("gfx/inv2_srlaunch");
256 sb_weapons[1][6] = Draw_CachePic ("gfx/inv2_lightng");
258 for (i = 0;i < 5;i++)
260 sb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_shotgun",i+1));
261 sb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_sshotgun",i+1));
262 sb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_nailgun",i+1));
263 sb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_snailgun",i+1));
264 sb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_rlaunch",i+1));
265 sb_weapons[2+i][5] = Draw_CachePic (va("gfx/inva%i_srlaunch",i+1));
266 sb_weapons[2+i][6] = Draw_CachePic (va("gfx/inva%i_lightng",i+1));
269 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
270 sb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
271 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
272 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
274 sb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
275 sb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
276 sb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
278 sb_items[0] = Draw_CachePic ("gfx/sb_key1");
279 sb_items[1] = Draw_CachePic ("gfx/sb_key2");
280 sb_items[2] = Draw_CachePic ("gfx/sb_invis");
281 sb_items[3] = Draw_CachePic ("gfx/sb_invuln");
282 sb_items[4] = Draw_CachePic ("gfx/sb_suit");
283 sb_items[5] = Draw_CachePic ("gfx/sb_quad");
285 sb_sigil[0] = Draw_CachePic ("gfx/sb_sigil1");
286 sb_sigil[1] = Draw_CachePic ("gfx/sb_sigil2");
287 sb_sigil[2] = Draw_CachePic ("gfx/sb_sigil3");
288 sb_sigil[3] = Draw_CachePic ("gfx/sb_sigil4");
290 sb_faces[4][0] = Draw_CachePic ("gfx/face1");
291 sb_faces[4][1] = Draw_CachePic ("gfx/face_p1");
292 sb_faces[3][0] = Draw_CachePic ("gfx/face2");
293 sb_faces[3][1] = Draw_CachePic ("gfx/face_p2");
294 sb_faces[2][0] = Draw_CachePic ("gfx/face3");
295 sb_faces[2][1] = Draw_CachePic ("gfx/face_p3");
296 sb_faces[1][0] = Draw_CachePic ("gfx/face4");
297 sb_faces[1][1] = Draw_CachePic ("gfx/face_p4");
298 sb_faces[0][0] = Draw_CachePic ("gfx/face5");
299 sb_faces[0][1] = Draw_CachePic ("gfx/face_p5");
301 sb_face_invis = Draw_CachePic ("gfx/face_invis");
302 sb_face_invuln = Draw_CachePic ("gfx/face_invul2");
303 sb_face_invis_invuln = Draw_CachePic ("gfx/face_inv2");
304 sb_face_quad = Draw_CachePic ("gfx/face_quad");
306 sb_sbar = Draw_CachePic ("gfx/sbar");
307 sb_ibar = Draw_CachePic ("gfx/ibar");
308 sb_scorebar = Draw_CachePic ("gfx/scorebar");
310 //MED 01/04/97 added new hipnotic weapons
311 if (gamemode == GAME_HIPNOTIC)
313 hsb_weapons[0][0] = Draw_CachePic ("gfx/inv_laser");
314 hsb_weapons[0][1] = Draw_CachePic ("gfx/inv_mjolnir");
315 hsb_weapons[0][2] = Draw_CachePic ("gfx/inv_gren_prox");
316 hsb_weapons[0][3] = Draw_CachePic ("gfx/inv_prox_gren");
317 hsb_weapons[0][4] = Draw_CachePic ("gfx/inv_prox");
319 hsb_weapons[1][0] = Draw_CachePic ("gfx/inv2_laser");
320 hsb_weapons[1][1] = Draw_CachePic ("gfx/inv2_mjolnir");
321 hsb_weapons[1][2] = Draw_CachePic ("gfx/inv2_gren_prox");
322 hsb_weapons[1][3] = Draw_CachePic ("gfx/inv2_prox_gren");
323 hsb_weapons[1][4] = Draw_CachePic ("gfx/inv2_prox");
325 for (i = 0;i < 5;i++)
327 hsb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_laser",i+1));
328 hsb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_mjolnir",i+1));
329 hsb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_gren_prox",i+1));
330 hsb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_prox_gren",i+1));
331 hsb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_prox",i+1));
334 hsb_items[0] = Draw_CachePic ("gfx/sb_wsuit");
335 hsb_items[1] = Draw_CachePic ("gfx/sb_eshld");
337 else if (gamemode == GAME_ROGUE)
339 rsb_invbar[0] = Draw_CachePic ("gfx/r_invbar1");
340 rsb_invbar[1] = Draw_CachePic ("gfx/r_invbar2");
342 rsb_weapons[0] = Draw_CachePic ("gfx/r_lava");
343 rsb_weapons[1] = Draw_CachePic ("gfx/r_superlava");
344 rsb_weapons[2] = Draw_CachePic ("gfx/r_gren");
345 rsb_weapons[3] = Draw_CachePic ("gfx/r_multirock");
346 rsb_weapons[4] = Draw_CachePic ("gfx/r_plasma");
348 rsb_items[0] = Draw_CachePic ("gfx/r_shield1");
349 rsb_items[1] = Draw_CachePic ("gfx/r_agrav1");
351 // PGM 01/19/97 - team color border
352 rsb_teambord = Draw_CachePic ("gfx/r_teambord");
353 // PGM 01/19/97 - team color border
355 rsb_ammo[0] = Draw_CachePic ("gfx/r_ammolava");
356 rsb_ammo[1] = Draw_CachePic ("gfx/r_ammomulti");
357 rsb_ammo[2] = Draw_CachePic ("gfx/r_ammoplasma");
361 sb_ranking = Draw_CachePic ("gfx/ranking");
362 sb_complete = Draw_CachePic ("gfx/complete");
363 sb_inter = Draw_CachePic ("gfx/inter");
364 sb_finale = Draw_CachePic ("gfx/finale");
367 void sbar_shutdown(void)
371 void sbar_newmap(void)
375 void Sbar_Init (void)
377 Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
378 Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
379 Cvar_RegisterVariable(&showfps);
380 Cvar_RegisterVariable(&showsound);
381 Cvar_RegisterVariable(&showspeed);
382 Cvar_RegisterVariable(&showtopspeed);
383 Cvar_RegisterVariable(&showtime);
384 Cvar_RegisterVariable(&showtime_format);
385 Cvar_RegisterVariable(&showdate);
386 Cvar_RegisterVariable(&showdate_format);
387 Cvar_RegisterVariable(&sbar_alpha_bg);
388 Cvar_RegisterVariable(&sbar_alpha_fg);
389 Cvar_RegisterVariable(&sbar_hudselector);
390 Cvar_RegisterVariable(&sbar_scorerank);
391 Cvar_RegisterVariable(&sbar_gametime);
392 Cvar_RegisterVariable(&sbar_miniscoreboard_size);
393 Cvar_RegisterVariable(&sbar_info_pos);
394 Cvar_RegisterVariable(&cl_deathscoreboard);
396 Cvar_RegisterVariable(&crosshair_color_red);
397 Cvar_RegisterVariable(&crosshair_color_green);
398 Cvar_RegisterVariable(&crosshair_color_blue);
399 Cvar_RegisterVariable(&crosshair_color_alpha);
400 Cvar_RegisterVariable(&crosshair_size);
402 if(gamemode == GAME_NEXUIZ)
404 Cvar_RegisterVariable(&sbar_flagstatus_right); // this cvar makes no sense in other games
405 Cvar_RegisterVariable(&sbar_flagstatus_pos); // this cvar makes no sense in other games
408 R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap);
412 //=============================================================================
414 // drawing routines are relative to the status bar location
423 void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
425 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
428 void Sbar_DrawPic (int x, int y, cachepic_t *pic)
430 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
433 void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
435 DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
442 Draws one solid graphics character
445 void Sbar_DrawCharacter (int x, int y, int num)
447 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);
455 void Sbar_DrawString (int x, int y, char *str)
457 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);
465 void Sbar_DrawNum (int x, int y, int num, int digits, int color)
470 l = dpsnprintf(str, sizeof(str), "%i", num);
484 Sbar_DrawPic (x, y, sb_nums[color][frame]);
497 void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
505 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
508 l = dpsnprintf(str, sizeof(str), "%i", num);
513 x += (digits-l) * lettersize;
522 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
529 //=============================================================================
532 int Sbar_IsTeammatch()
534 // currently only nexuiz uses the team score board
535 return ((gamemode == GAME_NEXUIZ)
536 && (teamplay.integer > 0));
544 static int fragsort[MAX_SCOREBOARD];
545 static int scoreboardlines;
547 int Sbar_GetSortedPlayerIndex (int index)
549 return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
552 static scoreboard_t teams[MAX_SCOREBOARD];
553 static int teamsort[MAX_SCOREBOARD];
554 static int teamlines;
555 void Sbar_SortFrags (void)
561 for (i=0 ; i<cl.maxclients ; i++)
563 if (cl.scores[i].name[0])
565 fragsort[scoreboardlines] = i;
570 for (i=0 ; i<scoreboardlines ; i++)
571 for (j=0 ; j<scoreboardlines-1-i ; j++)
572 if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
575 fragsort[j] = fragsort[j+1];
580 if (Sbar_IsTeammatch ())
582 // now sort players by teams.
583 for (i=0 ; i<scoreboardlines ; i++)
585 for (j=0 ; j<scoreboardlines-1-i ; j++)
587 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
590 fragsort[j] = fragsort[j+1];
596 // calculate team scores
598 for (i=0 ; i<scoreboardlines ; i++)
600 if (color != (cl.scores[fragsort[i]].colors & 15))
602 const char* teamname;
604 color = cl.scores[fragsort[i]].colors & 15;
610 teamname = "^1Red Team";
613 teamname = "^4Blue Team";
616 teamname = "^6Pink Team";
619 teamname = "^3Yellow Team";
622 teamname = "Total Team Score";
625 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
627 teams[teamlines-1].frags = 0;
628 teams[teamlines-1].colors = color + 16 * color;
631 if (cl.scores[fragsort[i]].frags != -666)
633 // do not add spedcators
634 // (ugly hack for nexuiz)
635 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
639 // now sort teams by scores.
640 for (i=0 ; i<teamlines ; i++)
642 for (i=0 ; i<teamlines ; i++)
644 for (j=0 ; j<teamlines-1-i ; j++)
646 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
649 teamsort[j] = teamsort[j+1];
662 void Sbar_SoloScoreboard (void)
665 char str[80], timestr[40];
667 int minutes, seconds;
670 t = (cl.intermission ? cl.completed_time : cl.time);
671 minutes = (int)(t / 60);
672 seconds = (int)(t - 60*floor(t/60));
674 // monsters and secrets are now both on the top row
675 if (cl.stats[STAT_TOTALMONSTERS])
676 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
677 if (cl.stats[STAT_TOTALSECRETS])
678 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
680 // figure out the map's filename without path or extension
681 strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
682 if (strrchr(str, '.'))
683 *(strrchr(str, '.')) = 0;
685 // append a : separator and then the full title
686 strlcat(str, ":", sizeof(str));
687 strlcat(str, cl.levelname, sizeof(str));
689 // if there's a newline character, terminate the string there
690 if (strchr(str, '\n'))
691 *(strchr(str, '\n')) = 0;
693 // make the time string
694 timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
696 // truncate the level name if necessary to make room for time
698 if ((int)strlen(str) > max)
701 // print the filename and message
702 Sbar_DrawString(8, 12, str);
705 Sbar_DrawString(8 + max*8, 12, timestr);
709 int minutes, seconds, tens, units;
712 if (gamemode != GAME_NEXUIZ) {
713 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
714 Sbar_DrawString (8, 4, str);
716 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
717 Sbar_DrawString (8, 12, str);
721 minutes = (int)(cl.time / 60);
722 seconds = (int)(cl.time - 60*minutes);
724 units = seconds - 10*tens;
725 dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
726 Sbar_DrawString (184, 4, str);
729 if (gamemode == GAME_NEXUIZ) {
730 l = (int) strlen (cl.worldmodel->name);
731 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
733 l = (int) strlen (cl.levelname);
734 Sbar_DrawString (232 - l*4, 12, cl.levelname);
744 void Sbar_DrawScoreboard (void)
746 Sbar_SoloScoreboard ();
747 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
748 //if (cl.gametype == GAME_DEATHMATCH)
750 Sbar_DeathmatchOverlay ();
753 //=============================================================================
755 // AK to make DrawInventory smaller
756 static void Sbar_DrawWeapon(int nr, float fade, int active)
758 if (sbar_hudselector.integer == 1)
760 // width = 300, height = 100
761 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
763 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);
765 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);
769 // width = 300, height = 100
770 const int w_width = 300, w_height = 100, w_space = 10;
771 const float w_scale = 0.4;
773 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);
774 //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);
783 void Sbar_DrawInventory (void)
790 if (gamemode == GAME_ROGUE)
792 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
793 Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
795 Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
798 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
801 for (i=0 ; i<7 ; i++)
803 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
805 time = cl.item_gettime[i];
806 flashon = (int)(max(0, cl.time - time)*10);
809 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i) )
815 flashon = (flashon%5) + 2;
817 Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
823 if (gamemode == GAME_HIPNOTIC)
825 int grenadeflashing=0;
826 for (i=0 ; i<4 ; i++)
828 if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
830 time = max(0, cl.item_gettime[hipweapons[i]]);
831 flashon = (int)((cl.time - time)*10);
834 if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]) )
840 flashon = (flashon%5) + 2;
842 // check grenade launcher
845 if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
850 Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
856 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
858 if (!grenadeflashing)
859 Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
862 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
865 Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
870 if (gamemode == GAME_ROGUE)
872 // check for powered up weapon.
873 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
875 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
876 Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
880 for (i=0 ; i<4 ; i++)
882 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
884 Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
886 Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
888 Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
890 Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
894 for (i=0 ; i<6 ; i++)
895 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
897 //MED 01/04/97 changed keys
898 if (gamemode != GAME_HIPNOTIC || (i>1))
899 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
902 //MED 01/04/97 added hipnotic items
904 if (gamemode == GAME_HIPNOTIC)
906 for (i=0 ; i<2 ; i++)
907 if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
908 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
911 if (gamemode == GAME_ROGUE)
914 for (i=0 ; i<2 ; i++)
915 if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
916 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
921 for (i=0 ; i<4 ; i++)
922 if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
923 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
927 //=============================================================================
934 void Sbar_DrawFrags (void)
944 l = min(scoreboardlines, 4);
948 for (i = 0;i < l;i++)
954 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
955 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);
956 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
957 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);
961 dpsnprintf (num, sizeof(num), "%3i",f);
963 if (k == cl.viewentity - 1)
965 Sbar_DrawCharacter ( x + 2, -24, 16);
966 Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
968 Sbar_DrawCharacter (x + 8, -24, num[0]);
969 Sbar_DrawCharacter (x + 16, -24, num[1]);
970 Sbar_DrawCharacter (x + 24, -24, num[2]);
975 //=============================================================================
983 void Sbar_DrawFace (void)
987 // PGM 01/19/97 - team color drawing
988 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
989 if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
995 s = &cl.scores[cl.viewentity - 1];
997 Sbar_DrawPic (112, 0, rsb_teambord);
998 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
999 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);
1000 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1001 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);
1005 dpsnprintf (num, sizeof(num), "%3i",f);
1007 if ((s->colors & 0xf0)==0)
1010 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1012 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1014 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1018 Sbar_DrawCharacter ( 109, 3, num[0]);
1019 Sbar_DrawCharacter ( 116, 3, num[1]);
1020 Sbar_DrawCharacter ( 123, 3, num[2]);
1025 // PGM 01/19/97 - team color drawing
1027 if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1028 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1029 else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1030 Sbar_DrawPic (112, 0, sb_face_quad );
1031 else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1032 Sbar_DrawPic (112, 0, sb_face_invis );
1033 else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1034 Sbar_DrawPic (112, 0, sb_face_invuln);
1037 f = cl.stats[STAT_HEALTH] / 20;
1039 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1042 double topspeed = 0;
1043 double topspeedxy = 0;
1044 time_t current_time = 3;
1045 time_t top_time = 0;
1046 time_t topxy_time = 0;
1048 static void get_showspeed_unit(int unitnumber, double *conversion_factor, const char **unit)
1051 unitnumber = showspeed.integer;
1056 if(gamemode == GAME_NEXUIZ)
1060 *conversion_factor = 1.0;
1064 *conversion_factor = 0.0254;
1065 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1066 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1070 *conversion_factor = 0.0254 * 3.6;
1071 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1075 *conversion_factor = 0.0254 * 3.6 * 0.6213711922;
1076 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1080 *conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1081 if(gamemode != GAME_NEXUIZ) *conversion_factor *= 1.5;
1086 void Sbar_ShowFPS(void)
1088 float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1089 char soundstring[32];
1091 char timestring[32];
1092 char datestring[32];
1093 char speedstring[32];
1094 char topspeedstring[48];
1095 qboolean red = false;
1101 topspeedstring[0] = 0;
1102 if (showfps.integer)
1105 static double nexttime = 0, lasttime = 0;
1106 static double framerate = 0;
1107 static int framecount = 0;
1108 double interval = 0.25;
1111 if (newtime >= nexttime)
1113 framerate = framecount / (newtime - lasttime);
1114 if (nexttime < newtime - interval * 1.5)
1117 nexttime += interval;
1123 if ((red = (calc < 1.0f)))
1124 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1126 dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1128 if (showtime.integer)
1129 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1130 if (showdate.integer)
1131 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1132 if (showsound.integer)
1133 dpsnprintf(soundstring, sizeof(soundstring), "%4i/%4i at %3ims\n", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1134 if (showspeed.integer || showtopspeed.integer)
1136 double speed, speedxy, f;
1138 speed = VectorLength(cl.movement_velocity);
1139 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1140 if (showspeed.integer)
1142 get_showspeed_unit(showspeed.integer, &f, &unit);
1143 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1145 if (showtopspeed.integer)
1147 qboolean topspeed_latched = false, topspeedxy_latched = false;
1148 get_showspeed_unit(showtopspeed.integer, &f, &unit);
1149 if (speed >= topspeed || current_time - top_time > 3)
1155 topspeed_latched = true;
1156 if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1158 topspeedxy = speedxy;
1162 topspeedxy_latched = true;
1163 dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1164 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1165 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1167 time(¤t_time);
1170 if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0] || topspeedstring[0])
1174 fps_height = fps_scaley * ((soundstring[0] != 0) + (fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0) + (topspeedstring[0] != 0));
1175 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1176 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1177 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_height;
1180 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(soundstring, 0, true, FONT_INFOBAR) * fps_scalex;
1181 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1182 DrawQ_String_Font(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1183 fps_y += fps_scaley;
1187 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1188 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1190 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1192 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1193 fps_y += fps_scaley;
1197 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1198 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1199 DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1200 fps_y += fps_scaley;
1204 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1205 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1206 DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1207 fps_y += fps_scaley;
1211 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1212 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1213 DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1214 fps_y += fps_scaley;
1216 if (topspeedstring[0])
1218 fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(topspeedstring, 0, false, FONT_INFOBAR) * fps_scalex;
1219 DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1220 DrawQ_String_Font(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1221 fps_y += fps_scaley;
1226 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)
1229 c2 = bound(0, c2, 1);
1230 c1 = bound(0, c1, 1 - c2);
1232 r[1] = rangey + rangeheight * (c2 + c1);
1233 r[2] = rangey + rangeheight * (c2);
1237 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);
1239 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);
1241 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);
1243 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);
1251 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1252 extern cvar_t v_kicktime;
1253 void Sbar_Score (int margin);
1254 void Sbar_Draw (void)
1258 if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar
1261 Sbar_DrawScoreboard ();
1262 else if (cl.intermission == 1)
1264 if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1266 Sbar_DrawScoreboard();
1269 Sbar_IntermissionOverlay();
1271 else if (cl.intermission == 2)
1272 Sbar_FinaleOverlay();
1273 else if (gamemode == GAME_DELUXEQUAKE)
1276 else if (gamemode == GAME_SOM)
1278 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1279 Sbar_DrawScoreboard ();
1282 // this is the top left of the sbar area
1284 sbar_y = vid_conheight.integer - 24*3;
1287 if (cl.stats[STAT_ARMOR])
1289 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1290 Sbar_DrawPic(0, 0, somsb_armor[2]);
1291 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1292 Sbar_DrawPic(0, 0, somsb_armor[1]);
1293 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1294 Sbar_DrawPic(0, 0, somsb_armor[0]);
1295 Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1299 Sbar_DrawPic(0, 24, somsb_health);
1300 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1303 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1304 Sbar_DrawPic(0, 48, somsb_ammo[0]);
1305 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1306 Sbar_DrawPic(0, 48, somsb_ammo[1]);
1307 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1308 Sbar_DrawPic(0, 48, somsb_ammo[2]);
1309 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1310 Sbar_DrawPic(0, 48, somsb_ammo[3]);
1311 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1312 if (cl.stats[STAT_SHELLS])
1313 Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1316 else if (gamemode == GAME_NEXUIZ)
1318 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1320 sbar_x = (vid_conwidth.integer - 640)/2;
1321 sbar_y = vid_conheight.integer - 47;
1322 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1323 Sbar_DrawScoreboard ();
1325 else if (sb_lines && sbar_hudselector.integer == 1)
1329 int redflag, blueflag;
1332 sbar_x = (vid_conwidth.integer - 320)/2;
1333 sbar_y = vid_conheight.integer - 24 - 16;
1335 // calculate intensity to draw weapons bar at
1336 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1337 fade = bound(0.7, fade, 1);
1338 for (i = 0; i < 8;i++)
1339 if (cl.stats[STAT_ITEMS] & (1 << i))
1340 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1341 if((cl.stats[STAT_ITEMS] & (1<<12)))
1342 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1345 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1346 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1347 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1348 if (redflag == 3 && blueflag == 3)
1350 // The Impossible Combination[tm]
1351 // Can only happen in Key Hunt mode...
1352 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1357 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1359 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1363 if (cl.stats[STAT_ARMOR] > 0)
1365 Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1366 if(cl.stats[STAT_ARMOR] > 200)
1367 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1368 else if(cl.stats[STAT_ARMOR] > 100)
1369 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1370 else if(cl.stats[STAT_ARMOR] > 50)
1371 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1372 else if(cl.stats[STAT_ARMOR] > 25)
1373 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1375 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1379 if (cl.stats[STAT_HEALTH] != 0)
1381 Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1382 if(cl.stats[STAT_HEALTH] > 200)
1383 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1384 else if(cl.stats[STAT_HEALTH] > 100)
1385 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1386 else if(cl.stats[STAT_HEALTH] > 50)
1387 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1388 else if(cl.stats[STAT_HEALTH] > 25)
1389 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1391 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1395 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1397 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1398 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1399 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1400 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1401 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1402 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1403 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1404 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1405 if(cl.stats[STAT_AMMO] > 10)
1406 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1408 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1411 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1412 Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1415 // The margin can be at most 8 to support 640x480 console size:
1416 // 320 + 2 * (144 + 16) = 640
1422 int redflag, blueflag;
1425 sbar_x = (vid_conwidth.integer - 640)/2;
1426 sbar_y = vid_conheight.integer - 47;
1428 // calculate intensity to draw weapons bar at
1429 fade = 3 - 2 * (cl.time - cl.weapontime);
1432 fade = min(fade, 1);
1433 for (i = 0; i < 8;i++)
1434 if (cl.stats[STAT_ITEMS] & (1 << i))
1435 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1437 if((cl.stats[STAT_ITEMS] & (1<<12)))
1438 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1441 //if (!cl.islocalgame)
1442 // Sbar_DrawFrags ();
1445 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1447 Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1450 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1451 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1452 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1453 if (redflag == 3 && blueflag == 3)
1455 // The Impossible Combination[tm]
1456 // Can only happen in Key Hunt mode...
1457 Sbar_DrawPic ((int) x, -179, sb_items[14]);
1462 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1464 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1468 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1471 if(cl.stats[STAT_HEALTH] > 100)
1472 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1473 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1474 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1476 Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1478 // AK dont draw ammo for the laser
1479 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1481 if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1482 Sbar_DrawPic (519, 0, sb_ammo[0]);
1483 else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1484 Sbar_DrawPic (519, 0, sb_ammo[1]);
1485 else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1486 Sbar_DrawPic (519, 0, sb_ammo[2]);
1487 else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1488 Sbar_DrawPic (519, 0, sb_ammo[3]);
1490 if(cl.stats[STAT_AMMO] <= 10)
1491 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1493 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1498 DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1500 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1501 Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1506 // Mini scoreboard uses 12*4 per other team, that is, 144
1507 // pixels when there are four teams...
1508 // Nexuiz by default sets vid_conwidth to 800... makes
1510 // so we need to shift it by 64 pixels to the right to fit
1511 // BUT: then it overlaps with the image that gets drawn
1512 // for viewsize 100! Therefore, just account for 3 teams,
1513 // that is, 96 pixels mini scoreboard size, needing 16 pixels
1517 else if (gamemode == GAME_ZYMOTIC)
1520 float scale = 64.0f / 256.0f;
1521 float kickoffset[3];
1522 VectorClear(kickoffset);
1525 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1526 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1528 sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1529 sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1530 // left1 16, 48 : 126 -66
1531 // left2 16, 128 : 196 -66
1532 // right 176, 48 : 196 -136
1533 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);
1534 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);
1535 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);
1536 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1538 float scale = 128.0f / 256.0f;
1539 float healthstart, healthheight, healthstarttc, healthendtc;
1540 float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1541 float ammostart, ammoheight, ammostarttc, ammoendtc;
1542 float clipstart, clipheight, clipstarttc, clipendtc;
1543 float kickoffset[3], offset;
1544 VectorClear(kickoffset);
1547 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1548 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1550 sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1551 sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1552 offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1553 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);
1554 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);
1555 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);
1556 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);
1557 healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1558 shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1559 healthstart = 204 - healthheight;
1560 shieldstart = healthstart - shieldheight;
1561 healthstarttc = healthstart * (1.0f / 256.0f);
1562 healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1563 shieldstarttc = shieldstart * (1.0f / 256.0f);
1564 shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1565 ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1566 ammostart = 114 - ammoheight;
1567 ammostarttc = ammostart * (1.0f / 256.0f);
1568 ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1569 clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1570 clipstart = 190 - clipheight;
1571 clipstarttc = clipstart * (1.0f / 256.0f);
1572 clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1573 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);
1574 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);
1575 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);
1576 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);
1577 DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1578 DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1581 else // Quake and others
1583 sbar_x = (vid_conwidth.integer - 320)/2;
1584 sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1585 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1586 //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1590 if (gamemode != GAME_GOODVSBAD2)
1591 Sbar_DrawInventory ();
1592 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1596 if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1598 if (gamemode != GAME_GOODVSBAD2)
1599 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1600 Sbar_DrawScoreboard ();
1604 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1606 // keys (hipnotic only)
1607 //MED 01/04/97 moved keys here so they would not be overwritten
1608 if (gamemode == GAME_HIPNOTIC)
1610 if (cl.stats[STAT_ITEMS] & IT_KEY1)
1611 Sbar_DrawPic (209, 3, sb_items[0]);
1612 if (cl.stats[STAT_ITEMS] & IT_KEY2)
1613 Sbar_DrawPic (209, 12, sb_items[1]);
1616 if (gamemode != GAME_GOODVSBAD2)
1618 if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1620 Sbar_DrawNum (24, 0, 666, 3, 1);
1621 Sbar_DrawPic (0, 0, sb_disc);
1625 if (gamemode == GAME_ROGUE)
1627 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1628 if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1629 Sbar_DrawPic (0, 0, sb_armor[2]);
1630 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1631 Sbar_DrawPic (0, 0, sb_armor[1]);
1632 else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1633 Sbar_DrawPic (0, 0, sb_armor[0]);
1637 Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1638 if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1639 Sbar_DrawPic (0, 0, sb_armor[2]);
1640 else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1641 Sbar_DrawPic (0, 0, sb_armor[1]);
1642 else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1643 Sbar_DrawPic (0, 0, sb_armor[0]);
1652 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1655 if (gamemode == GAME_ROGUE)
1657 if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1658 Sbar_DrawPic (224, 0, sb_ammo[0]);
1659 else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1660 Sbar_DrawPic (224, 0, sb_ammo[1]);
1661 else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1662 Sbar_DrawPic (224, 0, sb_ammo[2]);
1663 else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1664 Sbar_DrawPic (224, 0, sb_ammo[3]);
1665 else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1666 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1667 else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1668 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1669 else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1670 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1674 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1675 Sbar_DrawPic (224, 0, sb_ammo[0]);
1676 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1677 Sbar_DrawPic (224, 0, sb_ammo[1]);
1678 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1679 Sbar_DrawPic (224, 0, sb_ammo[2]);
1680 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1681 Sbar_DrawPic (224, 0, sb_ammo[3]);
1684 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1686 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1687 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1689 if (gamemode == GAME_TRANSFUSION)
1690 Sbar_MiniDeathmatchOverlay (0, 0);
1692 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1701 if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1703 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1704 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);
1707 if (cl_prydoncursor.integer)
1708 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);
1711 //=============================================================================
1715 Sbar_DeathmatchOverlay
1719 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1722 qboolean myself = false;
1724 minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1726 if((s - cl.scores) == cl.playerentity - 1)
1728 if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1729 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1732 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1734 if (s->qw_spectator)
1736 if (s->qw_ping || s->qw_packetloss)
1737 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 );
1739 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 );
1743 // draw colors behind score
1749 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1750 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);
1751 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1752 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);
1754 //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);
1755 if (s->qw_ping || s->qw_packetloss)
1756 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 );
1758 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 );
1763 if (s->qw_spectator)
1765 if (s->qw_ping || s->qw_packetloss)
1766 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 );
1768 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 );
1772 // draw colors behind score
1773 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1774 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);
1775 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1776 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);
1778 //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);
1779 if (s->qw_ping || s->qw_packetloss)
1780 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 );
1782 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 );
1788 void Sbar_DeathmatchOverlay (void)
1790 int i, y, xmin, xmax, ymin, ymax;
1792 // request new ping times every two second
1793 if (cl.last_ping_request < realtime - 2 && cls.netcon)
1795 cl.last_ping_request = realtime;
1796 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1798 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1799 MSG_WriteString(&cls.netcon->message, "pings");
1801 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*/)
1803 // 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
1804 static int ping_anyway_counter = 0;
1805 if(cl.parsingtextexpectingpingforscores == 1)
1807 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1808 if(++ping_anyway_counter >= 5)
1809 cl.parsingtextexpectingpingforscores = 0;
1811 if(cl.parsingtextexpectingpingforscores != 1)
1813 ping_anyway_counter = 0;
1814 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1815 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1816 MSG_WriteString(&cls.netcon->message, "ping");
1821 // 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
1822 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1823 MSG_WriteString(&cls.netcon->message, "pings");
1831 ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1833 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1834 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)
1836 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)
1837 xmax = vid_conwidth.integer - xmin;
1839 if(gamemode == GAME_NEXUIZ)
1840 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);
1842 DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1846 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1848 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 );
1852 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 );
1856 if (Sbar_IsTeammatch ())
1858 // show team scores first
1859 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1860 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1864 for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1865 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1870 Sbar_MiniDeathmatchOverlay
1874 void Sbar_MiniDeathmatchOverlay (int x, int y)
1876 int i, j, numlines, range_begin, range_end, myteam, teamsep;
1878 // do not draw this if sbar_miniscoreboard_size is zero
1879 if(sbar_miniscoreboard_size.value == 0)
1881 // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1882 if(sbar_miniscoreboard_size.value > 0)
1883 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1888 // decide where to print
1889 if (gamemode == GAME_TRANSFUSION)
1890 numlines = (vid_conwidth.integer - x + 127) / 128;
1892 numlines = (vid_conheight.integer - y + 7) / 8;
1894 // give up if there isn't room
1895 if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1899 for (i = 0; i < scoreboardlines; i++)
1900 if (fragsort[i] == cl.playerentity - 1)
1904 range_end = scoreboardlines;
1907 if (gamemode != GAME_TRANSFUSION)
1908 if (Sbar_IsTeammatch ())
1910 // reserve space for the team scores
1911 numlines -= teamlines;
1913 // find first and last player of my team (only draw the team totals and my own team)
1914 range_begin = range_end = i;
1915 myteam = cl.scores[fragsort[i]].colors & 15;
1916 while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1918 while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1921 // looks better than two players
1931 i = min(i, range_end - numlines);
1932 i = max(i, range_begin);
1934 if (gamemode == GAME_TRANSFUSION)
1936 for (;i < range_end && x < vid_conwidth.integer;i++)
1937 x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1941 if(range_end - i < numlines) // won't draw to bottom?
1942 y += 8 * (numlines - (range_end - i)); // bottom align
1943 // show team scores first
1944 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1945 y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1947 for (;i < range_end && y < vid_conheight.integer;i++)
1948 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1952 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1954 static int const sortorder[16] =
1973 const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1974 const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1975 int tc1 = sortorder[t1->colors & 15];
1976 int tc2 = sortorder[t2->colors & 15];
1980 void Sbar_Score (int margin)
1982 int i, me, score, otherleader, place, distribution, minutes, seconds;
1984 int sbar_x_save = sbar_x;
1985 int sbar_y_save = sbar_y;
1988 sbar_y = (int) (vid_conheight.value - (32+12));
1991 me = cl.playerentity - 1;
1992 if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
1994 if(Sbar_IsTeammatch())
1998 // team1 team3 team4
2002 scoreboard_t *teamcolorsort[16];
2005 for(i = 0; i < teamlines; ++i)
2006 teamcolorsort[i] = &(teams[i]);
2008 // Now sort them by color
2009 qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2012 // -12*4: four digits space
2013 place = (teamlines - 1) * (-12 * 4);
2015 for(i = 0; i < teamlines; ++i)
2017 int cindex = teamcolorsort[i]->colors & 15;
2018 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2019 float cm = max(max(c[0], c[1]), c[2]);
2020 float cr = c[0] / cm;
2021 float cg = c[1] / cm;
2022 float cb = c[2] / cm;
2023 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2025 Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2029 Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2042 // find leading score other than ourselves, to calculate distribution
2043 // find our place in the scoreboard
2044 score = cl.scores[me].frags;
2045 for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2047 if (cl.scores[i].name[0] && i != me)
2049 if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2051 if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2055 distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2057 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2058 else if (place == 2)
2059 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2061 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2062 if (otherleader < 0)
2063 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2064 if (distribution >= 0)
2066 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2067 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 1, 1, 0);
2069 else if (distribution >= -5)
2071 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2072 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 1, 0, 1, 0);
2076 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2077 Sbar_DrawXNum(-32*4, 0, score, 4, 32, 1, 0, 0, 1, 0);
2082 if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2084 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2085 minutes = (int)floor(timeleft / 60);
2086 seconds = (int)(floor(timeleft) - minutes * 60);
2089 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2090 if(sb_colon && sb_colon->tex != r_texture_notexture)
2091 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2092 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2094 else if (minutes >= 1)
2096 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 0, 1, 0);
2097 if(sb_colon && sb_colon->tex != r_texture_notexture)
2098 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2099 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2101 else if ((int)(timeleft * 4) & 1)
2102 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2104 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2106 else if (sbar_gametime.integer)
2108 minutes = (int)floor(cl.time / 60);
2109 seconds = (int)(floor(cl.time) - minutes * 60);
2110 Sbar_DrawXNum(-12*6, 32, minutes, 3, 12, 1, 1, 1, 1, 0);
2111 if(sb_colon && sb_colon->tex != r_texture_notexture)
2112 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2113 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2116 sbar_x = sbar_x_save;
2117 sbar_y = sbar_y_save;
2122 Sbar_IntermissionOverlay
2126 void Sbar_IntermissionOverlay (void)
2131 if (cl.gametype == GAME_DEATHMATCH)
2133 Sbar_DeathmatchOverlay ();
2137 sbar_x = (vid_conwidth.integer - 320) >> 1;
2138 sbar_y = (vid_conheight.integer - 200) >> 1;
2140 DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2141 DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2144 dig = (int)cl.completed_time / 60;
2145 Sbar_DrawNum (160, 64, dig, 3, 0);
2146 num = (int)cl.completed_time - dig*60;
2147 Sbar_DrawPic (234,64,sb_colon);
2148 Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2149 Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2151 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2152 if (gamemode != GAME_NEXUIZ)
2153 Sbar_DrawPic (232, 104, sb_slash);
2154 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2156 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2157 if (gamemode != GAME_NEXUIZ)
2158 Sbar_DrawPic (232, 144, sb_slash);
2159 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2170 void Sbar_FinaleOverlay (void)
2172 DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);