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