20218ea838650004935766cbfd6ad7496f88f30c
[divverent/darkplaces.git] / sbar.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // sbar.c -- status bar code
21
22 #include "quakedef.h"
23
24 cachepic_t *sb_disc;
25
26 #define STAT_MINUS 10 // num frame for '-' stats digit
27 cachepic_t *sb_nums[2][11];
28 cachepic_t *sb_colon, *sb_slash;
29 cachepic_t *sb_ibar;
30 cachepic_t *sb_sbar;
31 cachepic_t *sb_scorebar;
32 // AK only used by NEX
33 cachepic_t *sb_sbar_minimal;
34 cachepic_t *sb_sbar_overlay;
35
36 // AK changed the bound to 9
37 cachepic_t *sb_weapons[7][9]; // 0 is active, 1 is owned, 2-5 are flashes
38 cachepic_t *sb_ammo[4];
39 cachepic_t *sb_sigil[4];
40 cachepic_t *sb_armor[3];
41 cachepic_t *sb_items[32];
42
43 // 0-4 are based on health (in 20 increments)
44 // 0 is static, 1 is temporary animation
45 cachepic_t *sb_faces[5][2];
46 cachepic_t *sb_health; // GAME_NEXUIZ
47
48 cachepic_t *sb_face_invis;
49 cachepic_t *sb_face_quad;
50 cachepic_t *sb_face_invuln;
51 cachepic_t *sb_face_invis_invuln;
52
53 qboolean sb_showscores;
54
55 int sb_lines;                   // scan lines to draw
56
57 cachepic_t *rsb_invbar[2];
58 cachepic_t *rsb_weapons[5];
59 cachepic_t *rsb_items[2];
60 cachepic_t *rsb_ammo[3];
61 cachepic_t *rsb_teambord;               // PGM 01/19/97 - team color border
62
63 //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher
64 cachepic_t *hsb_weapons[7][5];   // 0 is active, 1 is owned, 2-5 are flashes
65 //MED 01/04/97 added array to simplify weapon parsing
66 int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};
67 //MED 01/04/97 added hipnotic items array
68 cachepic_t *hsb_items[2];
69
70 //GAME_SOM stuff:
71 cachepic_t *somsb_health;
72 cachepic_t *somsb_ammo[4];
73 cachepic_t *somsb_armor[3];
74
75 cachepic_t *zymsb_crosshair_center;
76 cachepic_t *zymsb_crosshair_line;
77 cachepic_t *zymsb_crosshair_health;
78 cachepic_t *zymsb_crosshair_ammo;
79 cachepic_t *zymsb_crosshair_clip;
80 cachepic_t *zymsb_crosshair_background;
81 cachepic_t *zymsb_crosshair_left1;
82 cachepic_t *zymsb_crosshair_left2;
83 cachepic_t *zymsb_crosshair_right;
84
85 cachepic_t *sb_ranking;
86 cachepic_t *sb_complete;
87 cachepic_t *sb_inter;
88 cachepic_t *sb_finale;
89
90 cvar_t showfps = {CVAR_SAVE, "showfps", "0", "shows your rendered fps (frames per second)"};
91 cvar_t showsound = {CVAR_SAVE, "showsound", "0", "shows number of active sound sources, sound latency, and other statistics"};
92 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"};
93 cvar_t showtime = {CVAR_SAVE, "showtime", "0", "shows current time of day (useful on screenshots)"};
94 cvar_t showtime_format = {CVAR_SAVE, "showtime_format", "%H:%M:%S", "format string for time of day"};
95 cvar_t showdate = {CVAR_SAVE, "showdate", "0", "shows current date (useful on screenshots)"};
96 cvar_t showdate_format = {CVAR_SAVE, "showdate_format", "%Y-%m-%d", "format string for date"};
97 cvar_t sbar_alpha_bg = {CVAR_SAVE, "sbar_alpha_bg", "0.4", "opacity value of the statusbar background image"};
98 cvar_t sbar_alpha_fg = {CVAR_SAVE, "sbar_alpha_fg", "1", "opacity value of the statusbar weapon/item icons and numbers"};
99 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)"};
100 cvar_t sbar_scorerank = {CVAR_SAVE, "sbar_scorerank", "1", "shows an overlay for your score (or team score) and rank in the scoreboard"};
101 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)"};
102 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"};
103 cvar_t sbar_flagstatus_right = {CVAR_SAVE, "sbar_flagstatus_right", "0", "moves Nexuiz flag status icons to the right"};
104 cvar_t sbar_flagstatus_pos = {CVAR_SAVE, "sbar_flagstatus_pos", "115", "pixel position of the Nexuiz flag status icons, from the bottom"};
105
106 cvar_t cl_deathscoreboard = {0, "cl_deathscoreboard", "1", "shows scoreboard (+showscores) while dead"};
107
108 cvar_t crosshair_color_red = {CVAR_SAVE, "crosshair_color_red", "1", "customizable crosshair color"};
109 cvar_t crosshair_color_green = {CVAR_SAVE, "crosshair_color_green", "0", "customizable crosshair color"};
110 cvar_t crosshair_color_blue = {CVAR_SAVE, "crosshair_color_blue", "0", "customizable crosshair color"};
111 cvar_t crosshair_color_alpha = {CVAR_SAVE, "crosshair_color_alpha", "1", "how opaque the crosshair should be"};
112 cvar_t crosshair_size = {CVAR_SAVE, "crosshair_size", "1", "adjusts size of the crosshair on the screen"};
113
114 void Sbar_MiniDeathmatchOverlay (int x, int y);
115 void Sbar_DeathmatchOverlay (void);
116 void Sbar_IntermissionOverlay (void);
117 void Sbar_FinaleOverlay (void);
118
119 void CL_VM_UpdateShowingScoresState (int showingscores);
120
121
122 /*
123 ===============
124 Sbar_ShowScores
125
126 Tab key down
127 ===============
128 */
129 void Sbar_ShowScores (void)
130 {
131         if (sb_showscores)
132                 return;
133         sb_showscores = true;
134         CL_VM_UpdateShowingScoresState(sb_showscores);
135 }
136
137 /*
138 ===============
139 Sbar_DontShowScores
140
141 Tab key up
142 ===============
143 */
144 void Sbar_DontShowScores (void)
145 {
146         sb_showscores = false;
147         CL_VM_UpdateShowingScoresState(sb_showscores);
148 }
149
150 void sbar_start(void)
151 {
152         int i;
153
154         if (gamemode == GAME_DELUXEQUAKE)
155         {
156         }
157         else if (gamemode == GAME_SOM)
158         {
159                 sb_disc = Draw_CachePic ("gfx/disc");
160
161                 for (i = 0;i < 10;i++)
162                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
163
164                 somsb_health = Draw_CachePic ("gfx/hud_health");
165                 somsb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
166                 somsb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
167                 somsb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
168                 somsb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
169                 somsb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
170                 somsb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
171                 somsb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
172         }
173         else if (gamemode == GAME_NEXUIZ)
174         {
175                 for (i = 0;i < 10;i++)
176                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
177                 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
178                 sb_colon = Draw_CachePic ("gfx/num_colon");
179
180                 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
181                 sb_ammo[1] = Draw_CachePic ("gfx/sb_bullets");
182                 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
183                 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
184
185                 sb_armor[0] = Draw_CachePic ("gfx/sb_armor");
186                 sb_armor[1] = NULL;
187                 sb_armor[2] = NULL;
188
189                 sb_health = Draw_CachePic ("gfx/sb_health");
190
191                 sb_items[2] = Draw_CachePic ("gfx/sb_slowmo");
192                 sb_items[3] = Draw_CachePic ("gfx/sb_invinc");
193                 sb_items[4] = Draw_CachePic ("gfx/sb_energy");
194                 sb_items[5] = Draw_CachePic ("gfx/sb_str");
195
196                 sb_items[11] = Draw_CachePic ("gfx/sb_flag_red_taken");
197                 sb_items[12] = Draw_CachePic ("gfx/sb_flag_red_lost");
198                 sb_items[13] = Draw_CachePic ("gfx/sb_flag_red_carrying");
199                 sb_items[14] = Draw_CachePic ("gfx/sb_key_carrying");
200                 sb_items[15] = Draw_CachePic ("gfx/sb_flag_blue_taken");
201                 sb_items[16] = Draw_CachePic ("gfx/sb_flag_blue_lost");
202                 sb_items[17] = Draw_CachePic ("gfx/sb_flag_blue_carrying");
203
204                 sb_sbar = Draw_CachePic ("gfx/sbar");
205                 sb_sbar_minimal = Draw_CachePic ("gfx/sbar_minimal");
206                 sb_sbar_overlay = Draw_CachePic ("gfx/sbar_overlay");
207
208                 for(i = 0; i < 9;i++)
209                         sb_weapons[0][i] = Draw_CachePic (va("gfx/inv_weapon%i",i));
210         }
211         else if (gamemode == GAME_ZYMOTIC)
212         {
213                 zymsb_crosshair_center = Draw_CachePic ("gfx/hud/crosshair_center");
214                 zymsb_crosshair_line = Draw_CachePic ("gfx/hud/crosshair_line");
215                 zymsb_crosshair_health = Draw_CachePic ("gfx/hud/crosshair_health");
216                 zymsb_crosshair_clip = Draw_CachePic ("gfx/hud/crosshair_clip");
217                 zymsb_crosshair_ammo = Draw_CachePic ("gfx/hud/crosshair_ammo");
218                 zymsb_crosshair_background = Draw_CachePic ("gfx/hud/crosshair_background");
219                 zymsb_crosshair_left1 = Draw_CachePic ("gfx/hud/crosshair_left1");
220                 zymsb_crosshair_left2 = Draw_CachePic ("gfx/hud/crosshair_left2");
221                 zymsb_crosshair_right = Draw_CachePic ("gfx/hud/crosshair_right");
222         }
223         else
224         {
225                 sb_disc = Draw_CachePic ("gfx/disc");
226
227                 for (i = 0;i < 10;i++)
228                 {
229                         sb_nums[0][i] = Draw_CachePic (va("gfx/num_%i",i));
230                         sb_nums[1][i] = Draw_CachePic (va("gfx/anum_%i",i));
231                 }
232
233                 sb_nums[0][10] = Draw_CachePic ("gfx/num_minus");
234                 sb_nums[1][10] = Draw_CachePic ("gfx/anum_minus");
235
236                 sb_colon = Draw_CachePic ("gfx/num_colon");
237                 sb_slash = Draw_CachePic ("gfx/num_slash");
238
239                 sb_weapons[0][0] = Draw_CachePic ("gfx/inv_shotgun");
240                 sb_weapons[0][1] = Draw_CachePic ("gfx/inv_sshotgun");
241                 sb_weapons[0][2] = Draw_CachePic ("gfx/inv_nailgun");
242                 sb_weapons[0][3] = Draw_CachePic ("gfx/inv_snailgun");
243                 sb_weapons[0][4] = Draw_CachePic ("gfx/inv_rlaunch");
244                 sb_weapons[0][5] = Draw_CachePic ("gfx/inv_srlaunch");
245                 sb_weapons[0][6] = Draw_CachePic ("gfx/inv_lightng");
246
247                 sb_weapons[1][0] = Draw_CachePic ("gfx/inv2_shotgun");
248                 sb_weapons[1][1] = Draw_CachePic ("gfx/inv2_sshotgun");
249                 sb_weapons[1][2] = Draw_CachePic ("gfx/inv2_nailgun");
250                 sb_weapons[1][3] = Draw_CachePic ("gfx/inv2_snailgun");
251                 sb_weapons[1][4] = Draw_CachePic ("gfx/inv2_rlaunch");
252                 sb_weapons[1][5] = Draw_CachePic ("gfx/inv2_srlaunch");
253                 sb_weapons[1][6] = Draw_CachePic ("gfx/inv2_lightng");
254
255                 for (i = 0;i < 5;i++)
256                 {
257                         sb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_shotgun",i+1));
258                         sb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_sshotgun",i+1));
259                         sb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_nailgun",i+1));
260                         sb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_snailgun",i+1));
261                         sb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_rlaunch",i+1));
262                         sb_weapons[2+i][5] = Draw_CachePic (va("gfx/inva%i_srlaunch",i+1));
263                         sb_weapons[2+i][6] = Draw_CachePic (va("gfx/inva%i_lightng",i+1));
264                 }
265
266                 sb_ammo[0] = Draw_CachePic ("gfx/sb_shells");
267                 sb_ammo[1] = Draw_CachePic ("gfx/sb_nails");
268                 sb_ammo[2] = Draw_CachePic ("gfx/sb_rocket");
269                 sb_ammo[3] = Draw_CachePic ("gfx/sb_cells");
270
271                 sb_armor[0] = Draw_CachePic ("gfx/sb_armor1");
272                 sb_armor[1] = Draw_CachePic ("gfx/sb_armor2");
273                 sb_armor[2] = Draw_CachePic ("gfx/sb_armor3");
274
275                 sb_items[0] = Draw_CachePic ("gfx/sb_key1");
276                 sb_items[1] = Draw_CachePic ("gfx/sb_key2");
277                 sb_items[2] = Draw_CachePic ("gfx/sb_invis");
278                 sb_items[3] = Draw_CachePic ("gfx/sb_invuln");
279                 sb_items[4] = Draw_CachePic ("gfx/sb_suit");
280                 sb_items[5] = Draw_CachePic ("gfx/sb_quad");
281
282                 sb_sigil[0] = Draw_CachePic ("gfx/sb_sigil1");
283                 sb_sigil[1] = Draw_CachePic ("gfx/sb_sigil2");
284                 sb_sigil[2] = Draw_CachePic ("gfx/sb_sigil3");
285                 sb_sigil[3] = Draw_CachePic ("gfx/sb_sigil4");
286
287                 sb_faces[4][0] = Draw_CachePic ("gfx/face1");
288                 sb_faces[4][1] = Draw_CachePic ("gfx/face_p1");
289                 sb_faces[3][0] = Draw_CachePic ("gfx/face2");
290                 sb_faces[3][1] = Draw_CachePic ("gfx/face_p2");
291                 sb_faces[2][0] = Draw_CachePic ("gfx/face3");
292                 sb_faces[2][1] = Draw_CachePic ("gfx/face_p3");
293                 sb_faces[1][0] = Draw_CachePic ("gfx/face4");
294                 sb_faces[1][1] = Draw_CachePic ("gfx/face_p4");
295                 sb_faces[0][0] = Draw_CachePic ("gfx/face5");
296                 sb_faces[0][1] = Draw_CachePic ("gfx/face_p5");
297
298                 sb_face_invis = Draw_CachePic ("gfx/face_invis");
299                 sb_face_invuln = Draw_CachePic ("gfx/face_invul2");
300                 sb_face_invis_invuln = Draw_CachePic ("gfx/face_inv2");
301                 sb_face_quad = Draw_CachePic ("gfx/face_quad");
302
303                 sb_sbar = Draw_CachePic ("gfx/sbar");
304                 sb_ibar = Draw_CachePic ("gfx/ibar");
305                 sb_scorebar = Draw_CachePic ("gfx/scorebar");
306
307         //MED 01/04/97 added new hipnotic weapons
308                 if (gamemode == GAME_HIPNOTIC)
309                 {
310                         hsb_weapons[0][0] = Draw_CachePic ("gfx/inv_laser");
311                         hsb_weapons[0][1] = Draw_CachePic ("gfx/inv_mjolnir");
312                         hsb_weapons[0][2] = Draw_CachePic ("gfx/inv_gren_prox");
313                         hsb_weapons[0][3] = Draw_CachePic ("gfx/inv_prox_gren");
314                         hsb_weapons[0][4] = Draw_CachePic ("gfx/inv_prox");
315
316                         hsb_weapons[1][0] = Draw_CachePic ("gfx/inv2_laser");
317                         hsb_weapons[1][1] = Draw_CachePic ("gfx/inv2_mjolnir");
318                         hsb_weapons[1][2] = Draw_CachePic ("gfx/inv2_gren_prox");
319                         hsb_weapons[1][3] = Draw_CachePic ("gfx/inv2_prox_gren");
320                         hsb_weapons[1][4] = Draw_CachePic ("gfx/inv2_prox");
321
322                         for (i = 0;i < 5;i++)
323                         {
324                                 hsb_weapons[2+i][0] = Draw_CachePic (va("gfx/inva%i_laser",i+1));
325                                 hsb_weapons[2+i][1] = Draw_CachePic (va("gfx/inva%i_mjolnir",i+1));
326                                 hsb_weapons[2+i][2] = Draw_CachePic (va("gfx/inva%i_gren_prox",i+1));
327                                 hsb_weapons[2+i][3] = Draw_CachePic (va("gfx/inva%i_prox_gren",i+1));
328                                 hsb_weapons[2+i][4] = Draw_CachePic (va("gfx/inva%i_prox",i+1));
329                         }
330
331                         hsb_items[0] = Draw_CachePic ("gfx/sb_wsuit");
332                         hsb_items[1] = Draw_CachePic ("gfx/sb_eshld");
333                 }
334                 else if (gamemode == GAME_ROGUE)
335                 {
336                         rsb_invbar[0] = Draw_CachePic ("gfx/r_invbar1");
337                         rsb_invbar[1] = Draw_CachePic ("gfx/r_invbar2");
338
339                         rsb_weapons[0] = Draw_CachePic ("gfx/r_lava");
340                         rsb_weapons[1] = Draw_CachePic ("gfx/r_superlava");
341                         rsb_weapons[2] = Draw_CachePic ("gfx/r_gren");
342                         rsb_weapons[3] = Draw_CachePic ("gfx/r_multirock");
343                         rsb_weapons[4] = Draw_CachePic ("gfx/r_plasma");
344
345                         rsb_items[0] = Draw_CachePic ("gfx/r_shield1");
346                         rsb_items[1] = Draw_CachePic ("gfx/r_agrav1");
347
348         // PGM 01/19/97 - team color border
349                         rsb_teambord = Draw_CachePic ("gfx/r_teambord");
350         // PGM 01/19/97 - team color border
351
352                         rsb_ammo[0] = Draw_CachePic ("gfx/r_ammolava");
353                         rsb_ammo[1] = Draw_CachePic ("gfx/r_ammomulti");
354                         rsb_ammo[2] = Draw_CachePic ("gfx/r_ammoplasma");
355                 }
356         }
357
358         sb_ranking = Draw_CachePic ("gfx/ranking");
359         sb_complete = Draw_CachePic ("gfx/complete");
360         sb_inter = Draw_CachePic ("gfx/inter");
361         sb_finale = Draw_CachePic ("gfx/finale");
362 }
363
364 void sbar_shutdown(void)
365 {
366 }
367
368 void sbar_newmap(void)
369 {
370 }
371
372 void Sbar_Init (void)
373 {
374         Cmd_AddCommand("+showscores", Sbar_ShowScores, "show scoreboard");
375         Cmd_AddCommand("-showscores", Sbar_DontShowScores, "hide scoreboard");
376         Cvar_RegisterVariable(&showfps);
377         Cvar_RegisterVariable(&showsound);
378         Cvar_RegisterVariable(&showspeed);
379         Cvar_RegisterVariable(&showtime);
380         Cvar_RegisterVariable(&showtime_format);
381         Cvar_RegisterVariable(&showdate);
382         Cvar_RegisterVariable(&showdate_format);
383         Cvar_RegisterVariable(&sbar_alpha_bg);
384         Cvar_RegisterVariable(&sbar_alpha_fg);
385         Cvar_RegisterVariable(&sbar_hudselector);
386         Cvar_RegisterVariable(&sbar_scorerank);
387         Cvar_RegisterVariable(&sbar_gametime);
388         Cvar_RegisterVariable(&sbar_miniscoreboard_size);
389         Cvar_RegisterVariable(&cl_deathscoreboard);
390
391         Cvar_RegisterVariable(&crosshair_color_red);
392         Cvar_RegisterVariable(&crosshair_color_green);
393         Cvar_RegisterVariable(&crosshair_color_blue);
394         Cvar_RegisterVariable(&crosshair_color_alpha);
395         Cvar_RegisterVariable(&crosshair_size);
396
397         if(gamemode == GAME_NEXUIZ)
398         {
399                 Cvar_RegisterVariable(&sbar_flagstatus_right); // this cvar makes no sense in other games
400                 Cvar_RegisterVariable(&sbar_flagstatus_pos); // this cvar makes no sense in other games
401         }
402
403         R_RegisterModule("sbar", sbar_start, sbar_shutdown, sbar_newmap);
404 }
405
406
407 //=============================================================================
408
409 // drawing routines are relative to the status bar location
410
411 int sbar_x, sbar_y;
412
413 /*
414 =============
415 Sbar_DrawPic
416 =============
417 */
418 void Sbar_DrawStretchPic (int x, int y, cachepic_t *pic, float alpha, float overridewidth, float overrideheight)
419 {
420         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, overridewidth, overrideheight, 1, 1, 1, alpha, 0);
421 }
422
423 void Sbar_DrawPic (int x, int y, cachepic_t *pic)
424 {
425         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, sbar_alpha_fg.value, 0);
426 }
427
428 void Sbar_DrawAlphaPic (int x, int y, cachepic_t *pic, float alpha)
429 {
430         DrawQ_Pic (sbar_x + x, sbar_y + y, pic, 0, 0, 1, 1, 1, alpha, 0);
431 }
432
433 /*
434 ================
435 Sbar_DrawCharacter
436
437 Draws one solid graphics character
438 ================
439 */
440 void Sbar_DrawCharacter (int x, int y, int num)
441 {
442         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);
443 }
444
445 /*
446 ================
447 Sbar_DrawString
448 ================
449 */
450 void Sbar_DrawString (int x, int y, char *str)
451 {
452         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);
453 }
454
455 /*
456 =============
457 Sbar_DrawNum
458 =============
459 */
460 void Sbar_DrawNum (int x, int y, int num, int digits, int color)
461 {
462         char str[32], *ptr;
463         int l, frame;
464
465         l = dpsnprintf(str, sizeof(str), "%i", num);
466         ptr = str;
467         if (l > digits)
468                 ptr += (l-digits);
469         if (l < digits)
470                 x += (digits-l)*24;
471
472         while (*ptr)
473         {
474                 if (*ptr == '-')
475                         frame = STAT_MINUS;
476                 else
477                         frame = *ptr -'0';
478
479                 Sbar_DrawPic (x, y, sb_nums[color][frame]);
480                 x += 24;
481
482                 ptr++;
483         }
484 }
485
486 /*
487 =============
488 Sbar_DrawXNum
489 =============
490 */
491
492 void Sbar_DrawXNum (int x, int y, int num, int digits, int lettersize, float r, float g, float b, float a, int flags)
493 {
494         char str[32], *ptr;
495         int l, frame;
496
497         if (digits < 0)
498         {
499                 digits = -digits;
500                 l = dpsnprintf(str, sizeof(str), "%0*i", digits, num);
501         }
502         else
503                 l = dpsnprintf(str, sizeof(str), "%i", num);
504         ptr = str;
505         if (l > digits)
506                 ptr += (l-digits);
507         if (l < digits)
508                 x += (digits-l) * lettersize;
509
510         while (*ptr)
511         {
512                 if (*ptr == '-')
513                         frame = STAT_MINUS;
514                 else
515                         frame = *ptr -'0';
516
517                 DrawQ_Pic (sbar_x + x, sbar_y + y, sb_nums[0][frame],lettersize,lettersize,r,g,b,a * sbar_alpha_fg.value,flags);
518                 x += lettersize;
519
520                 ptr++;
521         }
522 }
523
524 //=============================================================================
525
526
527 int Sbar_IsTeammatch()
528 {
529         // currently only nexuiz uses the team score board
530         return ((gamemode == GAME_NEXUIZ)
531                 && (teamplay.integer > 0));
532 }
533
534 /*
535 ===============
536 Sbar_SortFrags
537 ===============
538 */
539 static int fragsort[MAX_SCOREBOARD];
540 static int scoreboardlines;
541
542 int Sbar_GetSortedPlayerIndex (int index)
543 {
544         return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
545 }
546
547 static scoreboard_t teams[MAX_SCOREBOARD];
548 static int teamsort[MAX_SCOREBOARD];
549 static int teamlines;
550 void Sbar_SortFrags (void)
551 {
552         int i, j, k, color;
553
554         // sort by frags
555         scoreboardlines = 0;
556         for (i=0 ; i<cl.maxclients ; i++)
557         {
558                 if (cl.scores[i].name[0])
559                 {
560                         fragsort[scoreboardlines] = i;
561                         scoreboardlines++;
562                 }
563         }
564
565         for (i=0 ; i<scoreboardlines ; i++)
566                 for (j=0 ; j<scoreboardlines-1-i ; j++)
567                         if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
568                         {
569                                 k = fragsort[j];
570                                 fragsort[j] = fragsort[j+1];
571                                 fragsort[j+1] = k;
572                         }
573
574         teamlines = 0;
575         if (Sbar_IsTeammatch ())
576         {
577                 // now sort players by teams.
578                 for (i=0 ; i<scoreboardlines ; i++)
579                 {
580                         for (j=0 ; j<scoreboardlines-1-i ; j++)
581                         {
582                                 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
583                                 {
584                                         k = fragsort[j];
585                                         fragsort[j] = fragsort[j+1];
586                                         fragsort[j+1] = k;
587                                 }
588                         }
589                 }
590
591                 // calculate team scores
592                 color = -1;
593                 for (i=0 ; i<scoreboardlines ; i++)
594                 {
595                         if (color != (cl.scores[fragsort[i]].colors & 15))
596                         {
597                                 const char* teamname;
598
599                                 color = cl.scores[fragsort[i]].colors & 15;
600                                 teamlines++;
601
602                                 switch (color)
603                                 {
604                                         case 4:
605                                                 teamname = "^1Red Team";
606                                                 break;
607                                         case 13:
608                                                 teamname = "^4Blue Team";
609                                                 break;
610                                         case 9:
611                                                 teamname = "^6Pink Team";
612                                                 break;
613                                         case 12:
614                                                 teamname = "^3Yellow Team";
615                                                 break;
616                                         default:
617                                                 teamname = "Total Team Score";
618                                                 break;
619                                 }
620                                 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
621
622                                 teams[teamlines-1].frags = 0;
623                                 teams[teamlines-1].colors = color + 16 * color;
624                         }
625
626                         if (cl.scores[fragsort[i]].frags != -666)
627                         {
628                                 // do not add spedcators
629                                 // (ugly hack for nexuiz)
630                                 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
631                         }
632                 }
633
634                 // now sort teams by scores.
635                 for (i=0 ; i<teamlines ; i++)
636                         teamsort[i] = i;
637                 for (i=0 ; i<teamlines ; i++)
638                 {
639                         for (j=0 ; j<teamlines-1-i ; j++)
640                         {
641                                 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
642                                 {
643                                         k = teamsort[j];
644                                         teamsort[j] = teamsort[j+1];
645                                         teamsort[j+1] = k;
646                                 }
647                         }
648                 }
649         }
650 }
651
652 /*
653 ===============
654 Sbar_SoloScoreboard
655 ===============
656 */
657 void Sbar_SoloScoreboard (void)
658 {
659 #if 1
660         char    str[80], timestr[40];
661         int             max, timelen;
662         int             minutes, seconds;
663         double  t;
664
665         t = (cl.intermission ? cl.completed_time : cl.time);
666         minutes = (int)(t / 60);
667         seconds = (int)(t - 60*floor(t/60));
668
669         // monsters and secrets are now both on the top row
670         if (cl.stats[STAT_TOTALMONSTERS])
671                 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
672         if (cl.stats[STAT_TOTALSECRETS])
673                 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
674
675         // figure out the map's filename without path or extension
676         strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
677         if (strrchr(str, '.'))
678                 *(strrchr(str, '.')) = 0;
679
680         // append a : separator and then the full title
681         strlcat(str, ":", sizeof(str));
682         strlcat(str, cl.levelname, sizeof(str));
683
684         // if there's a newline character, terminate the string there
685         if (strchr(str, '\n'))
686                 *(strchr(str, '\n')) = 0;
687
688         // make the time string
689         timelen = dpsnprintf(timestr, sizeof(timestr), " %i:%02i", minutes, seconds);
690
691         // truncate the level name if necessary to make room for time
692         max = 38 - timelen;
693         if ((int)strlen(str) > max)
694                 str[max] = 0;
695
696         // print the filename and message
697         Sbar_DrawString(8, 12, str);
698
699         // print the time
700         Sbar_DrawString(8 + max*8, 12, timestr);
701
702 #else
703         char    str[80];
704         int             minutes, seconds, tens, units;
705         int             l;
706
707         if (gamemode != GAME_NEXUIZ) {
708                 dpsnprintf (str, sizeof(str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
709                 Sbar_DrawString (8, 4, str);
710
711                 dpsnprintf (str, sizeof(str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
712                 Sbar_DrawString (8, 12, str);
713         }
714
715 // time
716         minutes = (int)(cl.time / 60);
717         seconds = (int)(cl.time - 60*minutes);
718         tens = seconds / 10;
719         units = seconds - 10*tens;
720         dpsnprintf (str, sizeof(str), "Time :%3i:%i%i", minutes, tens, units);
721         Sbar_DrawString (184, 4, str);
722
723 // draw level name
724         if (gamemode == GAME_NEXUIZ) {
725                 l = (int) strlen (cl.worldmodel->name);
726                 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
727         } else {
728                 l = (int) strlen (cl.levelname);
729                 Sbar_DrawString (232 - l*4, 12, cl.levelname);
730         }
731 #endif
732 }
733
734 /*
735 ===============
736 Sbar_DrawScoreboard
737 ===============
738 */
739 void Sbar_DrawScoreboard (void)
740 {
741         Sbar_SoloScoreboard ();
742         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
743         //if (cl.gametype == GAME_DEATHMATCH)
744         if (!cl.islocalgame)
745                 Sbar_DeathmatchOverlay ();
746 }
747
748 //=============================================================================
749
750 // AK to make DrawInventory smaller
751 static void Sbar_DrawWeapon(int nr, float fade, int active)
752 {
753         if (sbar_hudselector.integer == 1)
754         {
755                 // width = 300, height = 100
756                 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
757
758                 DrawQ_Pic((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr, vid_conheight.integer - w_height, sb_weapons[0][nr], w_width, w_height, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 0.6, (active ? 1 : 0.6) * fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
759                 // FIXME ??
760                 DrawQ_String((vid_conwidth.integer - w_width * 9) * 0.5 + w_width * nr + w_space, vid_conheight.integer - w_height + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 1, 0, sbar_alpha_fg.value, 0, NULL, true);
761         }
762         else
763         {
764                 // width = 300, height = 100
765                 const int w_width = 300, w_height = 100, w_space = 10;
766                 const float w_scale = 0.4;
767
768                 DrawQ_Pic(vid_conwidth.integer - (w_width + w_space) * w_scale, (w_height + w_space) * w_scale * nr + w_space, sb_weapons[0][nr], w_width * w_scale, w_height * w_scale, (active) ? 1 : 0.6, active ? 1 : 0.6, active ? 1 : 1, fade * sbar_alpha_fg.value, DRAWFLAG_NORMAL);
769                 //DrawQ_String(vid_conwidth.integer - (w_space + font_size ), (w_height + w_space) * w_scale * nr + w_space, va("%i",nr+1), 0, font_size, font_size, 1, 0, 0, fade, 0, NULL, true);
770         }
771 }
772
773 /*
774 ===============
775 Sbar_DrawInventory
776 ===============
777 */
778 void Sbar_DrawInventory (void)
779 {
780         int i;
781         char num[6];
782         float time;
783         int flashon;
784
785         if (gamemode == GAME_ROGUE)
786         {
787                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
788                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
789                 else
790                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
791         }
792         else
793                 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
794
795         // weapons
796         for (i=0 ; i<7 ; i++)
797         {
798                 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
799                 {
800                         time = cl.item_gettime[i];
801                         flashon = (int)(max(0, cl.time - time)*10);
802                         if (flashon >= 10)
803                         {
804                                 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )
805                                         flashon = 1;
806                                 else
807                                         flashon = 0;
808                         }
809                         else
810                                 flashon = (flashon%5) + 2;
811
812                         Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
813                 }
814         }
815
816         // MED 01/04/97
817         // hipnotic weapons
818         if (gamemode == GAME_HIPNOTIC)
819         {
820                 int grenadeflashing=0;
821                 for (i=0 ; i<4 ; i++)
822                 {
823                         if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
824                         {
825                                 time = max(0, cl.item_gettime[hipweapons[i]]);
826                                 flashon = (int)((cl.time - time)*10);
827                                 if (flashon >= 10)
828                                 {
829                                         if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])  )
830                                                 flashon = 1;
831                                         else
832                                                 flashon = 0;
833                                 }
834                                 else
835                                         flashon = (flashon%5) + 2;
836
837                                 // check grenade launcher
838                                 if (i==2)
839                                 {
840                                         if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
841                                         {
842                                                 if (flashon)
843                                                 {
844                                                         grenadeflashing = 1;
845                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
846                                                 }
847                                         }
848                                 }
849                                 else if (i==3)
850                                 {
851                                         if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
852                                         {
853                                                 if (!grenadeflashing)
854                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
855                                         }
856                                         else
857                                                 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
858                                 }
859                                 else
860                                         Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
861                         }
862                 }
863         }
864
865         if (gamemode == GAME_ROGUE)
866         {
867                 // check for powered up weapon.
868                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
869                         for (i=0;i<5;i++)
870                                 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
871                                         Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
872         }
873
874         // ammo counts
875         for (i=0 ; i<4 ; i++)
876         {
877                 dpsnprintf (num, sizeof(num), "%4i",cl.stats[STAT_SHELLS+i] );
878                 if (num[0] != ' ')
879                         Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
880                 if (num[1] != ' ')
881                         Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
882                 if (num[2] != ' ')
883                         Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
884                 if (num[3] != ' ')
885                         Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
886         }
887
888         // items
889         for (i=0 ; i<6 ; i++)
890                 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
891                 {
892                         //MED 01/04/97 changed keys
893                         if (gamemode != GAME_HIPNOTIC || (i>1))
894                                 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
895                 }
896
897         //MED 01/04/97 added hipnotic items
898         // hipnotic items
899         if (gamemode == GAME_HIPNOTIC)
900         {
901                 for (i=0 ; i<2 ; i++)
902                         if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
903                                 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
904         }
905
906         if (gamemode == GAME_ROGUE)
907         {
908                 // new rogue items
909                 for (i=0 ; i<2 ; i++)
910                         if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
911                                 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
912         }
913         else
914         {
915                 // sigils
916                 for (i=0 ; i<4 ; i++)
917                         if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
918                                 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
919         }
920 }
921
922 //=============================================================================
923
924 /*
925 ===============
926 Sbar_DrawFrags
927 ===============
928 */
929 void Sbar_DrawFrags (void)
930 {
931         int i, k, l, x, f;
932         char num[12];
933         scoreboard_t *s;
934         unsigned char *c;
935
936         Sbar_SortFrags ();
937
938         // draw the text
939         l = min(scoreboardlines, 4);
940
941         x = 23 * 8;
942
943         for (i = 0;i < l;i++)
944         {
945                 k = fragsort[i];
946                 s = &cl.scores[k];
947
948                 // draw background
949                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
950                 DrawQ_Fill (sbar_x + x + 10, sbar_y     - 23, 28, 4, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
951                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
952                 DrawQ_Fill (sbar_x + x + 10, sbar_y + 4 - 23, 28, 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
953
954                 // draw number
955                 f = s->frags;
956                 dpsnprintf (num, sizeof(num), "%3i",f);
957
958                 if (k == cl.viewentity - 1)
959                 {
960                         Sbar_DrawCharacter ( x      + 2, -24, 16);
961                         Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
962                 }
963                 Sbar_DrawCharacter (x +  8, -24, num[0]);
964                 Sbar_DrawCharacter (x + 16, -24, num[1]);
965                 Sbar_DrawCharacter (x + 24, -24, num[2]);
966                 x += 32;
967         }
968 }
969
970 //=============================================================================
971
972
973 /*
974 ===============
975 Sbar_DrawFace
976 ===============
977 */
978 void Sbar_DrawFace (void)
979 {
980         int f;
981
982 // PGM 01/19/97 - team color drawing
983 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
984         if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
985         {
986                 char num[12];
987                 scoreboard_t *s;
988                 unsigned char *c;
989
990                 s = &cl.scores[cl.viewentity - 1];
991                 // draw background
992                 Sbar_DrawPic (112, 0, rsb_teambord);
993                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
994                 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+3, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
995                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
996                 DrawQ_Fill (sbar_x + 113, vid_conheight.integer-SBAR_HEIGHT+12, 22, 9, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
997
998                 // draw number
999                 f = s->frags;
1000                 dpsnprintf (num, sizeof(num), "%3i",f);
1001
1002                 if ((s->colors & 0xf0)==0)
1003                 {
1004                         if (num[0] != ' ')
1005                                 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1006                         if (num[1] != ' ')
1007                                 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1008                         if (num[2] != ' ')
1009                                 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1010                 }
1011                 else
1012                 {
1013                         Sbar_DrawCharacter ( 109, 3, num[0]);
1014                         Sbar_DrawCharacter ( 116, 3, num[1]);
1015                         Sbar_DrawCharacter ( 123, 3, num[2]);
1016                 }
1017
1018                 return;
1019         }
1020 // PGM 01/19/97 - team color drawing
1021
1022         if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1023                 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1024         else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1025                 Sbar_DrawPic (112, 0, sb_face_quad );
1026         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1027                 Sbar_DrawPic (112, 0, sb_face_invis );
1028         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1029                 Sbar_DrawPic (112, 0, sb_face_invuln);
1030         else
1031         {
1032                 f = cl.stats[STAT_HEALTH] / 20;
1033                 f = bound(0, f, 4);
1034                 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1035         }
1036 }
1037
1038 void Sbar_ShowFPS(void)
1039 {
1040         float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1041         char soundstring[32];
1042         char fpsstring[32];
1043         char timestring[32];
1044         char datestring[32];
1045         char speedstring[32];
1046         qboolean red = false;
1047         soundstring[0] = 0;
1048         fpsstring[0] = 0;
1049         timestring[0] = 0;
1050         datestring[0] = 0;
1051         speedstring[0] = 0;
1052         if (showfps.integer)
1053         {
1054                 float calc;
1055                 static double nexttime = 0, lasttime = 0;
1056                 static double framerate = 0;
1057                 static int framecount = 0;
1058                 double interval = 0.25;
1059                 double newtime;
1060                 newtime = realtime;
1061                 if (newtime >= nexttime)
1062                 {
1063                         framerate = framecount / (newtime - lasttime);
1064                         if (nexttime < newtime - interval * 1.5)
1065                                 nexttime = newtime;
1066                         lasttime = newtime;
1067                         nexttime += interval;
1068                         framecount = 0;
1069                 }
1070                 framecount++;
1071                 calc = framerate;
1072
1073                 if ((red = (calc < 1.0f)))
1074                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1075                 else
1076                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1077         }
1078         if (showtime.integer)
1079                 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1080         if (showdate.integer)
1081                 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1082         if (showsound.integer)
1083                 dpsnprintf(soundstring, sizeof(soundstring), "%4i/%4i at %3ims\n", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1084         if (showspeed.integer)
1085         {
1086                 double speed, speedxy, f;
1087                 const char *unit;
1088                 speed = VectorLength(cl.movement_velocity);
1089                 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1090                 switch(showspeed.integer)
1091                 {
1092                         default:
1093                         case 1:
1094                                 if(gamemode == GAME_NEXUIZ)
1095                                         unit = "in/s";
1096                                 else
1097                                         unit = "qu/s";
1098                                 f = 1.0;
1099                                 break;
1100                         case 2:
1101                                 unit = "m/s";
1102                                 f = 0.0254;
1103                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1104                                 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1105                                 break;
1106                         case 3:
1107                                 unit = "km/h";
1108                                 f = 0.0254 * 3.6;
1109                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1110                                 break;
1111                         case 4:
1112                                 unit = "mph";
1113                                 f = 0.0254 * 3.6 * 0.6213711922;
1114                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1115                                 break;
1116                         case 5:
1117                                 unit = "knots";
1118                                 f = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1119                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1120                                 break;
1121                 }
1122                 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1123         }
1124         if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0])
1125         {
1126                 fps_scalex = 12;
1127                 fps_scaley = 12;
1128                 fps_height = fps_scaley * ((soundstring[0] != 0) + (fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0));
1129                 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1130                 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1131                 fps_y = vid_conheight.integer - fps_height;
1132                 if (soundstring[0])
1133                 {
1134                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(soundstring, 0, true, FONT_INFOBAR) * fps_scalex;
1135                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1136                         DrawQ_String_Font(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1137                         fps_y += fps_scaley;
1138                 }
1139                 if (fpsstring[0])
1140                 {
1141                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1142                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1143                         if (red)
1144                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1145                         else
1146                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1147                         fps_y += fps_scaley;
1148                 }
1149                 if (timestring[0])
1150                 {
1151                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1152                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1153                         DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1154                         fps_y += fps_scaley;
1155                 }
1156                 if (datestring[0])
1157                 {
1158                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1159                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1160                         DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1161                         fps_y += fps_scaley;
1162                 }
1163                 if (speedstring[0])
1164                 {
1165                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1166                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1167                         DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1168                         fps_y += fps_scaley;
1169                 }
1170         }
1171 }
1172
1173 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)
1174 {
1175         float r[5];
1176         c2 = bound(0, c2, 1);
1177         c1 = bound(0, c1, 1 - c2);
1178         r[0] = 0;
1179         r[1] = rangey + rangeheight * (c2 + c1);
1180         r[2] = rangey + rangeheight * (c2);
1181         r[3] = rangey;
1182         r[4] = height;
1183         if (r[1] > r[0])
1184                 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);
1185         if (r[2] > r[1])
1186                 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);
1187         if (r[3] > r[2])
1188                 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);
1189         if (r[4] > r[3])
1190                 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);
1191 }
1192
1193 /*
1194 ===============
1195 Sbar_Draw
1196 ===============
1197 */
1198 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1199 extern cvar_t v_kicktime;
1200 void Sbar_Score (int margin);
1201 void Sbar_Draw (void)
1202 {
1203         cachepic_t *pic;
1204
1205         if(cl.csqc_vidvars.drawenginesbar)      //[515]: csqc drawsbar
1206         {
1207                 if (sb_showscores)
1208                         Sbar_DrawScoreboard ();
1209                 else if (cl.intermission == 1)
1210                 {
1211                         if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1212                         {
1213                                 Sbar_DrawScoreboard();
1214                                 return;
1215                         }
1216                         Sbar_IntermissionOverlay();
1217                 }
1218                 else if (cl.intermission == 2)
1219                         Sbar_FinaleOverlay();
1220                 else if (gamemode == GAME_DELUXEQUAKE)
1221                 {
1222                 }
1223                 else if (gamemode == GAME_SOM)
1224                 {
1225                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1226                                 Sbar_DrawScoreboard ();
1227                         else if (sb_lines)
1228                         {
1229                                 // this is the top left of the sbar area
1230                                 sbar_x = 0;
1231                                 sbar_y = vid_conheight.integer - 24*3;
1232
1233                                 // armor
1234                                 if (cl.stats[STAT_ARMOR])
1235                                 {
1236                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1237                                                 Sbar_DrawPic(0, 0, somsb_armor[2]);
1238                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1239                                                 Sbar_DrawPic(0, 0, somsb_armor[1]);
1240                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1241                                                 Sbar_DrawPic(0, 0, somsb_armor[0]);
1242                                         Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1243                                 }
1244
1245                                 // health
1246                                 Sbar_DrawPic(0, 24, somsb_health);
1247                                 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1248
1249                                 // ammo icon
1250                                 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1251                                         Sbar_DrawPic(0, 48, somsb_ammo[0]);
1252                                 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1253                                         Sbar_DrawPic(0, 48, somsb_ammo[1]);
1254                                 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1255                                         Sbar_DrawPic(0, 48, somsb_ammo[2]);
1256                                 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1257                                         Sbar_DrawPic(0, 48, somsb_ammo[3]);
1258                                 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1259                                 if (cl.stats[STAT_SHELLS])
1260                                         Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1261                         }
1262                 }
1263                 else if (gamemode == GAME_NEXUIZ)
1264                 {
1265                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1266                         {
1267                                 sbar_x = (vid_conwidth.integer - 640)/2;
1268                                 sbar_y = vid_conheight.integer - 47;
1269                                 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1270                                 Sbar_DrawScoreboard ();
1271                         }
1272                         else if (sb_lines && sbar_hudselector.integer == 1)
1273                         {
1274                                 int i;
1275                                 float fade;
1276                                 int redflag, blueflag;
1277                                 float x;
1278
1279                                 sbar_x = (vid_conwidth.integer - 320)/2;
1280                                 sbar_y = vid_conheight.integer - 24 - 16;
1281
1282                                 // calculate intensity to draw weapons bar at
1283                                 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1284                                 fade = bound(0.7, fade, 1);
1285                                 for (i = 0; i < 8;i++)
1286                                         if (cl.stats[STAT_ITEMS] & (1 << i))
1287                                                 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1288                                 if((cl.stats[STAT_ITEMS] & (1<<12)))
1289                                         Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1290
1291                                 // flag icons
1292                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1293                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1294                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1295                                 if (redflag == 3 && blueflag == 3)
1296                                 {
1297                                         // The Impossible Combination[tm]
1298                                         // Can only happen in Key Hunt mode...
1299                                         Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1300                                 }
1301                                 else
1302                                 {
1303                                         if (redflag)
1304                                                 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1305                                         if (blueflag)
1306                                                 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1307                                 }
1308
1309                                 // armor
1310                                 if (cl.stats[STAT_ARMOR] > 0)
1311                                 {
1312                                         Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1313                                         if(cl.stats[STAT_ARMOR] > 200)
1314                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1315                                         else if(cl.stats[STAT_ARMOR] > 100)
1316                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1317                                         else if(cl.stats[STAT_ARMOR] > 50)
1318                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1319                                         else if(cl.stats[STAT_ARMOR] > 25)
1320                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1321                                         else
1322                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1323                                 }
1324
1325                                 // health
1326                                 if (cl.stats[STAT_HEALTH] != 0)
1327                                 {
1328                                         Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1329                                         if(cl.stats[STAT_HEALTH] > 200)
1330                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1331                                         else if(cl.stats[STAT_HEALTH] > 100)
1332                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1333                                         else if(cl.stats[STAT_HEALTH] > 50)
1334                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1335                                         else if(cl.stats[STAT_HEALTH] > 25)
1336                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1337                                         else
1338                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1339                                 }
1340
1341                                 // ammo
1342                                 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1343                                 {
1344                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1345                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1346                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1347                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1348                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1349                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1350                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1351                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1352                                         if(cl.stats[STAT_AMMO] > 10)
1353                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1354                                         else
1355                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1356                                 }
1357
1358                                 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1359                                         Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1360                                 if (sbar_x > 0)
1361                                         Sbar_Score(16);
1362                                         // The margin can be at most 8 to support 640x480 console size:
1363                                         //   320 + 2 * (144 + 16) = 640
1364                         }
1365                         else if (sb_lines)
1366                         {
1367                                 int i;
1368                                 float fade;
1369                                 int redflag, blueflag;
1370                                 float x;
1371
1372                                 sbar_x = (vid_conwidth.integer - 640)/2;
1373                                 sbar_y = vid_conheight.integer - 47;
1374
1375                                 // calculate intensity to draw weapons bar at
1376                                 fade = 3 - 2 * (cl.time - cl.weapontime);
1377                                 if (fade > 0)
1378                                 {
1379                                         fade = min(fade, 1);
1380                                         for (i = 0; i < 8;i++)
1381                                                 if (cl.stats[STAT_ITEMS] & (1 << i))
1382                                                         Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1383
1384                                         if((cl.stats[STAT_ITEMS] & (1<<12)))
1385                                                 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1386                                 }
1387
1388                                 //if (!cl.islocalgame)
1389                                 //      Sbar_DrawFrags ();
1390
1391                                 if (sb_lines > 24)
1392                                         Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1393                                 else
1394                                         Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1395
1396                                 // flag icons
1397                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1398                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1399                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1400                                 if (redflag == 3 && blueflag == 3)
1401                                 {
1402                                         // The Impossible Combination[tm]
1403                                         // Can only happen in Key Hunt mode...
1404                                         Sbar_DrawPic ((int) x, -179, sb_items[14]);
1405                                 }
1406                                 else
1407                                 {
1408                                         if (redflag)
1409                                                 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1410                                         if (blueflag)
1411                                                 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1412                                 }
1413
1414                                 // armor
1415                                 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1416
1417                                 // health
1418                                 if(cl.stats[STAT_HEALTH] > 100)
1419                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1420                                 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1421                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1422                                 else
1423                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1424
1425                                 // AK dont draw ammo for the laser
1426                                 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1427                                 {
1428                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1429                                                 Sbar_DrawPic (519, 0, sb_ammo[0]);
1430                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1431                                                 Sbar_DrawPic (519, 0, sb_ammo[1]);
1432                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1433                                                 Sbar_DrawPic (519, 0, sb_ammo[2]);
1434                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1435                                                 Sbar_DrawPic (519, 0, sb_ammo[3]);
1436
1437                                         if(cl.stats[STAT_AMMO] <= 10)
1438                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1439                                         else
1440                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1441
1442                                 }
1443
1444                                 if (sb_lines > 24)
1445                                         DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1446
1447                                 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1448                                         Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1449
1450                                 if (sbar_x > 0)
1451                                         Sbar_Score(-16);
1452                                         // Because:
1453                                         //   Mini scoreboard uses 12*4 per other team, that is, 144
1454                                         //   pixels when there are four teams...
1455                                         //   Nexuiz by default sets vid_conwidth to 800... makes
1456                                         //   sbar_x == 80...
1457                                         //   so we need to shift it by 64 pixels to the right to fit
1458                                         //   BUT: then it overlaps with the image that gets drawn
1459                                         //   for viewsize 100! Therefore, just account for 3 teams,
1460                                         //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1461                                         //   to the right!
1462                         }
1463                 }
1464                 else if (gamemode == GAME_ZYMOTIC)
1465                 {
1466 #if 1
1467                         float scale = 64.0f / 256.0f;
1468                         float kickoffset[3];
1469                         VectorClear(kickoffset);
1470                         if (v_dmg_time > 0)
1471                         {
1472                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1473                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1474                         }
1475                         sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1476                         sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1477                         // left1 16, 48 : 126 -66
1478                         // left2 16, 128 : 196 -66
1479                         // right 176, 48 : 196 -136
1480                         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);
1481                         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);
1482                         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);
1483                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1484 #else
1485                         float scale = 128.0f / 256.0f;
1486                         float healthstart, healthheight, healthstarttc, healthendtc;
1487                         float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1488                         float ammostart, ammoheight, ammostarttc, ammoendtc;
1489                         float clipstart, clipheight, clipstarttc, clipendtc;
1490                         float kickoffset[3], offset;
1491                         VectorClear(kickoffset);
1492                         if (v_dmg_time > 0)
1493                         {
1494                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1495                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1496                         }
1497                         sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1498                         sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1499                         offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1500                         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);
1501                         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);
1502                         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);
1503                         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);
1504                         healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1505                         shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1506                         healthstart = 204 - healthheight;
1507                         shieldstart = healthstart - shieldheight;
1508                         healthstarttc = healthstart * (1.0f / 256.0f);
1509                         healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1510                         shieldstarttc = shieldstart * (1.0f / 256.0f);
1511                         shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1512                         ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1513                         ammostart = 114 - ammoheight;
1514                         ammostarttc = ammostart * (1.0f / 256.0f);
1515                         ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1516                         clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1517                         clipstart = 190 - clipheight;
1518                         clipstarttc = clipstart * (1.0f / 256.0f);
1519                         clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1520                         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);
1521                         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);
1522                         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);
1523                         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);
1524                         DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1525                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1526 #endif
1527                 }
1528                 else // Quake and others
1529                 {
1530                         sbar_x = (vid_conwidth.integer - 320)/2;
1531                         sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1532                         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1533                         //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1534
1535                         if (sb_lines > 24)
1536                         {
1537                                 if (gamemode != GAME_GOODVSBAD2)
1538                                         Sbar_DrawInventory ();
1539                                 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1540                                         Sbar_DrawFrags ();
1541                         }
1542
1543                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1544                         {
1545                                 if (gamemode != GAME_GOODVSBAD2)
1546                                         Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1547                                 Sbar_DrawScoreboard ();
1548                         }
1549                         else if (sb_lines)
1550                         {
1551                                 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1552
1553                                 // keys (hipnotic only)
1554                                 //MED 01/04/97 moved keys here so they would not be overwritten
1555                                 if (gamemode == GAME_HIPNOTIC)
1556                                 {
1557                                         if (cl.stats[STAT_ITEMS] & IT_KEY1)
1558                                                 Sbar_DrawPic (209, 3, sb_items[0]);
1559                                         if (cl.stats[STAT_ITEMS] & IT_KEY2)
1560                                                 Sbar_DrawPic (209, 12, sb_items[1]);
1561                                 }
1562                                 // armor
1563                                 if (gamemode != GAME_GOODVSBAD2)
1564                                 {
1565                                         if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1566                                         {
1567                                                 Sbar_DrawNum (24, 0, 666, 3, 1);
1568                                                 Sbar_DrawPic (0, 0, sb_disc);
1569                                         }
1570                                         else
1571                                         {
1572                                                 if (gamemode == GAME_ROGUE)
1573                                                 {
1574                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1575                                                         if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1576                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1577                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1578                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1579                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1580                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1581                                                 }
1582                                                 else
1583                                                 {
1584                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1585                                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1586                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1587                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1588                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1589                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1590                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1591                                                 }
1592                                         }
1593                                 }
1594
1595                                 // face
1596                                 Sbar_DrawFace ();
1597
1598                                 // health
1599                                 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1600
1601                                 // ammo icon
1602                                 if (gamemode == GAME_ROGUE)
1603                                 {
1604                                         if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1605                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1606                                         else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1607                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1608                                         else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1609                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1610                                         else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1611                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1612                                         else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1613                                                 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1614                                         else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1615                                                 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1616                                         else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1617                                                 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1618                                 }
1619                                 else
1620                                 {
1621                                         if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1622                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1623                                         else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1624                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1625                                         else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1626                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1627                                         else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1628                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1629                                 }
1630
1631                                 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1632
1633                                 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1634                                 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1635                                 {
1636                                         if (gamemode == GAME_TRANSFUSION)
1637                                                 Sbar_MiniDeathmatchOverlay (0, 0);
1638                                         else
1639                                                 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1640                                         Sbar_Score(24);
1641                                 }
1642                         }
1643                 }
1644         }
1645
1646         Sbar_ShowFPS();
1647
1648         if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1649         {
1650                 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1651                 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);
1652         }
1653
1654         if (cl_prydoncursor.integer)
1655                 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);
1656 }
1657
1658 //=============================================================================
1659
1660 /*
1661 ==================
1662 Sbar_DeathmatchOverlay
1663
1664 ==================
1665 */
1666 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1667 {
1668         int minutes;
1669         qboolean myself = false;
1670         unsigned char *c;
1671         minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1672
1673         if((s - cl.scores) == cl.playerentity - 1)
1674                 myself = true;
1675         if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1676                 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1677                         myself = true;
1678
1679         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1680         {
1681                 if (s->qw_spectator)
1682                 {
1683                         if (s->qw_ping || s->qw_packetloss)
1684                                 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 );
1685                         else
1686                                 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 );
1687                 }
1688                 else
1689                 {
1690                         // draw colors behind score
1691                         //
1692                         //
1693                         //
1694                         //
1695                         //
1696                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1697                         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);
1698                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1699                         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);
1700                         // print the text
1701                         //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);
1702                         if (s->qw_ping || s->qw_packetloss)
1703                                 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 );
1704                         else
1705                                 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 );
1706                 }
1707         }
1708         else
1709         {
1710                 if (s->qw_spectator)
1711                 {
1712                         if (s->qw_ping || s->qw_packetloss)
1713                                 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 );
1714                         else
1715                                 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 );
1716                 }
1717                 else
1718                 {
1719                         // draw colors behind score
1720                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1721                         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);
1722                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1723                         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);
1724                         // print the text
1725                         //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);
1726                         if (s->qw_ping || s->qw_packetloss)
1727                                 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 );
1728                         else
1729                                 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 );
1730                 }
1731         }
1732         return 8;
1733 }
1734
1735 void Sbar_DeathmatchOverlay (void)
1736 {
1737         int i, y, xmin, xmax, ymin, ymax;
1738
1739         // request new ping times every two second
1740         if (cl.last_ping_request < realtime - 2 && cls.netcon)
1741         {
1742                 cl.last_ping_request = realtime;
1743                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1744                 {
1745                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1746                         MSG_WriteString(&cls.netcon->message, "pings");
1747                 }
1748                 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*/)
1749                 {
1750                         // 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
1751                         static int ping_anyway_counter = 0;
1752                         if(cl.parsingtextexpectingpingforscores == 1)
1753                         {
1754                                 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1755                                 if(++ping_anyway_counter >= 5)
1756                                         cl.parsingtextexpectingpingforscores = 0;
1757                         }
1758                         if(cl.parsingtextexpectingpingforscores != 1)
1759                         {
1760                                 ping_anyway_counter = 0;
1761                                 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1762                                 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1763                                 MSG_WriteString(&cls.netcon->message, "ping");
1764                         }
1765                 }
1766                 else
1767                 {
1768                         // 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
1769                         MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1770                         MSG_WriteString(&cls.netcon->message, "pings");
1771                 }
1772         }
1773
1774         // scores
1775         Sbar_SortFrags ();
1776
1777         ymin = 8;
1778         ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1779
1780         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1781                 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)
1782         else
1783                 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)
1784         xmax = vid_conwidth.integer - xmin;
1785
1786         if(gamemode == GAME_NEXUIZ)
1787                 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);
1788
1789         DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1790
1791         // draw the text
1792         y = 40;
1793         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1794         {
1795                 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 );
1796         }
1797         else
1798         {
1799                 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 );
1800         }
1801         y += 8;
1802
1803         if (Sbar_IsTeammatch ())
1804         {
1805                 // show team scores first
1806                 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1807                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1808                 y += 5;
1809         }
1810
1811         for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1812                 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1813 }
1814
1815 /*
1816 ==================
1817 Sbar_MiniDeathmatchOverlay
1818
1819 ==================
1820 */
1821 void Sbar_MiniDeathmatchOverlay (int x, int y)
1822 {
1823         int i, j, numlines, range_begin, range_end, myteam, teamsep;
1824
1825         // do not draw this if sbar_miniscoreboard_size is zero
1826         if(sbar_miniscoreboard_size.value == 0)
1827                 return;
1828         // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1829         if(sbar_miniscoreboard_size.value > 0)
1830                 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1831
1832         // scores
1833         Sbar_SortFrags ();
1834
1835         // decide where to print
1836         if (gamemode == GAME_TRANSFUSION)
1837                 numlines = (vid_conwidth.integer - x + 127) / 128;
1838         else
1839                 numlines = (vid_conheight.integer - y + 7) / 8;
1840
1841         // give up if there isn't room
1842         if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1843                 return;
1844
1845         //find us
1846         for (i = 0; i < scoreboardlines; i++)
1847                 if (fragsort[i] == cl.playerentity - 1)
1848                         break;
1849
1850         range_begin = 0;
1851         range_end = scoreboardlines;
1852         teamsep = 0;
1853
1854         if (gamemode != GAME_TRANSFUSION)
1855                 if (Sbar_IsTeammatch ())
1856                 {
1857                         // reserve space for the team scores
1858                         numlines -= teamlines;
1859
1860                         // find first and last player of my team (only draw the team totals and my own team)
1861                         range_begin = range_end = i;
1862                         myteam = cl.scores[fragsort[i]].colors & 15;
1863                         while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1864                                 --range_begin;
1865                         while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1866                                 ++range_end;
1867
1868                         // looks better than two players
1869                         if(numlines == 2)
1870                         {
1871                                 teamsep = 8;
1872                                 numlines = 1;
1873                         }
1874                 }
1875
1876         // figure out start
1877         i -= numlines/2;
1878         i = min(i, range_end - numlines);
1879         i = max(i, range_begin);
1880
1881         if (gamemode == GAME_TRANSFUSION)
1882         {
1883                 for (;i < range_end && x < vid_conwidth.integer;i++)
1884                         x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1885         }
1886         else
1887         {
1888                 if(range_end - i < numlines) // won't draw to bottom?
1889                         y += 8 * (numlines - (range_end - i)); // bottom align
1890                 // show team scores first
1891                 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1892                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1893                 y += teamsep;
1894                 for (;i < range_end && y < vid_conheight.integer;i++)
1895                         y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1896         }
1897 }
1898
1899 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1900 {
1901         static int const sortorder[16] =
1902         {
1903                 1001,
1904                 1002,
1905                 1003,
1906                 1004,
1907                 1, // red
1908                 1005,
1909                 1006,
1910                 1007,
1911                 1008,
1912                 4, // pink
1913                 1009,
1914                 1010,
1915                 3, // yellow
1916                 2, // blue
1917                 1011,
1918                 1012
1919         };
1920         const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1921         const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1922         int tc1 = sortorder[t1->colors & 15];
1923         int tc2 = sortorder[t2->colors & 15];
1924         return tc1 - tc2;
1925 }
1926
1927 void Sbar_Score (int margin)
1928 {
1929         int i, me, score, otherleader, place, distribution, minutes, seconds;
1930         double timeleft;
1931         int sbar_x_save = sbar_x;
1932         int sbar_y_save = sbar_y;
1933
1934
1935         sbar_y = (int) (vid_conheight.value - (32+12));
1936         sbar_x -= margin;
1937
1938         me = cl.playerentity - 1;
1939         if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
1940         {
1941                 if(Sbar_IsTeammatch())
1942                 {
1943                         // Layout:
1944                         //
1945                         //   team1 team3 team4
1946                         //
1947                         //         TEAM2
1948
1949                         scoreboard_t *teamcolorsort[16];
1950
1951                         Sbar_SortFrags();
1952                         for(i = 0; i < teamlines; ++i)
1953                                 teamcolorsort[i] = &(teams[i]);
1954
1955                         // Now sort them by color
1956                         qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
1957
1958                         // : margin
1959                         // -12*4: four digits space
1960                         place = (teamlines - 1) * (-12 * 4);
1961
1962                         for(i = 0; i < teamlines; ++i)
1963                         {
1964                                 int cindex = teamcolorsort[i]->colors & 15;
1965                                 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
1966                                 float cm = max(max(c[0], c[1]), c[2]);
1967                                 float cr = c[0] / cm;
1968                                 float cg = c[1] / cm;
1969                                 float cb = c[2] / cm;
1970                                 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
1971                                 {
1972                                         Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
1973                                 }
1974                                 else // other team
1975                                 {
1976                                         Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
1977                                         place += 4 * 12;
1978                                 }
1979                         }
1980                 }
1981                 else
1982                 {
1983                         // Layout:
1984                         //
1985                         //   leading  place
1986                         //
1987                         //        FRAGS
1988                         //
1989                         // find leading score other than ourselves, to calculate distribution
1990                         // find our place in the scoreboard
1991                         score = cl.scores[me].frags;
1992                         for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
1993                         {
1994                                 if (cl.scores[i].name[0] && i != me)
1995                                 {
1996                                         if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
1997                                                 otherleader = i;
1998                                         if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
1999                                                 place++;
2000                                 }
2001                         }
2002                         distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2003                         if (place == 1)
2004                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2005                         else if (place == 2)
2006                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2007                         else
2008                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2009                         if (otherleader < 0)
2010                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2011                         if (distribution >= 0)
2012                         {
2013                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2014                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2015                         }
2016                         else if (distribution >= -5)
2017                         {
2018                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2019                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 0, 1, 0);
2020                         }
2021                         else
2022                         {
2023                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2024                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 0, 0, 1, 0);
2025                         }
2026                 }
2027         }
2028
2029         if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2030         {
2031                 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2032                 minutes = (int)floor(timeleft / 60);
2033                 seconds = (int)(floor(timeleft) - minutes * 60);
2034                 if (minutes >= 5)
2035                 {
2036                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2037                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2038                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2039                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2040                 }
2041                 else if (minutes >= 1)
2042                 {
2043                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 0, 1, 0);
2044                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2045                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2046                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2047                 }
2048                 else if ((int)(timeleft * 4) & 1)
2049                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2050                 else
2051                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2052         }
2053         else if (sbar_gametime.integer)
2054         {
2055                 minutes = (int)floor(cl.time / 60);
2056                 seconds = (int)(floor(cl.time) - minutes * 60);
2057                 Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2058                 if(sb_colon && sb_colon->tex != r_texture_notexture)
2059                         DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2060                 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2061         }
2062
2063         sbar_x = sbar_x_save;
2064         sbar_y = sbar_y_save;
2065 }
2066
2067 /*
2068 ==================
2069 Sbar_IntermissionOverlay
2070
2071 ==================
2072 */
2073 void Sbar_IntermissionOverlay (void)
2074 {
2075         int             dig;
2076         int             num;
2077
2078         if (cl.gametype == GAME_DEATHMATCH)
2079         {
2080                 Sbar_DeathmatchOverlay ();
2081                 return;
2082         }
2083
2084         sbar_x = (vid_conwidth.integer - 320) >> 1;
2085         sbar_y = (vid_conheight.integer - 200) >> 1;
2086
2087         DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2088         DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2089
2090 // time
2091         dig = (int)cl.completed_time / 60;
2092         Sbar_DrawNum (160, 64, dig, 3, 0);
2093         num = (int)cl.completed_time - dig*60;
2094         Sbar_DrawPic (234,64,sb_colon);
2095         Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2096         Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2097
2098         Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2099         if (gamemode != GAME_NEXUIZ)
2100                 Sbar_DrawPic (232, 104, sb_slash);
2101         Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2102
2103         Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2104         if (gamemode != GAME_NEXUIZ)
2105                 Sbar_DrawPic (232, 144, sb_slash);
2106         Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2107
2108 }
2109
2110
2111 /*
2112 ==================
2113 Sbar_FinaleOverlay
2114
2115 ==================
2116 */
2117 void Sbar_FinaleOverlay (void)
2118 {
2119         DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2120 }
2121