fix bug in csqc getplayerkey reported by Blub
[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 int Sbar_GetSortedPlayerIndex (int index)
537 {
538         return index >= 0 && index < scoreboardlines ? fragsort[index] : -1;
539 }
540
541 static scoreboard_t teams[MAX_SCOREBOARD];
542 static int teamsort[MAX_SCOREBOARD];
543 static int teamlines;
544 void Sbar_SortFrags (void)
545 {
546         int i, j, k, color;
547
548         // sort by frags
549         scoreboardlines = 0;
550         for (i=0 ; i<cl.maxclients ; i++)
551         {
552                 if (cl.scores[i].name[0])
553                 {
554                         fragsort[scoreboardlines] = i;
555                         scoreboardlines++;
556                 }
557         }
558
559         for (i=0 ; i<scoreboardlines ; i++)
560                 for (j=0 ; j<scoreboardlines-1-i ; j++)
561                         if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags)
562                         {
563                                 k = fragsort[j];
564                                 fragsort[j] = fragsort[j+1];
565                                 fragsort[j+1] = k;
566                         }
567
568         teamlines = 0;
569         if (Sbar_IsTeammatch ())
570         {
571                 // now sort players by teams.
572                 for (i=0 ; i<scoreboardlines ; i++)
573                 {
574                         for (j=0 ; j<scoreboardlines-1-i ; j++)
575                         {
576                                 if (cl.scores[fragsort[j]].colors < cl.scores[fragsort[j+1]].colors)
577                                 {
578                                         k = fragsort[j];
579                                         fragsort[j] = fragsort[j+1];
580                                         fragsort[j+1] = k;
581                                 }
582                         }
583                 }
584
585                 // calculate team scores
586                 color = -1;
587                 for (i=0 ; i<scoreboardlines ; i++)
588                 {
589                         if (color != (cl.scores[fragsort[i]].colors & 15))
590                         {
591                                 const char* teamname;
592
593                                 color = cl.scores[fragsort[i]].colors & 15;
594                                 teamlines++;
595
596                                 switch (color)
597                                 {
598                                         case 4:
599                                                 teamname = "^1Red Team";
600                                                 break;
601                                         case 13:
602                                                 teamname = "^4Blue Team";
603                                                 break;
604                                         case 9:
605                                                 teamname = "^6Pink Team";
606                                                 break;
607                                         case 12:
608                                                 teamname = "^3Yellow Team";
609                                                 break;
610                                         default:
611                                                 teamname = "Total Team Score";
612                                                 break;
613                                 }
614                                 strlcpy(teams[teamlines-1].name, teamname, sizeof(teams[teamlines-1].name));
615
616                                 teams[teamlines-1].frags = 0;
617                                 teams[teamlines-1].colors = color + 16 * color;
618                         }
619
620                         if (cl.scores[fragsort[i]].frags != -666)
621                         {
622                                 // do not add spedcators
623                                 // (ugly hack for nexuiz)
624                                 teams[teamlines-1].frags += cl.scores[fragsort[i]].frags;
625                         }
626                 }
627
628                 // now sort teams by scores.
629                 for (i=0 ; i<teamlines ; i++)
630                         teamsort[i] = i;
631                 for (i=0 ; i<teamlines ; i++)
632                 {
633                         for (j=0 ; j<teamlines-1-i ; j++)
634                         {
635                                 if (teams[teamsort[j]].frags < teams[teamsort[j+1]].frags)
636                                 {
637                                         k = teamsort[j];
638                                         teamsort[j] = teamsort[j+1];
639                                         teamsort[j+1] = k;
640                                 }
641                         }
642                 }
643         }
644 }
645
646 /*
647 ===============
648 Sbar_SoloScoreboard
649 ===============
650 */
651 void Sbar_SoloScoreboard (void)
652 {
653 #if 1
654         char    str[80], timestr[40];
655         int             max, timelen;
656         int             minutes, seconds;
657         double  t;
658
659         t = (cl.intermission ? cl.completed_time : cl.time);
660         minutes = (int)(t / 60);
661         seconds = (int)(t - 60*floor(t/60));
662
663         // monsters and secrets are now both on the top row
664         if (cl.stats[STAT_TOTALMONSTERS])
665                 Sbar_DrawString(8, 4, va("Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]));
666         if (cl.stats[STAT_TOTALSECRETS])
667                 Sbar_DrawString(8+22*8, 4, va("Secrets:%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]));
668
669         // figure out the map's filename without path or extension
670         strlcpy(str, FS_FileWithoutPath(cl.worldmodel ? cl.worldmodel->name : ""), sizeof(str));
671         if (strrchr(str, '.'))
672                 *(strrchr(str, '.')) = 0;
673
674         // append a : separator and then the full title
675         strlcat(str, ":", sizeof(str));
676         strlcat(str, cl.levelname, sizeof(str));
677
678         // if there's a newline character, terminate the string there
679         if (strchr(str, '\n'))
680                 *(strchr(str, '\n')) = 0;
681
682         // make the time string
683         timelen = sprintf(timestr, " %i:%02i", minutes, seconds);
684
685         // truncate the level name if necessary to make room for time
686         max = 38 - timelen;
687         if ((int)strlen(str) > max)
688                 str[max] = 0;
689
690         // print the filename and message
691         Sbar_DrawString(8, 12, str);
692
693         // print the time
694         Sbar_DrawString(8 + max*8, 12, timestr);
695
696 #else
697         char    str[80];
698         int             minutes, seconds, tens, units;
699         int             l;
700
701         if (gamemode != GAME_NEXUIZ) {
702                 sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
703                 Sbar_DrawString (8, 4, str);
704
705                 sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]);
706                 Sbar_DrawString (8, 12, str);
707         }
708
709 // time
710         minutes = (int)(cl.time / 60);
711         seconds = (int)(cl.time - 60*minutes);
712         tens = seconds / 10;
713         units = seconds - 10*tens;
714         sprintf (str,"Time :%3i:%i%i", minutes, tens, units);
715         Sbar_DrawString (184, 4, str);
716
717 // draw level name
718         if (gamemode == GAME_NEXUIZ) {
719                 l = (int) strlen (cl.worldmodel->name);
720                 Sbar_DrawString (232 - l*4, 12, cl.worldmodel->name);
721         } else {
722                 l = (int) strlen (cl.levelname);
723                 Sbar_DrawString (232 - l*4, 12, cl.levelname);
724         }
725 #endif
726 }
727
728 /*
729 ===============
730 Sbar_DrawScoreboard
731 ===============
732 */
733 void Sbar_DrawScoreboard (void)
734 {
735         Sbar_SoloScoreboard ();
736         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
737         //if (cl.gametype == GAME_DEATHMATCH)
738         if (!cl.islocalgame)
739                 Sbar_DeathmatchOverlay ();
740 }
741
742 //=============================================================================
743
744 // AK to make DrawInventory smaller
745 static void Sbar_DrawWeapon(int nr, float fade, int active)
746 {
747         if (sbar_hudselector.integer == 1)
748         {
749                 // width = 300, height = 100
750                 const int w_width = 32, w_height = 12, w_space = 2, font_size = 8;
751
752                 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);
753                 // FIXME ??
754                 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);
755         }
756         else
757         {
758                 // width = 300, height = 100
759                 const int w_width = 300, w_height = 100, w_space = 10;
760                 const float w_scale = 0.4;
761
762                 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);
763                 //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);
764         }
765 }
766
767 /*
768 ===============
769 Sbar_DrawInventory
770 ===============
771 */
772 void Sbar_DrawInventory (void)
773 {
774         int i;
775         char num[6];
776         float time;
777         int flashon;
778
779         if (gamemode == GAME_ROGUE)
780         {
781                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
782                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[0], sbar_alpha_bg.value);
783                 else
784                         Sbar_DrawAlphaPic (0, -24, rsb_invbar[1], sbar_alpha_bg.value);
785         }
786         else
787                 Sbar_DrawAlphaPic (0, -24, sb_ibar, sbar_alpha_bg.value);
788
789         // weapons
790         for (i=0 ; i<7 ; i++)
791         {
792                 if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )
793                 {
794                         time = cl.item_gettime[i];
795                         flashon = (int)(max(0, cl.time - time)*10);
796                         if (flashon >= 10)
797                         {
798                                 if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )
799                                         flashon = 1;
800                                 else
801                                         flashon = 0;
802                         }
803                         else
804                                 flashon = (flashon%5) + 2;
805
806                         Sbar_DrawAlphaPic (i*24, -16, sb_weapons[flashon][i], sbar_alpha_bg.value);
807                 }
808         }
809
810         // MED 01/04/97
811         // hipnotic weapons
812         if (gamemode == GAME_HIPNOTIC)
813         {
814                 int grenadeflashing=0;
815                 for (i=0 ; i<4 ; i++)
816                 {
817                         if (cl.stats[STAT_ITEMS] & (1<<hipweapons[i]) )
818                         {
819                                 time = max(0, cl.item_gettime[hipweapons[i]]);
820                                 flashon = (int)((cl.time - time)*10);
821                                 if (flashon >= 10)
822                                 {
823                                         if ( cl.stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i])  )
824                                                 flashon = 1;
825                                         else
826                                                 flashon = 0;
827                                 }
828                                 else
829                                         flashon = (flashon%5) + 2;
830
831                                 // check grenade launcher
832                                 if (i==2)
833                                 {
834                                         if (cl.stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)
835                                         {
836                                                 if (flashon)
837                                                 {
838                                                         grenadeflashing = 1;
839                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][2]);
840                                                 }
841                                         }
842                                 }
843                                 else if (i==3)
844                                 {
845                                         if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN<<4))
846                                         {
847                                                 if (!grenadeflashing)
848                                                         Sbar_DrawPic (96, -16, hsb_weapons[flashon][3]);
849                                         }
850                                         else
851                                                 Sbar_DrawPic (96, -16, hsb_weapons[flashon][4]);
852                                 }
853                                 else
854                                         Sbar_DrawPic (176 + (i*24), -16, hsb_weapons[flashon][i]);
855                         }
856                 }
857         }
858
859         if (gamemode == GAME_ROGUE)
860         {
861                 // check for powered up weapon.
862                 if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )
863                         for (i=0;i<5;i++)
864                                 if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))
865                                         Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]);
866         }
867
868         // ammo counts
869         for (i=0 ; i<4 ; i++)
870         {
871                 sprintf (num, "%4i",cl.stats[STAT_SHELLS+i] );
872                 if (num[0] != ' ')
873                         Sbar_DrawCharacter ( (6*i+0)*8 - 2, -24, 18 + num[0] - '0');
874                 if (num[1] != ' ')
875                         Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[1] - '0');
876                 if (num[2] != ' ')
877                         Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[2] - '0');
878                 if (num[3] != ' ')
879                         Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[3] - '0');
880         }
881
882         // items
883         for (i=0 ; i<6 ; i++)
884                 if (cl.stats[STAT_ITEMS] & (1<<(17+i)))
885                 {
886                         //MED 01/04/97 changed keys
887                         if (gamemode != GAME_HIPNOTIC || (i>1))
888                                 Sbar_DrawPic (192 + i*16, -16, sb_items[i]);
889                 }
890
891         //MED 01/04/97 added hipnotic items
892         // hipnotic items
893         if (gamemode == GAME_HIPNOTIC)
894         {
895                 for (i=0 ; i<2 ; i++)
896                         if (cl.stats[STAT_ITEMS] & (1<<(24+i)))
897                                 Sbar_DrawPic (288 + i*16, -16, hsb_items[i]);
898         }
899
900         if (gamemode == GAME_ROGUE)
901         {
902                 // new rogue items
903                 for (i=0 ; i<2 ; i++)
904                         if (cl.stats[STAT_ITEMS] & (1<<(29+i)))
905                                 Sbar_DrawPic (288 + i*16, -16, rsb_items[i]);
906         }
907         else
908         {
909                 // sigils
910                 for (i=0 ; i<4 ; i++)
911                         if (cl.stats[STAT_ITEMS] & (1<<(28+i)))
912                                 Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]);
913         }
914 }
915
916 //=============================================================================
917
918 /*
919 ===============
920 Sbar_DrawFrags
921 ===============
922 */
923 void Sbar_DrawFrags (void)
924 {
925         int i, k, l, x, f;
926         char num[12];
927         scoreboard_t *s;
928         unsigned char *c;
929
930         Sbar_SortFrags ();
931
932         // draw the text
933         l = min(scoreboardlines, 4);
934
935         x = 23 * 8;
936
937         for (i = 0;i < l;i++)
938         {
939                 k = fragsort[i];
940                 s = &cl.scores[k];
941
942                 // draw background
943                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
944                 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);
945                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
946                 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);
947
948                 // draw number
949                 f = s->frags;
950                 sprintf (num, "%3i",f);
951
952                 if (k == cl.viewentity - 1)
953                 {
954                         Sbar_DrawCharacter ( x      + 2, -24, 16);
955                         Sbar_DrawCharacter ( x + 32 - 4, -24, 17);
956                 }
957                 Sbar_DrawCharacter (x +  8, -24, num[0]);
958                 Sbar_DrawCharacter (x + 16, -24, num[1]);
959                 Sbar_DrawCharacter (x + 24, -24, num[2]);
960                 x += 32;
961         }
962 }
963
964 //=============================================================================
965
966
967 /*
968 ===============
969 Sbar_DrawFace
970 ===============
971 */
972 void Sbar_DrawFace (void)
973 {
974         int f;
975
976 // PGM 01/19/97 - team color drawing
977 // PGM 03/02/97 - fixed so color swatch only appears in CTF modes
978         if (gamemode == GAME_ROGUE && !cl.islocalgame && (teamplay.integer > 3) && (teamplay.integer < 7))
979         {
980                 char num[12];
981                 scoreboard_t *s;
982                 unsigned char *c;
983
984                 s = &cl.scores[cl.viewentity - 1];
985                 // draw background
986                 Sbar_DrawPic (112, 0, rsb_teambord);
987                 c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
988                 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);
989                 c = palette_rgb_shirtscoreboard[s->colors & 0xf];
990                 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);
991
992                 // draw number
993                 f = s->frags;
994                 sprintf (num, "%3i",f);
995
996                 if ((s->colors & 0xf0)==0)
997                 {
998                         if (num[0] != ' ')
999                                 Sbar_DrawCharacter(109, 3, 18 + num[0] - '0');
1000                         if (num[1] != ' ')
1001                                 Sbar_DrawCharacter(116, 3, 18 + num[1] - '0');
1002                         if (num[2] != ' ')
1003                                 Sbar_DrawCharacter(123, 3, 18 + num[2] - '0');
1004                 }
1005                 else
1006                 {
1007                         Sbar_DrawCharacter ( 109, 3, num[0]);
1008                         Sbar_DrawCharacter ( 116, 3, num[1]);
1009                         Sbar_DrawCharacter ( 123, 3, num[2]);
1010                 }
1011
1012                 return;
1013         }
1014 // PGM 01/19/97 - team color drawing
1015
1016         if ( (cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) )
1017                 Sbar_DrawPic (112, 0, sb_face_invis_invuln);
1018         else if (cl.stats[STAT_ITEMS] & IT_QUAD)
1019                 Sbar_DrawPic (112, 0, sb_face_quad );
1020         else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
1021                 Sbar_DrawPic (112, 0, sb_face_invis );
1022         else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1023                 Sbar_DrawPic (112, 0, sb_face_invuln);
1024         else
1025         {
1026                 f = cl.stats[STAT_HEALTH] / 20;
1027                 f = bound(0, f, 4);
1028                 Sbar_DrawPic (112, 0, sb_faces[f][cl.time <= cl.faceanimtime]);
1029         }
1030 }
1031
1032 void Sbar_ShowFPS(void)
1033 {
1034         float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1035         char fpsstring[32];
1036         char timestring[32];
1037         char datestring[32];
1038         char speedstring[32];
1039         qboolean red = false;
1040         fpsstring[0] = 0;
1041         timestring[0] = 0;
1042         datestring[0] = 0;
1043         speedstring[0] = 0;
1044         if (showfps.integer)
1045         {
1046                 float calc;
1047                 static double nexttime = 0, lasttime = 0;
1048                 static double framerate = 0;
1049                 static int framecount = 0;
1050                 double interval = 0.25;
1051                 double newtime;
1052                 newtime = realtime;
1053                 if (newtime >= nexttime)
1054                 {
1055                         framerate = framecount / (newtime - lasttime);
1056                         if (nexttime < newtime - interval * 1.5)
1057                                 nexttime = newtime;
1058                         lasttime = newtime;
1059                         nexttime += interval;
1060                         framecount = 0;
1061                 }
1062                 framecount++;
1063                 calc = framerate;
1064
1065                 if ((red = (calc < 1.0f)))
1066                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0f / calc + 0.5));
1067                 else
1068                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(calc + 0.5));
1069         }
1070         if (showtime.integer)
1071                 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1072         if (showdate.integer)
1073                 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1074         if (showspeed.integer)
1075         {
1076                 double speed, speedxy, f;
1077                 const char *unit;
1078                 speed = VectorLength(cl.movement_velocity);
1079                 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1080                 switch(showspeed.integer)
1081                 {
1082                         default:
1083                         case 1:
1084                                 if(gamemode == GAME_NEXUIZ)
1085                                         unit = "in/s";
1086                                 else
1087                                         unit = "qu/s";
1088                                 f = 1.0;
1089                                 break;
1090                         case 2:
1091                                 unit = "m/s";
1092                                 f = 0.0254;
1093                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1094                                 // 1qu=1.5in is for non-Nexuiz only - Nexuiz players are overly large, but 1qu=1in fixes that
1095                                 break;
1096                         case 3:
1097                                 unit = "km/h";
1098                                 f = 0.0254 * 3.6;
1099                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1100                                 break;
1101                         case 4:
1102                                 unit = "mph";
1103                                 f = 0.0254 * 3.6 * 0.6213711922;
1104                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1105                                 break;
1106                         case 5:
1107                                 unit = "knots";
1108                                 f = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h
1109                                 if(gamemode != GAME_NEXUIZ) f *= 1.5;
1110                                 break;
1111                 }
1112                 dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1113         }
1114         if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0])
1115         {
1116                 fps_scalex = 12;
1117                 fps_scaley = 12;
1118                 fps_height = fps_scaley * ((fpsstring[0] != 0) + (timestring[0] != 0) + (datestring[0] != 0) + (speedstring[0] != 0));
1119                 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1120                 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1121                 fps_y = vid_conheight.integer - fps_height;
1122                 if (fpsstring[0])
1123                 {
1124                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1125                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1126                         if (red)
1127                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1128                         else
1129                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1130                         fps_y += fps_scaley;
1131                 }
1132                 if (timestring[0])
1133                 {
1134                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1135                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1136                         DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1137                         fps_y += fps_scaley;
1138                 }
1139                 if (datestring[0])
1140                 {
1141                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1142                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1143                         DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1144                         fps_y += fps_scaley;
1145                 }
1146                 if (speedstring[0])
1147                 {
1148                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1149                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1150                         DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1151                         fps_y += fps_scaley;
1152                 }
1153         }
1154 }
1155
1156 void Sbar_DrawGauge(float x, float y, cachepic_t *pic, float width, float height, float rangey, float rangeheight, float c1, float c2, float c1r, float c1g, float c1b, float c1a, float c2r, float c2g, float c2b, float c2a, float c3r, float c3g, float c3b, float c3a, int drawflags)
1157 {
1158         float r[5];
1159         c2 = bound(0, c2, 1);
1160         c1 = bound(0, c1, 1 - c2);
1161         r[0] = 0;
1162         r[1] = rangey + rangeheight * (c2 + c1);
1163         r[2] = rangey + rangeheight * (c2);
1164         r[3] = rangey;
1165         r[4] = height;
1166         if (r[1] > r[0])
1167                 DrawQ_SuperPic(x, y + r[0], pic, width, (r[1] - r[0]), 0,(r[0] / height), c3r,c3g,c3b,c3a, 1,(r[0] / height), c3r,c3g,c3b,c3a, 0,(r[1] / height), c3r,c3g,c3b,c3a, 1,(r[1] / height), c3r,c3g,c3b,c3a, drawflags);
1168         if (r[2] > r[1])
1169                 DrawQ_SuperPic(x, y + r[1], pic, width, (r[2] - r[1]), 0,(r[1] / height), c1r,c1g,c1b,c1a, 1,(r[1] / height), c1r,c1g,c1b,c1a, 0,(r[2] / height), c1r,c1g,c1b,c1a, 1,(r[2] / height), c1r,c1g,c1b,c1a, drawflags);
1170         if (r[3] > r[2])
1171                 DrawQ_SuperPic(x, y + r[2], pic, width, (r[3] - r[2]), 0,(r[2] / height), c2r,c2g,c2b,c2a, 1,(r[2] / height), c2r,c2g,c2b,c2a, 0,(r[3] / height), c2r,c2g,c2b,c2a, 1,(r[3] / height), c2r,c2g,c2b,c2a, drawflags);
1172         if (r[4] > r[3])
1173                 DrawQ_SuperPic(x, y + r[3], pic, width, (r[4] - r[3]), 0,(r[3] / height), c3r,c3g,c3b,c3a, 1,(r[3] / height), c3r,c3g,c3b,c3a, 0,(r[4] / height), c3r,c3g,c3b,c3a, 1,(r[4] / height), c3r,c3g,c3b,c3a, drawflags);
1174 }
1175
1176 /*
1177 ===============
1178 Sbar_Draw
1179 ===============
1180 */
1181 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1182 extern cvar_t v_kicktime;
1183 void Sbar_Score (int margin);
1184 void Sbar_Draw (void)
1185 {
1186         cachepic_t *pic;
1187
1188         if(cl.csqc_vidvars.drawenginesbar)      //[515]: csqc drawsbar
1189         {
1190                 if (sb_showscores)
1191                         Sbar_DrawScoreboard ();
1192                 else if (cl.intermission == 1)
1193                 {
1194                         if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1195                         {
1196                                 Sbar_DrawScoreboard();
1197                                 return;
1198                         }
1199                         Sbar_IntermissionOverlay();
1200                 }
1201                 else if (cl.intermission == 2)
1202                         Sbar_FinaleOverlay();
1203                 else if (gamemode == GAME_DELUXEQUAKE)
1204                 {
1205                 }
1206                 else if (gamemode == GAME_SOM)
1207                 {
1208                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1209                                 Sbar_DrawScoreboard ();
1210                         else if (sb_lines)
1211                         {
1212                                 // this is the top left of the sbar area
1213                                 sbar_x = 0;
1214                                 sbar_y = vid_conheight.integer - 24*3;
1215
1216                                 // armor
1217                                 if (cl.stats[STAT_ARMOR])
1218                                 {
1219                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1220                                                 Sbar_DrawPic(0, 0, somsb_armor[2]);
1221                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1222                                                 Sbar_DrawPic(0, 0, somsb_armor[1]);
1223                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1224                                                 Sbar_DrawPic(0, 0, somsb_armor[0]);
1225                                         Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1226                                 }
1227
1228                                 // health
1229                                 Sbar_DrawPic(0, 24, somsb_health);
1230                                 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1231
1232                                 // ammo icon
1233                                 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1234                                         Sbar_DrawPic(0, 48, somsb_ammo[0]);
1235                                 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1236                                         Sbar_DrawPic(0, 48, somsb_ammo[1]);
1237                                 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1238                                         Sbar_DrawPic(0, 48, somsb_ammo[2]);
1239                                 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1240                                         Sbar_DrawPic(0, 48, somsb_ammo[3]);
1241                                 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1242                                 if (cl.stats[STAT_SHELLS])
1243                                         Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1244                         }
1245                 }
1246                 else if (gamemode == GAME_NEXUIZ)
1247                 {
1248                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1249                         {
1250                                 sbar_x = (vid_conwidth.integer - 640)/2;
1251                                 sbar_y = vid_conheight.integer - 47;
1252                                 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1253                                 Sbar_DrawScoreboard ();
1254                         }
1255                         else if (sb_lines && sbar_hudselector.integer == 1)
1256                         {
1257                                 int i;
1258                                 float fade;
1259                                 int redflag, blueflag;
1260                                 float x;
1261
1262                                 sbar_x = (vid_conwidth.integer - 320)/2;
1263                                 sbar_y = vid_conheight.integer - 24 - 16;
1264
1265                                 // calculate intensity to draw weapons bar at
1266                                 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1267                                 fade = bound(0.7, fade, 1);
1268                                 for (i = 0; i < 8;i++)
1269                                         if (cl.stats[STAT_ITEMS] & (1 << i))
1270                                                 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1271                                 if((cl.stats[STAT_ITEMS] & (1<<12)))
1272                                         Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1273
1274                                 // flag icons
1275                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1276                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1277                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1278                                 if (redflag == 3 && blueflag == 3)
1279                                 {
1280                                         // The Impossible Combination[tm]
1281                                         // Can only happen in Key Hunt mode...
1282                                         Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128), sb_items[14]);
1283                                 }
1284                                 else
1285                                 {
1286                                         if (redflag)
1287                                                 Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64), sb_items[redflag+10]);
1288                                         if (blueflag)
1289                                                 Sbar_DrawPic (x, (vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128), sb_items[blueflag+14]);
1290                                 }
1291
1292                                 // armor
1293                                 if (cl.stats[STAT_ARMOR] > 0)
1294                                 {
1295                                         Sbar_DrawStretchPic (0, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1296                                         if(cl.stats[STAT_ARMOR] > 200)
1297                                                 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1298                                         else if(cl.stats[STAT_ARMOR] > 100)
1299                                                 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1300                                         else if(cl.stats[STAT_ARMOR] > 50)
1301                                                 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1302                                         else if(cl.stats[STAT_ARMOR] > 25)
1303                                                 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1304                                         else
1305                                                 Sbar_DrawXNum(24,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1306                                 }
1307
1308                                 // health
1309                                 if (cl.stats[STAT_HEALTH] != 0)
1310                                 {
1311                                         Sbar_DrawStretchPic (112, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1312                                         if(cl.stats[STAT_HEALTH] > 200)
1313                                                 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1314                                         else if(cl.stats[STAT_HEALTH] > 100)
1315                                                 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1316                                         else if(cl.stats[STAT_HEALTH] > 50)
1317                                                 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1318                                         else if(cl.stats[STAT_HEALTH] > 25)
1319                                                 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1320                                         else
1321                                                 Sbar_DrawXNum(136,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1322                                 }
1323
1324                                 // ammo
1325                                 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1326                                 {
1327                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1328                                                 Sbar_DrawStretchPic (224, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1329                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1330                                                 Sbar_DrawStretchPic (224, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1331                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1332                                                 Sbar_DrawStretchPic (224, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1333                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1334                                                 Sbar_DrawStretchPic (224, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1335                                         if(cl.stats[STAT_AMMO] > 10)
1336                                                 Sbar_DrawXNum(248, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1337                                         else
1338                                                 Sbar_DrawXNum(248, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1339                                 }
1340
1341                                 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1342                                         Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1343                                 if (sbar_x > 0)
1344                                         Sbar_Score(16);
1345                                         // The margin can be at most 8 to support 640x480 console size:
1346                                         //   320 + 2 * (144 + 16) = 640
1347                         }
1348                         else if (sb_lines)
1349                         {
1350                                 int i;
1351                                 float fade;
1352                                 int redflag, blueflag;
1353                                 float x;
1354
1355                                 sbar_x = (vid_conwidth.integer - 640)/2;
1356                                 sbar_y = vid_conheight.integer - 47;
1357
1358                                 // calculate intensity to draw weapons bar at
1359                                 fade = 3 - 2 * (cl.time - cl.weapontime);
1360                                 if (fade > 0)
1361                                 {
1362                                         fade = min(fade, 1);
1363                                         for (i = 0; i < 8;i++)
1364                                                 if (cl.stats[STAT_ITEMS] & (1 << i))
1365                                                         Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1366
1367                                         if((cl.stats[STAT_ITEMS] & (1<<12)))
1368                                                 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1369                                 }
1370
1371                                 //if (!cl.islocalgame)
1372                                 //      Sbar_DrawFrags ();
1373
1374                                 if (sb_lines > 24)
1375                                         Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1376                                 else
1377                                         Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1378
1379                                 // flag icons
1380                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1381                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1382                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1383                                 if (redflag == 3 && blueflag == 3)
1384                                 {
1385                                         // The Impossible Combination[tm]
1386                                         // Can only happen in Key Hunt mode...
1387                                         Sbar_DrawPic (x, -179, sb_items[14]);
1388                                 }
1389                                 else
1390                                 {
1391                                         if (redflag)
1392                                                 Sbar_DrawPic (x, -117, sb_items[redflag+10]);
1393                                         if (blueflag)
1394                                                 Sbar_DrawPic (x, -177, sb_items[blueflag+14]);
1395                                 }
1396
1397                                 // armor
1398                                 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1399
1400                                 // health
1401                                 if(cl.stats[STAT_HEALTH] > 100)
1402                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1403                                 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1404                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1405                                 else
1406                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1407
1408                                 // AK dont draw ammo for the laser
1409                                 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1410                                 {
1411                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1412                                                 Sbar_DrawPic (519, 0, sb_ammo[0]);
1413                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1414                                                 Sbar_DrawPic (519, 0, sb_ammo[1]);
1415                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1416                                                 Sbar_DrawPic (519, 0, sb_ammo[2]);
1417                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1418                                                 Sbar_DrawPic (519, 0, sb_ammo[3]);
1419
1420                                         if(cl.stats[STAT_AMMO] <= 10)
1421                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1422                                         else
1423                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1424
1425                                 }
1426
1427                                 if (sb_lines > 24)
1428                                         DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1429
1430                                 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1431                                         Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1432
1433                                 if (sbar_x > 0)
1434                                         Sbar_Score(-16);
1435                                         // Because:
1436                                         //   Mini scoreboard uses 12*4 per other team, that is, 144
1437                                         //   pixels when there are four teams...
1438                                         //   Nexuiz by default sets vid_conwidth to 800... makes
1439                                         //   sbar_x == 80...
1440                                         //   so we need to shift it by 64 pixels to the right to fit
1441                                         //   BUT: then it overlaps with the image that gets drawn
1442                                         //   for viewsize 100! Therefore, just account for 3 teams,
1443                                         //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1444                                         //   to the right!
1445                         }
1446                 }
1447                 else if (gamemode == GAME_ZYMOTIC)
1448                 {
1449 #if 1
1450                         float scale = 64.0f / 256.0f;
1451                         float kickoffset[3];
1452                         VectorClear(kickoffset);
1453                         if (v_dmg_time > 0)
1454                         {
1455                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1456                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1457                         }
1458                         sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1459                         sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1460                         // left1 16, 48 : 126 -66
1461                         // left2 16, 128 : 196 -66
1462                         // right 176, 48 : 196 -136
1463                         Sbar_DrawGauge(sbar_x +  16 * scale, sbar_y +  48 * scale, zymsb_crosshair_left1, 64*scale,  80*scale, 78*scale,  -66*scale, cl.stats[STAT_AMMO]  * (1.0 / 200.0), cl.stats[STAT_SHELLS]  * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1464                         Sbar_DrawGauge(sbar_x +  16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2, 64*scale,  80*scale, 68*scale,  -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1465                         Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y +  48 * scale, zymsb_crosshair_right, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR]  * (1.0 / 300.0), cl.stats[STAT_HEALTH]  * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL);
1466                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1467 #else
1468                         float scale = 128.0f / 256.0f;
1469                         float healthstart, healthheight, healthstarttc, healthendtc;
1470                         float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1471                         float ammostart, ammoheight, ammostarttc, ammoendtc;
1472                         float clipstart, clipheight, clipstarttc, clipendtc;
1473                         float kickoffset[3], offset;
1474                         VectorClear(kickoffset);
1475                         if (v_dmg_time > 0)
1476                         {
1477                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1478                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1479                         }
1480                         sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1481                         sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1482                         offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1483                         DrawQ_SuperPic(sbar_x +  120           * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0);
1484                         DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120            * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0);
1485                         DrawQ_SuperPic(sbar_x +  120           * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0);
1486                         DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120            * scale, zymsb_crosshair_line, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0);
1487                         healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1488                         shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1489                         healthstart = 204 - healthheight;
1490                         shieldstart = healthstart - shieldheight;
1491                         healthstarttc = healthstart * (1.0f / 256.0f);
1492                         healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1493                         shieldstarttc = shieldstart * (1.0f / 256.0f);
1494                         shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1495                         ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1496                         ammostart = 114 - ammoheight;
1497                         ammostarttc = ammostart * (1.0f / 256.0f);
1498                         ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1499                         clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1500                         clipstart = 190 - clipheight;
1501                         clipstarttc = clipstart * (1.0f / 256.0f);
1502                         clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1503                         if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1504                         if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL);
1505                         if (ammoheight > 0)   DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart   * scale, zymsb_crosshair_ammo,   256 * scale, ammoheight   * scale, 0,ammostarttc,   0.8f,0.8f,0.0f,1.0f, 1,ammostarttc,   0.8f,0.8f,0.0f,1.0f, 0,ammoendtc,   0.8f,0.8f,0.0f,1.0f, 1,ammoendtc,   0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL);
1506                         if (clipheight > 0)   DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart   * scale, zymsb_crosshair_clip,   256 * scale, clipheight   * scale, 0,clipstarttc,   1.0f,1.0f,0.0f,1.0f, 1,clipstarttc,   1.0f,1.0f,0.0f,1.0f, 0,clipendtc,   1.0f,1.0f,0.0f,1.0f, 1,clipendtc,   1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL);
1507                         DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1508                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1509 #endif
1510                 }
1511                 else // Quake and others
1512                 {
1513                         sbar_x = (vid_conwidth.integer - 320)/2;
1514                         sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1515                         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1516                         //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1517
1518                         if (sb_lines > 24)
1519                         {
1520                                 if (gamemode != GAME_GOODVSBAD2)
1521                                         Sbar_DrawInventory ();
1522                                 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1523                                         Sbar_DrawFrags ();
1524                         }
1525
1526                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1527                         {
1528                                 if (gamemode != GAME_GOODVSBAD2)
1529                                         Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1530                                 Sbar_DrawScoreboard ();
1531                         }
1532                         else if (sb_lines)
1533                         {
1534                                 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1535
1536                                 // keys (hipnotic only)
1537                                 //MED 01/04/97 moved keys here so they would not be overwritten
1538                                 if (gamemode == GAME_HIPNOTIC)
1539                                 {
1540                                         if (cl.stats[STAT_ITEMS] & IT_KEY1)
1541                                                 Sbar_DrawPic (209, 3, sb_items[0]);
1542                                         if (cl.stats[STAT_ITEMS] & IT_KEY2)
1543                                                 Sbar_DrawPic (209, 12, sb_items[1]);
1544                                 }
1545                                 // armor
1546                                 if (gamemode != GAME_GOODVSBAD2)
1547                                 {
1548                                         if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1549                                         {
1550                                                 Sbar_DrawNum (24, 0, 666, 3, 1);
1551                                                 Sbar_DrawPic (0, 0, sb_disc);
1552                                         }
1553                                         else
1554                                         {
1555                                                 if (gamemode == GAME_ROGUE)
1556                                                 {
1557                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1558                                                         if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1559                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1560                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1561                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1562                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1563                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1564                                                 }
1565                                                 else
1566                                                 {
1567                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1568                                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1569                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1570                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1571                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1572                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1573                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1574                                                 }
1575                                         }
1576                                 }
1577
1578                                 // face
1579                                 Sbar_DrawFace ();
1580
1581                                 // health
1582                                 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1583
1584                                 // ammo icon
1585                                 if (gamemode == GAME_ROGUE)
1586                                 {
1587                                         if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1588                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1589                                         else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1590                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1591                                         else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1592                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1593                                         else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1594                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1595                                         else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1596                                                 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1597                                         else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1598                                                 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1599                                         else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1600                                                 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1601                                 }
1602                                 else
1603                                 {
1604                                         if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1605                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1606                                         else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1607                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1608                                         else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1609                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1610                                         else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1611                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1612                                 }
1613
1614                                 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1615
1616                                 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1617                                 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1618                                 {
1619                                         if (gamemode == GAME_TRANSFUSION)
1620                                                 Sbar_MiniDeathmatchOverlay (0, 0);
1621                                         else
1622                                                 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1623                                         Sbar_Score(24);
1624                                 }
1625                         }
1626                 }
1627         }
1628
1629         Sbar_ShowFPS();
1630
1631         if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1632         {
1633                 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1634                 DrawQ_Pic((vid_conwidth.integer - pic->width * crosshair_size.value) * 0.5f, (vid_conheight.integer - pic->height * crosshair_size.value) * 0.5f, pic, pic->width * crosshair_size.value, pic->height * crosshair_size.value, crosshair_color_red.value, crosshair_color_green.value, crosshair_color_blue.value, crosshair_color_alpha.value, 0);
1635         }
1636
1637         if (cl_prydoncursor.integer)
1638                 DrawQ_Pic((cl.cmd.cursor_screen[0] + 1) * 0.5 * vid_conwidth.integer, (cl.cmd.cursor_screen[1] + 1) * 0.5 * vid_conheight.integer, Draw_CachePic (va("gfx/prydoncursor%03i", cl_prydoncursor.integer)), 0, 0, 1, 1, 1, 1, 0);
1639 }
1640
1641 //=============================================================================
1642
1643 /*
1644 ==================
1645 Sbar_DeathmatchOverlay
1646
1647 ==================
1648 */
1649 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1650 {
1651         int minutes;
1652         qboolean myself = false;
1653         unsigned char *c;
1654         minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1655
1656         if((s - cl.scores) == cl.playerentity - 1)
1657                 myself = true;
1658         if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1659                 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1660                         myself = true;
1661
1662         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1663         {
1664                 if (s->qw_spectator)
1665                 {
1666                         if (s->qw_ping || s->qw_packetloss)
1667                                 DrawQ_String_Font(x, y, va("%4i %3i %4i spectator  %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1668                         else
1669                                 DrawQ_String_Font(x, y, va("         %4i spectator  %c%s", minutes, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1670                 }
1671                 else
1672                 {
1673                         // draw colors behind score
1674                         //
1675                         //
1676                         //
1677                         //
1678                         //
1679                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1680                         DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1681                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1682                         DrawQ_Fill(x + 14*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1683                         // print the text
1684                         //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1685                         if (s->qw_ping || s->qw_packetloss)
1686                                 DrawQ_String_Font(x, y, va("%4i %3i %4i %5i %-4s %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1687                         else
1688                                 DrawQ_String_Font(x, y, va("         %4i %5i %-4s %c%s", minutes,(int) s->frags, cl.qw_teamplay ? s->qw_team : "", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1689                 }
1690         }
1691         else
1692         {
1693                 if (s->qw_spectator)
1694                 {
1695                         if (s->qw_ping || s->qw_packetloss)
1696                                 DrawQ_String_Font(x, y, va("%4i %3i spect %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1697                         else
1698                                 DrawQ_String_Font(x, y, va("         spect %c%s", myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1699                 }
1700                 else
1701                 {
1702                         // draw colors behind score
1703                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1704                         DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+1, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1705                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1706                         DrawQ_Fill(x + 9*8*FONT_SBAR->width_of[0], y+4, 40*FONT_SBAR->width_of[0], 3, c[0] * (1.0f / 255.0f), c[1] * (1.0f / 255.0f), c[2] * (1.0f / 255.0f), sbar_alpha_fg.value, 0);
1707                         // print the text
1708                         //DrawQ_String(x, y, va("%c%4i %s", myself ? 13 : ' ', (int) s->frags, s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, true);
1709                         if (s->qw_ping || s->qw_packetloss)
1710                                 DrawQ_String_Font(x, y, va("%4i %3i %5i %c%s", bound(0, s->qw_ping, 9999), bound(0, s->qw_packetloss, 99), (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1711                         else
1712                                 DrawQ_String_Font(x, y, va("         %5i %c%s", (int) s->frags, myself ? 13 : ' ', s->name), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1713                 }
1714         }
1715         return 8;
1716 }
1717
1718 void Sbar_DeathmatchOverlay (void)
1719 {
1720         int i, y, xmin, xmax, ymin, ymax;
1721
1722         // request new ping times every two second
1723         if (cl.last_ping_request < realtime - 2 && cls.netcon)
1724         {
1725                 cl.last_ping_request = realtime;
1726                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1727                 {
1728                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1729                         MSG_WriteString(&cls.netcon->message, "pings");
1730                 }
1731                 else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5 || cls.protocol == PROTOCOL_DARKPLACES6/* || cls.protocol == PROTOCOL_DARKPLACES7*/)
1732                 {
1733                         // these servers usually lack the pings command and so a less efficient "ping" command must be sent, which on modern DP servers will also reply with a pingplreport command after the ping listing
1734                         static int ping_anyway_counter = 0;
1735                         if(cl.parsingtextexpectingpingforscores == 1)
1736                         {
1737                                 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1738                                 if(++ping_anyway_counter >= 5)
1739                                         cl.parsingtextexpectingpingforscores = 0;
1740                         }
1741                         if(cl.parsingtextexpectingpingforscores != 1)
1742                         {
1743                                 ping_anyway_counter = 0;
1744                                 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1745                                 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1746                                 MSG_WriteString(&cls.netcon->message, "ping");
1747                         }
1748                 }
1749                 else
1750                 {
1751                         // newer server definitely has pings command, so use it for more efficiency, avoids ping reports spamming the console if they are misparsed, and saves a little bandwidth
1752                         MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1753                         MSG_WriteString(&cls.netcon->message, "pings");
1754                 }
1755         }
1756
1757         // scores
1758         Sbar_SortFrags ();
1759
1760         ymin = 8;
1761         ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1762
1763         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1764                 xmin = (vid_conwidth.integer - (26 + 15) * 8 * FONT_SBAR->width_of[0]) / 2; // 26 characters until name, then we assume 15 character names (they can be longer but usually aren't)
1765         else
1766                 xmin = (vid_conwidth.integer - (16 + 25) * 8 * FONT_SBAR->width_of[0]) / 2; // 16 characters until name, then we assume 25 character names (they can be longer but usually aren't)
1767         xmax = vid_conwidth.integer - xmin;
1768
1769         if(gamemode == GAME_NEXUIZ)
1770                 DrawQ_Pic (xmin - 8, ymin - 8, 0, xmax-xmin+1 + 2*8, ymax-ymin+1 + 2*8, 0, 0, 0, sbar_alpha_bg.value, 0);
1771
1772         DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1773
1774         // draw the text
1775         y = 40;
1776         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1777         {
1778                 DrawQ_String_Font(xmin, y, va("ping pl%% time frags team  name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1779         }
1780         else
1781         {
1782                 DrawQ_String_Font(xmin, y, va("ping pl%% frags  name"), 0, 8, 8, 1, 1, 1, 1 * sbar_alpha_fg.value, 0, NULL, false, FONT_SBAR );
1783         }
1784         y += 8;
1785
1786         if (Sbar_IsTeammatch ())
1787         {
1788                 // show team scores first
1789                 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1790                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1791                 y += 5;
1792         }
1793
1794         for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1795                 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1796 }
1797
1798 /*
1799 ==================
1800 Sbar_MiniDeathmatchOverlay
1801
1802 ==================
1803 */
1804 void Sbar_MiniDeathmatchOverlay (int x, int y)
1805 {
1806         int i, j, numlines, range_begin, range_end, myteam, teamsep;
1807
1808         // do not draw this if sbar_miniscoreboard_size is zero
1809         if(sbar_miniscoreboard_size.value == 0)
1810                 return;
1811         // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1812         if(sbar_miniscoreboard_size.value > 0)
1813                 y = vid_conheight.integer - sbar_miniscoreboard_size.value * 8;
1814
1815         // scores
1816         Sbar_SortFrags ();
1817
1818         // decide where to print
1819         if (gamemode == GAME_TRANSFUSION)
1820                 numlines = (vid_conwidth.integer - x + 127) / 128;
1821         else
1822                 numlines = (vid_conheight.integer - y + 7) / 8;
1823
1824         // give up if there isn't room
1825         if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1826                 return;
1827
1828         //find us
1829         for (i = 0; i < scoreboardlines; i++)
1830                 if (fragsort[i] == cl.playerentity - 1)
1831                         break;
1832
1833         range_begin = 0;
1834         range_end = scoreboardlines;
1835         teamsep = 0;
1836
1837         if (gamemode != GAME_TRANSFUSION)
1838                 if (Sbar_IsTeammatch ())
1839                 {
1840                         // reserve space for the team scores
1841                         numlines -= teamlines;
1842
1843                         // find first and last player of my team (only draw the team totals and my own team)
1844                         range_begin = range_end = i;
1845                         myteam = cl.scores[fragsort[i]].colors & 15;
1846                         while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1847                                 --range_begin;
1848                         while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1849                                 ++range_end;
1850
1851                         // looks better than two players
1852                         if(numlines == 2)
1853                         {
1854                                 teamsep = 8;
1855                                 numlines = 1;
1856                         }
1857                 }
1858
1859         // figure out start
1860         i -= numlines/2;
1861         i = min(i, range_end - numlines);
1862         i = max(i, range_begin);
1863
1864         if (gamemode == GAME_TRANSFUSION)
1865         {
1866                 for (;i < range_end && x < vid_conwidth.integer;i++)
1867                         x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1868         }
1869         else
1870         {
1871                 if(range_end - i < numlines) // won't draw to bottom?
1872                         y += 8 * (numlines - (range_end - i)); // bottom align
1873                 // show team scores first
1874                 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1875                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1876                 y += teamsep;
1877                 for (;i < range_end && y < vid_conheight.integer;i++)
1878                         y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1879         }
1880 }
1881
1882 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1883 {
1884         static int const sortorder[16] =
1885         {
1886                 1001,
1887                 1002,
1888                 1003,
1889                 1004,
1890                 1, // red
1891                 1005,
1892                 1006,
1893                 1007,
1894                 1008,
1895                 4, // pink
1896                 1009,
1897                 1010,
1898                 3, // yellow
1899                 2, // blue
1900                 1011,
1901                 1012
1902         };
1903         const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1904         const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1905         int tc1 = sortorder[t1->colors & 15];
1906         int tc2 = sortorder[t2->colors & 15];
1907         return tc1 - tc2;
1908 }
1909
1910 void Sbar_Score (int margin)
1911 {
1912         int i, me, score, otherleader, place, distribution, minutes, seconds;
1913         double timeleft;
1914         int sbar_x_save = sbar_x;
1915         int sbar_y_save = sbar_y;
1916
1917         sbar_y = vid_conheight.value - (32+12);
1918         sbar_x -= margin;
1919
1920         me = cl.playerentity - 1;
1921         if (me >= 0 && me < cl.maxclients)
1922         {
1923                 if(Sbar_IsTeammatch())
1924                 {
1925                         // Layout:
1926                         //
1927                         //   team1 team3 team4
1928                         //
1929                         //         TEAM2
1930
1931                         scoreboard_t *teamcolorsort[16];
1932
1933                         Sbar_SortFrags();
1934                         for(i = 0; i < teamlines; ++i)
1935                                 teamcolorsort[i] = &(teams[i]);
1936
1937                         // Now sort them by color
1938                         qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
1939
1940                         // : margin
1941                         // -12*4: four digits space
1942                         place = (teamlines - 1) * (-12 * 4);
1943
1944                         for(i = 0; i < teamlines; ++i)
1945                         {
1946                                 int cindex = teamcolorsort[i]->colors & 15;
1947                                 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
1948                                 float cm = max(max(c[0], c[1]), c[2]);
1949                                 float cr = c[0] / cm;
1950                                 float cg = c[1] / cm;
1951                                 float cb = c[2] / cm;
1952                                 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
1953                                 {
1954                                         Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
1955                                 }
1956                                 else // other team
1957                                 {
1958                                         Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
1959                                         place += 4 * 12;
1960                                 }
1961                         }
1962                 }
1963                 else
1964                 {
1965                         // Layout:
1966                         //
1967                         //   leading  place
1968                         //
1969                         //        FRAGS
1970                         //
1971                         // find leading score other than ourselves, to calculate distribution
1972                         // find our place in the scoreboard
1973                         score = cl.scores[me].frags;
1974                         for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
1975                         {
1976                                 if (cl.scores[i].name[0] && i != me)
1977                                 {
1978                                         if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
1979                                                 otherleader = i;
1980                                         if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
1981                                                 place++;
1982                                 }
1983                         }
1984                         distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
1985                         if (place == 1)
1986                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
1987                         else if (place == 2)
1988                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
1989                         else
1990                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
1991                         if (otherleader < 0)
1992                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
1993                         if (distribution >= 0)
1994                         {
1995                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
1996                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
1997                         }
1998                         else if (distribution >= -5)
1999                         {
2000                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2001                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 0, 1, 0);
2002                         }
2003                         else
2004                         {
2005                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2006                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 0, 0, 1, 0);
2007                         }
2008                 }
2009         }
2010
2011         if (cl.statsf[STAT_TIMELIMIT])
2012         {
2013                 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2014                 minutes = (int)floor(timeleft / 60);
2015                 seconds = (int)(floor(timeleft) - minutes * 60);
2016                 if (minutes >= 5)
2017                 {
2018                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2019                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2020                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2021                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2022                 }
2023                 else if (minutes >= 1)
2024                 {
2025                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 0, 1, 0);
2026                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2027                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2028                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2029                 }
2030                 else if ((int)(timeleft * 4) & 1)
2031                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2032                 else
2033                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2034         }
2035         else
2036         {
2037                 minutes = (int)floor(cl.time / 60);
2038                 seconds = (int)(floor(cl.time) - minutes * 60);
2039                 Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2040                 if(sb_colon && sb_colon->tex != r_texture_notexture)
2041                         DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2042                 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2043         }
2044
2045         sbar_x = sbar_x_save;
2046         sbar_y = sbar_y_save;
2047 }
2048
2049 /*
2050 ==================
2051 Sbar_IntermissionOverlay
2052
2053 ==================
2054 */
2055 void Sbar_IntermissionOverlay (void)
2056 {
2057         int             dig;
2058         int             num;
2059
2060         if (cl.gametype == GAME_DEATHMATCH)
2061         {
2062                 Sbar_DeathmatchOverlay ();
2063                 return;
2064         }
2065
2066         sbar_x = (vid_conwidth.integer - 320) >> 1;
2067         sbar_y = (vid_conheight.integer - 200) >> 1;
2068
2069         DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2070         DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2071
2072 // time
2073         dig = (int)cl.completed_time / 60;
2074         Sbar_DrawNum (160, 64, dig, 3, 0);
2075         num = (int)cl.completed_time - dig*60;
2076         Sbar_DrawPic (234,64,sb_colon);
2077         Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2078         Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2079
2080         Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2081         if (gamemode != GAME_NEXUIZ)
2082                 Sbar_DrawPic (232, 104, sb_slash);
2083         Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2084
2085         Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2086         if (gamemode != GAME_NEXUIZ)
2087                 Sbar_DrawPic (232, 144, sb_slash);
2088         Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2089
2090 }
2091
2092
2093 /*
2094 ==================
2095 Sbar_FinaleOverlay
2096
2097 ==================
2098 */
2099 void Sbar_FinaleOverlay (void)
2100 {
2101         DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2102 }
2103