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