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