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