]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sbar.c
implemented a new preallocated "boxbrush" type for use with
[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 static double showfps_nexttime = 0, showfps_lasttime = -1;
1094 static double showfps_framerate = 0;
1095 static int showfps_framecount = 0;
1096
1097 void Sbar_ShowFPS_Update(void)
1098 {
1099         double interval = 1;
1100         double newtime;
1101         newtime = realtime;
1102         if (newtime >= showfps_nexttime)
1103         {
1104                 showfps_framerate = showfps_framecount / (newtime - showfps_lasttime);
1105                 if (showfps_nexttime < newtime - interval * 1.5)
1106                         showfps_nexttime = newtime;
1107                 showfps_lasttime = newtime;
1108                 showfps_nexttime += interval;
1109                 showfps_framecount = 0;
1110         }
1111         showfps_framecount++;
1112 }
1113
1114 void Sbar_ShowFPS(void)
1115 {
1116         float fps_x, fps_y, fps_scalex, fps_scaley, fps_height;
1117         char soundstring[32];
1118         char fpsstring[32];
1119         char timestring[32];
1120         char datestring[32];
1121         char speedstring[32];
1122         char blurstring[32];
1123         char topspeedstring[48];
1124         qboolean red = false;
1125         soundstring[0] = 0;
1126         fpsstring[0] = 0;
1127         timestring[0] = 0;
1128         datestring[0] = 0;
1129         speedstring[0] = 0;
1130         blurstring[0] = 0;
1131         topspeedstring[0] = 0;
1132         if (showfps.integer)
1133         {
1134                 red = (showfps_framerate < 1.0f);
1135                 if(showfps.integer == 2)
1136                         dpsnprintf(fpsstring, sizeof(fpsstring), "%7.3f mspf", (1000.0 / showfps_framerate));
1137                 else if (red)
1138                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i spf", (int)(1.0 / showfps_framerate + 0.5));
1139                 else
1140                         dpsnprintf(fpsstring, sizeof(fpsstring), "%4i fps", (int)(showfps_framerate + 0.5));
1141         }
1142         if (showtime.integer)
1143                 strlcpy(timestring, Sys_TimeString(showtime_format.string), sizeof(timestring));
1144         if (showdate.integer)
1145                 strlcpy(datestring, Sys_TimeString(showdate_format.string), sizeof(datestring));
1146         if (showblur.integer)
1147                 dpsnprintf(blurstring, sizeof(blurstring), "%3i%% blur", (int)(cl.motionbluralpha * 100));
1148         if (showsound.integer)
1149                 dpsnprintf(soundstring, sizeof(soundstring), "%4i/4%i at %3ims", cls.soundstats.mixedsounds, cls.soundstats.totalsounds, cls.soundstats.latency_milliseconds);
1150         if (showspeed.integer || showtopspeed.integer)
1151         {
1152                 double speed, speedxy, f;
1153                 const char *unit;
1154                 speed = VectorLength(cl.movement_velocity);
1155                 speedxy = sqrt(cl.movement_velocity[0] * cl.movement_velocity[0] + cl.movement_velocity[1] * cl.movement_velocity[1]);
1156                 if (showspeed.integer)
1157                 {
1158                         get_showspeed_unit(showspeed.integer, &f, &unit);
1159                         dpsnprintf(speedstring, sizeof(speedstring), "%.0f (%.0f) %s", f*speed, f*speedxy, unit);
1160                 }
1161                 if (showtopspeed.integer)
1162                 {
1163                         qboolean topspeed_latched = false, topspeedxy_latched = false;
1164                         get_showspeed_unit(showtopspeed.integer, &f, &unit);
1165                         if (speed >= topspeed || current_time - top_time > 3)
1166                         {
1167                                 topspeed = speed;
1168                                 time(&top_time);
1169                         }
1170                         else
1171                                 topspeed_latched = true;
1172                         if (speedxy >= topspeedxy || current_time - topxy_time > 3)
1173                         {
1174                                 topspeedxy = speedxy;
1175                                 time(&topxy_time);
1176                         }
1177                         else
1178                                 topspeedxy_latched = true;
1179                         dpsnprintf(topspeedstring, sizeof(topspeedstring), "%s%.0f%s (%s%.0f%s) %s",
1180                                 topspeed_latched ? "^1" : "^xf88", f*topspeed, "^xf88",
1181                                 topspeedxy_latched ? "^1" : "^xf88", f*topspeedxy, "^xf88",
1182                                 unit);
1183                         time(&current_time);
1184                 }
1185         }
1186         if (fpsstring[0] || timestring[0] || datestring[0] || speedstring[0] || blurstring[0] || topspeedstring[0])
1187         {
1188                 fps_scalex = 12;
1189                 fps_scaley = 12;
1190                 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));
1191                 //fps_y = vid_conheight.integer - sb_lines; // yes this may draw over the sbar
1192                 //fps_y = bound(0, fps_y, vid_conheight.integer - fps_height);
1193                 fps_y = vid_conheight.integer - sbar_info_pos.integer - fps_height;
1194                 if (soundstring[0])
1195                 {
1196                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(soundstring, 0, true, FONT_INFOBAR) * fps_scalex;
1197                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1198                         DrawQ_String_Font(fps_x, fps_y, soundstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1199                         fps_y += fps_scaley;
1200                 }
1201                 if (fpsstring[0])
1202                 {
1203                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(fpsstring, 0, true, FONT_INFOBAR) * fps_scalex;
1204                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1205                         if (red)
1206                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 0, 0, 1, 0, NULL, true, FONT_INFOBAR);
1207                         else
1208                                 DrawQ_String_Font(fps_x, fps_y, fpsstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1209                         fps_y += fps_scaley;
1210                 }
1211                 if (timestring[0])
1212                 {
1213                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(timestring, 0, true, FONT_INFOBAR) * fps_scalex;
1214                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1215                         DrawQ_String_Font(fps_x, fps_y, timestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1216                         fps_y += fps_scaley;
1217                 }
1218                 if (datestring[0])
1219                 {
1220                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(datestring, 0, true, FONT_INFOBAR) * fps_scalex;
1221                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1222                         DrawQ_String_Font(fps_x, fps_y, datestring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1223                         fps_y += fps_scaley;
1224                 }
1225                 if (speedstring[0])
1226                 {
1227                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(speedstring, 0, true, FONT_INFOBAR) * fps_scalex;
1228                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1229                         DrawQ_String_Font(fps_x, fps_y, speedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1230                         fps_y += fps_scaley;
1231                 }
1232                 if (topspeedstring[0])
1233                 {
1234                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(topspeedstring, 0, false, FONT_INFOBAR) * fps_scalex;
1235                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1236                         DrawQ_String_Font(fps_x, fps_y, topspeedstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, false, FONT_INFOBAR);
1237                         fps_y += fps_scaley;
1238                 }
1239                 if (blurstring[0])
1240                 {
1241                         fps_x = vid_conwidth.integer - DrawQ_TextWidth_Font(blurstring, 0, true, FONT_INFOBAR) * fps_scalex;
1242                         DrawQ_Fill(fps_x, fps_y, vid_conwidth.integer - fps_x, fps_scaley, 0, 0, 0, 0.5, 0);
1243                         DrawQ_String_Font(fps_x, fps_y, blurstring, 0, fps_scalex, fps_scaley, 1, 1, 1, 1, 0, NULL, true, FONT_INFOBAR);
1244                         fps_y += fps_scaley;
1245                 }
1246         }
1247 }
1248
1249 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)
1250 {
1251         float r[5];
1252         c2 = bound(0, c2, 1);
1253         c1 = bound(0, c1, 1 - c2);
1254         r[0] = 0;
1255         r[1] = rangey + rangeheight * (c2 + c1);
1256         r[2] = rangey + rangeheight * (c2);
1257         r[3] = rangey;
1258         r[4] = height;
1259         if (r[1] > r[0])
1260                 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);
1261         if (r[2] > r[1])
1262                 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);
1263         if (r[3] > r[2])
1264                 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);
1265         if (r[4] > r[3])
1266                 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);
1267 }
1268
1269 /*
1270 ===============
1271 Sbar_Draw
1272 ===============
1273 */
1274 extern float v_dmg_time, v_dmg_roll, v_dmg_pitch;
1275 extern cvar_t v_kicktime;
1276 void Sbar_Score (int margin);
1277 void Sbar_Draw (void)
1278 {
1279         cachepic_t *pic;
1280
1281         if(cl.csqc_vidvars.drawenginesbar)      //[515]: csqc drawsbar
1282         {
1283                 if (sb_showscores)
1284                         Sbar_DrawScoreboard ();
1285                 else if (cl.intermission == 1)
1286                 {
1287                         if(gamemode == GAME_NEXUIZ) // display full scoreboard (that is, show scores + map name)
1288                         {
1289                                 Sbar_DrawScoreboard();
1290                                 return;
1291                         }
1292                         Sbar_IntermissionOverlay();
1293                 }
1294                 else if (cl.intermission == 2)
1295                         Sbar_FinaleOverlay();
1296                 else if (gamemode == GAME_DELUXEQUAKE)
1297                 {
1298                 }
1299                 else if (gamemode == GAME_SOM)
1300                 {
1301                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1302                                 Sbar_DrawScoreboard ();
1303                         else if (sb_lines)
1304                         {
1305                                 // this is the top left of the sbar area
1306                                 sbar_x = 0;
1307                                 sbar_y = vid_conheight.integer - 24*3;
1308
1309                                 // armor
1310                                 if (cl.stats[STAT_ARMOR])
1311                                 {
1312                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1313                                                 Sbar_DrawPic(0, 0, somsb_armor[2]);
1314                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1315                                                 Sbar_DrawPic(0, 0, somsb_armor[1]);
1316                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1317                                                 Sbar_DrawPic(0, 0, somsb_armor[0]);
1318                                         Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1319                                 }
1320
1321                                 // health
1322                                 Sbar_DrawPic(0, 24, somsb_health);
1323                                 Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1324
1325                                 // ammo icon
1326                                 if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1327                                         Sbar_DrawPic(0, 48, somsb_ammo[0]);
1328                                 else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1329                                         Sbar_DrawPic(0, 48, somsb_ammo[1]);
1330                                 else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1331                                         Sbar_DrawPic(0, 48, somsb_ammo[2]);
1332                                 else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1333                                         Sbar_DrawPic(0, 48, somsb_ammo[3]);
1334                                 Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false);
1335                                 if (cl.stats[STAT_SHELLS])
1336                                         Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true);
1337                         }
1338                 }
1339                 else if (gamemode == GAME_NEXUIZ)
1340                 {
1341                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1342                         {
1343                                 sbar_x = (vid_conwidth.integer - 640)/2;
1344                                 sbar_y = vid_conheight.integer - 47;
1345                                 Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1346                                 Sbar_DrawScoreboard ();
1347                         }
1348                         else if (sb_lines && sbar_hudselector.integer == 1)
1349                         {
1350                                 int i;
1351                                 float fade;
1352                                 int redflag, blueflag;
1353                                 float x;
1354
1355                                 sbar_x = (vid_conwidth.integer - 320)/2;
1356                                 sbar_y = vid_conheight.integer - 24 - 16;
1357
1358                                 // calculate intensity to draw weapons bar at
1359                                 fade = 3.2 - 2 * (cl.time - cl.weapontime);
1360                                 fade = bound(0.7, fade, 1);
1361                                 for (i = 0; i < 8;i++)
1362                                         if (cl.stats[STAT_ITEMS] & (1 << i))
1363                                                 Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1364                                 if((cl.stats[STAT_ITEMS] & (1<<12)))
1365                                         Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1366
1367                                 // flag icons
1368                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1369                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1370                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1371                                 if (redflag == 3 && blueflag == 3)
1372                                 {
1373                                         // The Impossible Combination[tm]
1374                                         // Can only happen in Key Hunt mode...
1375                                         Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[14]);
1376                                 }
1377                                 else
1378                                 {
1379                                         if (redflag)
1380                                                 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 64)), sb_items[redflag+10]);
1381                                         if (blueflag)
1382                                                 Sbar_DrawPic ((int) x, (int) ((vid_conheight.integer - sbar_y) - (sbar_flagstatus_pos.value + 128)), sb_items[blueflag+14]);
1383                                 }
1384
1385                                 // armor
1386                                 if (cl.stats[STAT_ARMOR] > 0)
1387                                 {
1388                                         Sbar_DrawStretchPic (72, 0, sb_armor[0], sbar_alpha_fg.value, 24, 24);
1389                                         if(cl.stats[STAT_ARMOR] > 200)
1390                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0,1,0,1,0);
1391                                         else if(cl.stats[STAT_ARMOR] > 100)
1392                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.2,1,0.2,1,0);
1393                                         else if(cl.stats[STAT_ARMOR] > 50)
1394                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.6,0.7,0.8,1,0);
1395                                         else if(cl.stats[STAT_ARMOR] > 25)
1396                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,1,1,0.2,1,0);
1397                                         else
1398                                                 Sbar_DrawXNum(0,0,cl.stats[STAT_ARMOR],3,24,0.7,0,0,1,0);
1399                                 }
1400
1401                                 // health
1402                                 if (cl.stats[STAT_HEALTH] != 0)
1403                                 {
1404                                         Sbar_DrawStretchPic (184, 0, sb_health, sbar_alpha_fg.value, 24, 24);
1405                                         if(cl.stats[STAT_HEALTH] > 200)
1406                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0,1,0,1,0);
1407                                         else if(cl.stats[STAT_HEALTH] > 100)
1408                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.2,1,0.2,1,0);
1409                                         else if(cl.stats[STAT_HEALTH] > 50)
1410                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1411                                         else if(cl.stats[STAT_HEALTH] > 25)
1412                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,1,1,0.2,1,0);
1413                                         else
1414                                                 Sbar_DrawXNum(112,0,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1415                                 }
1416
1417                                 // ammo
1418                                 if ((cl.stats[STAT_ITEMS] & (NEX_IT_SHELLS | NEX_IT_BULLETS | NEX_IT_ROCKETS | NEX_IT_CELLS)) || cl.stats[STAT_AMMO] != 0)
1419                                 {
1420                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1421                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[0], sbar_alpha_fg.value, 24, 24);
1422                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1423                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[1], sbar_alpha_fg.value, 24, 24);
1424                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1425                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[2], sbar_alpha_fg.value, 24, 24);
1426                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1427                                                 Sbar_DrawStretchPic (296, 0, sb_ammo[3], sbar_alpha_fg.value, 24, 24);
1428                                         if(cl.stats[STAT_AMMO] > 10)
1429                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.6,0.7,0.8,1,0);
1430                                         else
1431                                                 Sbar_DrawXNum(224, 0, cl.stats[STAT_AMMO], 3, 24, 0.7,0,0,1,0);
1432                                 }
1433
1434                                 if (sbar_x + 320 + 160 <= vid_conwidth.integer)
1435                                         Sbar_MiniDeathmatchOverlay (sbar_x + 320, sbar_y);
1436                                 if (sbar_x > 0)
1437                                         Sbar_Score(16);
1438                                         // The margin can be at most 8 to support 640x480 console size:
1439                                         //   320 + 2 * (144 + 16) = 640
1440                         }
1441                         else if (sb_lines)
1442                         {
1443                                 int i;
1444                                 float fade;
1445                                 int redflag, blueflag;
1446                                 float x;
1447
1448                                 sbar_x = (vid_conwidth.integer - 640)/2;
1449                                 sbar_y = vid_conheight.integer - 47;
1450
1451                                 // calculate intensity to draw weapons bar at
1452                                 fade = 3 - 2 * (cl.time - cl.weapontime);
1453                                 if (fade > 0)
1454                                 {
1455                                         fade = min(fade, 1);
1456                                         for (i = 0; i < 8;i++)
1457                                                 if (cl.stats[STAT_ITEMS] & (1 << i))
1458                                                         Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON]));
1459
1460                                         if((cl.stats[STAT_ITEMS] & (1<<12)))
1461                                                 Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1));
1462                                 }
1463
1464                                 //if (!cl.islocalgame)
1465                                 //      Sbar_DrawFrags ();
1466
1467                                 if (sb_lines > 24)
1468                                         Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value);
1469                                 else
1470                                         Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value);
1471
1472                                 // flag icons
1473                                 redflag = ((cl.stats[STAT_ITEMS]>>15) & 3);
1474                                 blueflag = ((cl.stats[STAT_ITEMS]>>17) & 3);
1475                                 x = sbar_flagstatus_right.integer ? vid_conwidth.integer - 10 - sbar_x - 64 : 10 - sbar_x;
1476                                 if (redflag == 3 && blueflag == 3)
1477                                 {
1478                                         // The Impossible Combination[tm]
1479                                         // Can only happen in Key Hunt mode...
1480                                         Sbar_DrawPic ((int) x, -179, sb_items[14]);
1481                                 }
1482                                 else
1483                                 {
1484                                         if (redflag)
1485                                                 Sbar_DrawPic ((int) x, -117, sb_items[redflag+10]);
1486                                         if (blueflag)
1487                                                 Sbar_DrawPic ((int) x, -177, sb_items[blueflag+14]);
1488                                 }
1489
1490                                 // armor
1491                                 Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0);
1492
1493                                 // health
1494                                 if(cl.stats[STAT_HEALTH] > 100)
1495                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0);
1496                                 else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5)
1497                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0);
1498                                 else
1499                                         Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0);
1500
1501                                 // AK dont draw ammo for the laser
1502                                 if(cl.stats[STAT_ACTIVEWEAPON] != 12)
1503                                 {
1504                                         if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS)
1505                                                 Sbar_DrawPic (519, 0, sb_ammo[0]);
1506                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS)
1507                                                 Sbar_DrawPic (519, 0, sb_ammo[1]);
1508                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS)
1509                                                 Sbar_DrawPic (519, 0, sb_ammo[2]);
1510                                         else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS)
1511                                                 Sbar_DrawPic (519, 0, sb_ammo[3]);
1512
1513                                         if(cl.stats[STAT_AMMO] <= 10)
1514                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0);
1515                                         else
1516                                                 Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0);
1517
1518                                 }
1519
1520                                 if (sb_lines > 24)
1521                                         DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay,0,0,1,1,1,1,DRAWFLAG_MODULATE);
1522
1523                                 if (sbar_x + 600 + 160 <= vid_conwidth.integer)
1524                                         Sbar_MiniDeathmatchOverlay (sbar_x + 600, sbar_y);
1525
1526                                 if (sbar_x > 0)
1527                                         Sbar_Score(-16);
1528                                         // Because:
1529                                         //   Mini scoreboard uses 12*4 per other team, that is, 144
1530                                         //   pixels when there are four teams...
1531                                         //   Nexuiz by default sets vid_conwidth to 800... makes
1532                                         //   sbar_x == 80...
1533                                         //   so we need to shift it by 64 pixels to the right to fit
1534                                         //   BUT: then it overlaps with the image that gets drawn
1535                                         //   for viewsize 100! Therefore, just account for 3 teams,
1536                                         //   that is, 96 pixels mini scoreboard size, needing 16 pixels
1537                                         //   to the right!
1538                         }
1539                 }
1540                 else if (gamemode == GAME_ZYMOTIC)
1541                 {
1542 #if 1
1543                         float scale = 64.0f / 256.0f;
1544                         float kickoffset[3];
1545                         VectorClear(kickoffset);
1546                         if (v_dmg_time > 0)
1547                         {
1548                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1549                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1550                         }
1551                         sbar_x = (int)((vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]);
1552                         sbar_y = (int)((vid_conheight.integer - 256 * scale)/2 + kickoffset[1]);
1553                         // left1 16, 48 : 126 -66
1554                         // left2 16, 128 : 196 -66
1555                         // right 176, 48 : 196 -136
1556                         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);
1557                         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);
1558                         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);
1559                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1560 #else
1561                         float scale = 128.0f / 256.0f;
1562                         float healthstart, healthheight, healthstarttc, healthendtc;
1563                         float shieldstart, shieldheight, shieldstarttc, shieldendtc;
1564                         float ammostart, ammoheight, ammostarttc, ammoendtc;
1565                         float clipstart, clipheight, clipstarttc, clipendtc;
1566                         float kickoffset[3], offset;
1567                         VectorClear(kickoffset);
1568                         if (v_dmg_time > 0)
1569                         {
1570                                 kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale;
1571                                 kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale;
1572                         }
1573                         sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0];
1574                         sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1];
1575                         offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?)
1576                         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);
1577                         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);
1578                         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);
1579                         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);
1580                         healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f);
1581                         shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f);
1582                         healthstart = 204 - healthheight;
1583                         shieldstart = healthstart - shieldheight;
1584                         healthstarttc = healthstart * (1.0f / 256.0f);
1585                         healthendtc = (healthstart + healthheight) * (1.0f / 256.0f);
1586                         shieldstarttc = shieldstart * (1.0f / 256.0f);
1587                         shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f);
1588                         ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f);
1589                         ammostart = 114 - ammoheight;
1590                         ammostarttc = ammostart * (1.0f / 256.0f);
1591                         ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f);
1592                         clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f);
1593                         clipstart = 190 - clipheight;
1594                         clipstarttc = clipstart * (1.0f / 256.0f);
1595                         clipendtc = (clipstart + clipheight) * (1.0f / 256.0f);
1596                         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);
1597                         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);
1598                         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);
1599                         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);
1600                         DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1601                         DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL);
1602 #endif
1603                 }
1604                 else // Quake and others
1605                 {
1606                         sbar_x = (vid_conwidth.integer - 320)/2;
1607                         sbar_y = vid_conheight.integer - SBAR_HEIGHT;
1608                         // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1609                         //if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION)
1610
1611                         if (sb_lines > 24)
1612                         {
1613                                 if (gamemode != GAME_GOODVSBAD2)
1614                                         Sbar_DrawInventory ();
1615                                 if (!cl.islocalgame && gamemode != GAME_TRANSFUSION)
1616                                         Sbar_DrawFrags ();
1617                         }
1618
1619                         if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer))
1620                         {
1621                                 if (gamemode != GAME_GOODVSBAD2)
1622                                         Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value);
1623                                 Sbar_DrawScoreboard ();
1624                         }
1625                         else if (sb_lines)
1626                         {
1627                                 Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value);
1628
1629                                 // keys (hipnotic only)
1630                                 //MED 01/04/97 moved keys here so they would not be overwritten
1631                                 if (gamemode == GAME_HIPNOTIC)
1632                                 {
1633                                         if (cl.stats[STAT_ITEMS] & IT_KEY1)
1634                                                 Sbar_DrawPic (209, 3, sb_items[0]);
1635                                         if (cl.stats[STAT_ITEMS] & IT_KEY2)
1636                                                 Sbar_DrawPic (209, 12, sb_items[1]);
1637                                 }
1638                                 // armor
1639                                 if (gamemode != GAME_GOODVSBAD2)
1640                                 {
1641                                         if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
1642                                         {
1643                                                 Sbar_DrawNum (24, 0, 666, 3, 1);
1644                                                 Sbar_DrawPic (0, 0, sb_disc);
1645                                         }
1646                                         else
1647                                         {
1648                                                 if (gamemode == GAME_ROGUE)
1649                                                 {
1650                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1651                                                         if (cl.stats[STAT_ITEMS] & RIT_ARMOR3)
1652                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1653                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2)
1654                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1655                                                         else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1)
1656                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1657                                                 }
1658                                                 else
1659                                                 {
1660                                                         Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25);
1661                                                         if (cl.stats[STAT_ITEMS] & IT_ARMOR3)
1662                                                                 Sbar_DrawPic (0, 0, sb_armor[2]);
1663                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR2)
1664                                                                 Sbar_DrawPic (0, 0, sb_armor[1]);
1665                                                         else if (cl.stats[STAT_ITEMS] & IT_ARMOR1)
1666                                                                 Sbar_DrawPic (0, 0, sb_armor[0]);
1667                                                 }
1668                                         }
1669                                 }
1670
1671                                 // face
1672                                 Sbar_DrawFace ();
1673
1674                                 // health
1675                                 Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25);
1676
1677                                 // ammo icon
1678                                 if (gamemode == GAME_ROGUE)
1679                                 {
1680                                         if (cl.stats[STAT_ITEMS] & RIT_SHELLS)
1681                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1682                                         else if (cl.stats[STAT_ITEMS] & RIT_NAILS)
1683                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1684                                         else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS)
1685                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1686                                         else if (cl.stats[STAT_ITEMS] & RIT_CELLS)
1687                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1688                                         else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS)
1689                                                 Sbar_DrawPic (224, 0, rsb_ammo[0]);
1690                                         else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO)
1691                                                 Sbar_DrawPic (224, 0, rsb_ammo[1]);
1692                                         else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)
1693                                                 Sbar_DrawPic (224, 0, rsb_ammo[2]);
1694                                 }
1695                                 else
1696                                 {
1697                                         if (cl.stats[STAT_ITEMS] & IT_SHELLS)
1698                                                 Sbar_DrawPic (224, 0, sb_ammo[0]);
1699                                         else if (cl.stats[STAT_ITEMS] & IT_NAILS)
1700                                                 Sbar_DrawPic (224, 0, sb_ammo[1]);
1701                                         else if (cl.stats[STAT_ITEMS] & IT_ROCKETS)
1702                                                 Sbar_DrawPic (224, 0, sb_ammo[2]);
1703                                         else if (cl.stats[STAT_ITEMS] & IT_CELLS)
1704                                                 Sbar_DrawPic (224, 0, sb_ammo[3]);
1705                                 }
1706
1707                                 Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10);
1708
1709                                 // LordHavoc: changed to draw the deathmatch overlays in any multiplayer mode
1710                                 if ((!cl.islocalgame || cl.gametype != GAME_COOP))
1711                                 {
1712                                         if (gamemode == GAME_TRANSFUSION)
1713                                                 Sbar_MiniDeathmatchOverlay (0, 0);
1714                                         else
1715                                                 Sbar_MiniDeathmatchOverlay (sbar_x + 324, vid_conheight.integer - 8*8);
1716                                         Sbar_Score(24);
1717                                 }
1718                         }
1719                 }
1720         }
1721
1722         if (cl.csqc_vidvars.drawcrosshair && crosshair.integer >= 1 && !cl.intermission && !r_letterbox.value)
1723         {
1724                 pic = Draw_CachePic (va("gfx/crosshair%i", crosshair.integer));
1725                 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);
1726         }
1727
1728         if (cl_prydoncursor.integer)
1729                 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);
1730 }
1731
1732 //=============================================================================
1733
1734 /*
1735 ==================
1736 Sbar_DeathmatchOverlay
1737
1738 ==================
1739 */
1740 float Sbar_PrintScoreboardItem(scoreboard_t *s, float x, float y)
1741 {
1742         int minutes;
1743         qboolean myself = false;
1744         unsigned char *c;
1745         minutes = (int)((cl.intermission ? cl.completed_time - s->qw_entertime : cl.time - s->qw_entertime) / 60.0);
1746
1747         if((s - cl.scores) == cl.playerentity - 1)
1748                 myself = true;
1749         if((s - teams) >= 0 && (s - teams) < MAX_SCOREBOARD)
1750                 if((s->colors & 15) == (cl.scores[cl.playerentity - 1].colors & 15))
1751                         myself = true;
1752
1753         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1754         {
1755                 if (s->qw_spectator)
1756                 {
1757                         if (s->qw_ping || s->qw_packetloss)
1758                                 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 );
1759                         else
1760                                 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 );
1761                 }
1762                 else
1763                 {
1764                         // draw colors behind score
1765                         //
1766                         //
1767                         //
1768                         //
1769                         //
1770                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1771                         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);
1772                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1773                         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);
1774                         // print the text
1775                         //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);
1776                         if (s->qw_ping || s->qw_packetloss)
1777                                 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 );
1778                         else
1779                                 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 );
1780                 }
1781         }
1782         else
1783         {
1784                 if (s->qw_spectator)
1785                 {
1786                         if (s->qw_ping || s->qw_packetloss)
1787                                 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 );
1788                         else
1789                                 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 );
1790                 }
1791                 else
1792                 {
1793                         // draw colors behind score
1794                         c = palette_rgb_pantsscoreboard[(s->colors & 0xf0) >> 4];
1795                         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);
1796                         c = palette_rgb_shirtscoreboard[s->colors & 0xf];
1797                         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);
1798                         // print the text
1799                         //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);
1800                         if (s->qw_ping || s->qw_packetloss)
1801                                 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 );
1802                         else
1803                                 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 );
1804                 }
1805         }
1806         return 8;
1807 }
1808
1809 void Sbar_DeathmatchOverlay (void)
1810 {
1811         int i, y, xmin, xmax, ymin, ymax;
1812
1813         // request new ping times every two second
1814         if (cl.last_ping_request < realtime - 2 && cls.netcon)
1815         {
1816                 cl.last_ping_request = realtime;
1817                 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1818                 {
1819                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1820                         MSG_WriteString(&cls.netcon->message, "pings");
1821                 }
1822                 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*/)
1823                 {
1824                         // 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
1825                         static int ping_anyway_counter = 0;
1826                         if(cl.parsingtextexpectingpingforscores == 1)
1827                         {
1828                                 Con_DPrintf("want to send ping, but still waiting for other reply\n");
1829                                 if(++ping_anyway_counter >= 5)
1830                                         cl.parsingtextexpectingpingforscores = 0;
1831                         }
1832                         if(cl.parsingtextexpectingpingforscores != 1)
1833                         {
1834                                 ping_anyway_counter = 0;
1835                                 cl.parsingtextexpectingpingforscores = 1; // hide the output of the next ping report
1836                                 MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1837                                 MSG_WriteString(&cls.netcon->message, "ping");
1838                         }
1839                 }
1840                 else
1841                 {
1842                         // 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
1843                         MSG_WriteByte(&cls.netcon->message, clc_stringcmd);
1844                         MSG_WriteString(&cls.netcon->message, "pings");
1845                 }
1846         }
1847
1848         // scores
1849         Sbar_SortFrags ();
1850
1851         ymin = 8;
1852         ymax = 40 + 8 + (Sbar_IsTeammatch() ? (teamlines * 8 + 5): 0) + scoreboardlines * 8 - 1;
1853
1854         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1855                 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)
1856         else
1857                 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)
1858         xmax = vid_conwidth.integer - xmin;
1859
1860         if(gamemode == GAME_NEXUIZ)
1861                 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);
1862
1863         DrawQ_Pic ((vid_conwidth.integer - sb_ranking->width)/2, 8, sb_ranking, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
1864
1865         // draw the text
1866         y = 40;
1867         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1868         {
1869                 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 );
1870         }
1871         else
1872         {
1873                 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 );
1874         }
1875         y += 8;
1876
1877         if (Sbar_IsTeammatch ())
1878         {
1879                 // show team scores first
1880                 for (i = 0;i < teamlines && y < vid_conheight.integer;i++)
1881                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[i]), xmin, y);
1882                 y += 5;
1883         }
1884
1885         for (i = 0;i < scoreboardlines && y < vid_conheight.integer;i++)
1886                 y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], xmin, y);
1887 }
1888
1889 /*
1890 ==================
1891 Sbar_MiniDeathmatchOverlay
1892
1893 ==================
1894 */
1895 void Sbar_MiniDeathmatchOverlay (int x, int y)
1896 {
1897         int i, j, numlines, range_begin, range_end, myteam, teamsep;
1898
1899         // do not draw this if sbar_miniscoreboard_size is zero
1900         if(sbar_miniscoreboard_size.value == 0)
1901                 return;
1902         // adjust the given y if sbar_miniscoreboard_size doesn't indicate default (< 0)
1903         if(sbar_miniscoreboard_size.value > 0)
1904                 y = (int) (vid_conheight.integer - sbar_miniscoreboard_size.value * 8);
1905
1906         // scores
1907         Sbar_SortFrags ();
1908
1909         // decide where to print
1910         if (gamemode == GAME_TRANSFUSION)
1911                 numlines = (vid_conwidth.integer - x + 127) / 128;
1912         else
1913                 numlines = (vid_conheight.integer - y + 7) / 8;
1914
1915         // give up if there isn't room
1916         if (x >= vid_conwidth.integer || y >= vid_conheight.integer || numlines < 1)
1917                 return;
1918
1919         //find us
1920         for (i = 0; i < scoreboardlines; i++)
1921                 if (fragsort[i] == cl.playerentity - 1)
1922                         break;
1923
1924         range_begin = 0;
1925         range_end = scoreboardlines;
1926         teamsep = 0;
1927
1928         if (gamemode != GAME_TRANSFUSION)
1929                 if (Sbar_IsTeammatch ())
1930                 {
1931                         // reserve space for the team scores
1932                         numlines -= teamlines;
1933
1934                         // find first and last player of my team (only draw the team totals and my own team)
1935                         range_begin = range_end = i;
1936                         myteam = cl.scores[fragsort[i]].colors & 15;
1937                         while(range_begin > 0 && (cl.scores[fragsort[range_begin-1]].colors & 15) == myteam)
1938                                 --range_begin;
1939                         while(range_end < scoreboardlines && (cl.scores[fragsort[range_end]].colors & 15) == myteam)
1940                                 ++range_end;
1941
1942                         // looks better than two players
1943                         if(numlines == 2)
1944                         {
1945                                 teamsep = 8;
1946                                 numlines = 1;
1947                         }
1948                 }
1949
1950         // figure out start
1951         i -= numlines/2;
1952         i = min(i, range_end - numlines);
1953         i = max(i, range_begin);
1954
1955         if (gamemode == GAME_TRANSFUSION)
1956         {
1957                 for (;i < range_end && x < vid_conwidth.integer;i++)
1958                         x += 128 + (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1959         }
1960         else
1961         {
1962                 if(range_end - i < numlines) // won't draw to bottom?
1963                         y += 8 * (numlines - (range_end - i)); // bottom align
1964                 // show team scores first
1965                 for (j = 0;j < teamlines && y < vid_conheight.integer;j++)
1966                         y += (int)Sbar_PrintScoreboardItem((teams + teamsort[j]), x, y);
1967                 y += teamsep;
1968                 for (;i < range_end && y < vid_conheight.integer;i++)
1969                         y += (int)Sbar_PrintScoreboardItem(cl.scores + fragsort[i], x, y);
1970         }
1971 }
1972
1973 int Sbar_TeamColorCompare(const void *t1_, const void *t2_)
1974 {
1975         static int const sortorder[16] =
1976         {
1977                 1001,
1978                 1002,
1979                 1003,
1980                 1004,
1981                 1, // red
1982                 1005,
1983                 1006,
1984                 1007,
1985                 1008,
1986                 4, // pink
1987                 1009,
1988                 1010,
1989                 3, // yellow
1990                 2, // blue
1991                 1011,
1992                 1012
1993         };
1994         const scoreboard_t *t1 = *(scoreboard_t **) t1_;
1995         const scoreboard_t *t2 = *(scoreboard_t **) t2_;
1996         int tc1 = sortorder[t1->colors & 15];
1997         int tc2 = sortorder[t2->colors & 15];
1998         return tc1 - tc2;
1999 }
2000
2001 void Sbar_Score (int margin)
2002 {
2003         int i, me, score, otherleader, place, distribution, minutes, seconds;
2004         double timeleft;
2005         int sbar_x_save = sbar_x;
2006         int sbar_y_save = sbar_y;
2007
2008
2009         sbar_y = (int) (vid_conheight.value - (32+12));
2010         sbar_x -= margin;
2011
2012         me = cl.playerentity - 1;
2013         if (sbar_scorerank.integer && me >= 0 && me < cl.maxclients)
2014         {
2015                 if(Sbar_IsTeammatch())
2016                 {
2017                         // Layout:
2018                         //
2019                         //   team1 team3 team4
2020                         //
2021                         //         TEAM2
2022
2023                         scoreboard_t *teamcolorsort[16];
2024
2025                         Sbar_SortFrags();
2026                         for(i = 0; i < teamlines; ++i)
2027                                 teamcolorsort[i] = &(teams[i]);
2028
2029                         // Now sort them by color
2030                         qsort(teamcolorsort, teamlines, sizeof(*teamcolorsort), Sbar_TeamColorCompare);
2031
2032                         // : margin
2033                         // -12*4: four digits space
2034                         place = (teamlines - 1) * (-12 * 4);
2035
2036                         for(i = 0; i < teamlines; ++i)
2037                         {
2038                                 int cindex = teamcolorsort[i]->colors & 15;
2039                                 unsigned char *c = palette_rgb_shirtscoreboard[cindex];
2040                                 float cm = max(max(c[0], c[1]), c[2]);
2041                                 float cr = c[0] / cm;
2042                                 float cg = c[1] / cm;
2043                                 float cb = c[2] / cm;
2044                                 if(cindex == (cl.scores[cl.playerentity - 1].colors & 15)) // my team
2045                                 {
2046                                         Sbar_DrawXNum(-32*4, 0, teamcolorsort[i]->frags, 4, 32, cr, cg, cb, 1, 0);
2047                                 }
2048                                 else // other team
2049                                 {
2050                                         Sbar_DrawXNum(place, -12, teamcolorsort[i]->frags, 4, 12, cr, cg, cb, 1, 0);
2051                                         place += 4 * 12;
2052                                 }
2053                         }
2054                 }
2055                 else
2056                 {
2057                         // Layout:
2058                         //
2059                         //   leading  place
2060                         //
2061                         //        FRAGS
2062                         //
2063                         // find leading score other than ourselves, to calculate distribution
2064                         // find our place in the scoreboard
2065                         score = cl.scores[me].frags;
2066                         for (i = 0, otherleader = -1, place = 1;i < cl.maxclients;i++)
2067                         {
2068                                 if (cl.scores[i].name[0] && i != me)
2069                                 {
2070                                         if (otherleader == -1 || cl.scores[i].frags > cl.scores[otherleader].frags)
2071                                                 otherleader = i;
2072                                         if (score < cl.scores[i].frags || (score == cl.scores[i].frags && i < me))
2073                                                 place++;
2074                                 }
2075                         }
2076                         distribution = otherleader >= 0 ? score - cl.scores[otherleader].frags : 0;
2077                         if (place == 1)
2078                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 1, 1, 0);
2079                         else if (place == 2)
2080                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 1, 0, 1, 0);
2081                         else
2082                                 Sbar_DrawXNum(-3*12, -12, place, 3, 12, 1, 0, 0, 1, 0);
2083                         if (otherleader < 0)
2084                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2085                         if (distribution >= 0)
2086                         {
2087                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 1, 1, 0);
2088                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 1, 1, 0);
2089                         }
2090                         else if (distribution >= -5)
2091                         {
2092                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 1, 0, 1, 0);
2093                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 1, 0, 1, 0);
2094                         }
2095                         else
2096                         {
2097                                 Sbar_DrawXNum(-7*12, -12, distribution, 4, 12, 1, 0, 0, 1, 0);
2098                                 Sbar_DrawXNum(-32*4,   0, score, 4, 32, 1, 0, 0, 1, 0);
2099                         }
2100                 }
2101         }
2102
2103         if (sbar_gametime.integer && cl.statsf[STAT_TIMELIMIT])
2104         {
2105                 timeleft = max(0, cl.statsf[STAT_TIMELIMIT] * 60 - cl.time);
2106                 minutes = (int)floor(timeleft / 60);
2107                 seconds = (int)(floor(timeleft) - minutes * 60);
2108                 if (minutes >= 5)
2109                 {
2110                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2111                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2112                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2113                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2114                 }
2115                 else if (minutes >= 1)
2116                 {
2117                         Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 0, 1, 0);
2118                         if(sb_colon && sb_colon->tex != r_texture_notexture)
2119                                 DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 0, sbar_alpha_fg.value, 0);
2120                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 0, 1, 0);
2121                 }
2122                 else if ((int)(timeleft * 4) & 1)
2123                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2124                 else
2125                         Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 0, 0, 1, 0);
2126         }
2127         else if (sbar_gametime.integer)
2128         {
2129                 minutes = (int)floor(cl.time / 60);
2130                 seconds = (int)(floor(cl.time) - minutes * 60);
2131                 Sbar_DrawXNum(-12*6, 32, minutes,  3, 12, 1, 1, 1, 1, 0);
2132                 if(sb_colon && sb_colon->tex != r_texture_notexture)
2133                         DrawQ_Pic(sbar_x + -12*3, sbar_y + 32, sb_colon, 12, 12, 1, 1, 1, sbar_alpha_fg.value, 0);
2134                 Sbar_DrawXNum(-12*2, 32, seconds, -2, 12, 1, 1, 1, 1, 0);
2135         }
2136
2137         sbar_x = sbar_x_save;
2138         sbar_y = sbar_y_save;
2139 }
2140
2141 /*
2142 ==================
2143 Sbar_IntermissionOverlay
2144
2145 ==================
2146 */
2147 void Sbar_IntermissionOverlay (void)
2148 {
2149         int             dig;
2150         int             num;
2151
2152         if (cl.gametype == GAME_DEATHMATCH)
2153         {
2154                 Sbar_DeathmatchOverlay ();
2155                 return;
2156         }
2157
2158         sbar_x = (vid_conwidth.integer - 320) >> 1;
2159         sbar_y = (vid_conheight.integer - 200) >> 1;
2160
2161         DrawQ_Pic (sbar_x + 64, sbar_y + 24, sb_complete, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2162         DrawQ_Pic (sbar_x + 0, sbar_y + 56, sb_inter, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2163
2164 // time
2165         dig = (int)cl.completed_time / 60;
2166         Sbar_DrawNum (160, 64, dig, 3, 0);
2167         num = (int)cl.completed_time - dig*60;
2168         Sbar_DrawPic (234,64,sb_colon);
2169         Sbar_DrawPic (246,64,sb_nums[0][num/10]);
2170         Sbar_DrawPic (266,64,sb_nums[0][num%10]);
2171
2172 // LA: Display as "a" instead of "a/b" if b is 0
2173         if(cl.stats[STAT_TOTALSECRETS])
2174         {
2175                 Sbar_DrawNum (160, 104, cl.stats[STAT_SECRETS], 3, 0);
2176                 if (gamemode != GAME_NEXUIZ)
2177                         Sbar_DrawPic (232, 104, sb_slash);
2178                 Sbar_DrawNum (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0);
2179         }
2180         else
2181         {
2182                 Sbar_DrawNum (240, 104, cl.stats[STAT_SECRETS], 3, 0);
2183         }
2184
2185         if(cl.stats[STAT_TOTALMONSTERS])
2186         {
2187                 Sbar_DrawNum (160, 144, cl.stats[STAT_MONSTERS], 3, 0);
2188                 if (gamemode != GAME_NEXUIZ)
2189                         Sbar_DrawPic (232, 144, sb_slash);
2190                 Sbar_DrawNum (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0);
2191         }
2192         else
2193         {
2194                 Sbar_DrawNum (240, 144, cl.stats[STAT_MONSTERS], 3, 0);
2195         }
2196 }
2197
2198
2199 /*
2200 ==================
2201 Sbar_FinaleOverlay
2202
2203 ==================
2204 */
2205 void Sbar_FinaleOverlay (void)
2206 {
2207         DrawQ_Pic((vid_conwidth.integer - sb_finale->width)/2, 16, sb_finale, 0, 0, 1, 1, 1, 1 * sbar_alpha_fg.value, 0);
2208 }
2209