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