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