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