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