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