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