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