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